<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Mostly harmless - Latest under wtf</title>
        <link>http://cyprio.net</link>
        <description><![CDATA[rants and news from the fringe]]></description>
        <atom:link href="http://cyprio.net/wtf/index.xml" rel="self"
                   type="application/rss+xml" />
        <lastBuildDate>Wed, 03 Sep 2025 00:00:00 UT</lastBuildDate>
        <item>
    <title>Parsing locations in Aguaxaca</title>
    <link>http://cyprio.net/wtf/2025-09-03-parsing-locations-in-aguaxaca.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Parsing locations in Aguaxaca</h1>
        <span class="date">September  3, 2025</span>
        
    </header>
    <p>The little <a href="https://agua.cypr.io">Aguaxaca</a> project has been humming
along for a month now. Ingesting data as it comes, and I decided to take
30 minutes to take a look at the mess I’m now in. *starts panicking*</p>
<p>Lots of things here are mostly for my own sake. Sorry if that’s noise in
your RSS reader.</p>
<h1 id="where-were-we">Where were we?</h1>
<p>Currently, I get a list of water-delivery spots (locations) from
“<span class="citation" data-cites="SOAPA_Oax">@SOAPA_Oax</span>”’s images on X/Twitter, and store lots of stringy data as a
result. I’m gathering data, so the schema is 💩 on purpose. I should
probably <del>trash</del> alter it once I’m more confident about the shape of
the “source material”, and how to parse this stuff properly.</p>
<p>For each new delivery report, I create a new <code>deliveries</code> record with
the location name as a string. Lots of duplication. This will obviously
not scale, but it’s fine for the initial exploration phase and volume of
data. The goal is to build a clean directory of locations and child
locations, and store deliveries with a foreign key from locations for
example…</p>
<h1 id="where-we-got">Where we got</h1>
<p>This is a small overview of locations names, after ±1 month of
collecting raw data. This should be a full panorama of all the locations
(or 99% of them, hopefully?) that get water deliveries in the city of
Oaxaca. Yay. 🎉</p>
<p>You can follow along on <a href="https://agua.cypr.io">agua.cypr.io</a>,
just to see how bad things are, and feel some Schadenfreude at my
expense.</p>
<p>We can already see that locations are further divided in
“sub-locations”. For example the <em>Infonavit</em> location includes <em>1a
etapa</em>, <em>2a etapa</em>, (…), each with distinct delivery schedules, most
of the time. Oh boy. 🥲</p>
<p>The same pattern is repeated for the <em>Guadalupe Victoria</em> location,
and many others. For all these, we could have a simple parent-child
relation. For example, the location <em>Infonavit</em> would have several
sub-locations like <em>1a etapa</em>, <em>2a etapa</em>, etc. Sub-locations are
optional, and sometimes a “parent” location will also be advertised in a
delivery schedule, without more details. I mean, it could be “Yay!
everyone gets water today!”, or “meh… typing these names is annoying”.</p>
<p>All locations have a distinct <em>type</em>, like <em>colonia</em>, <em>ejido</em>,
<em>fraccionamiento</em>, <em>unidad</em>, etc. This is usually coherent, good. We
can keep that as is, or spawn a separate table. I doubt we’ll see more
than 10 different location types though, so it’s more about DB size and
performance than anything (I mean storing a foreign-key integer instead
of a string).</p>
<p>So, what do we know?</p>
<ol type="1">
<li>As I mentioned, lots of location names have sub-locations. These are
usually appended between parens in the delivery announcement.
consistency here.</li>
<li>The guiding rule could be that the “top” location is whatever is
<em>not</em> inside parens, and then extract details about sub-locations to be
as thorough as possible.</li>
<li>I should probably capitalize/downcase all names to get a semblance of</li>
<li>Different sub-locations can get deliveries on different days, so we
do need the granularity of location and sub-location.</li>
</ol>
<p>The parent-child relation would still allow some sort of tree-like
structure, and granularity if we want to make something out of the data,
for example send notifications when a specific sub-location gets water,
or is likely to get water in the coming days.</p>
<h1 id="show-us-the-data">Show us the data</h1>
<p>Here’s some select data for a few locations, if you wanted a more
concrete overview.</p>
<h2 id="yalalag-parte-baja">📍 <strong>Yalalag (parte baja)</strong></h2>
<p>Location would be “Yalalag”, with a sub-location like “Parte Baja”.</p>
<h2 id="reforma-parte-baja-and-reforma-parte-alta">📍 <strong>Reforma (parte baja)</strong>, and <strong>Reforma (parte alta)</strong></h2>
<p>Location would be “Reforma”, with 2 sub-locations like “Parte Alta”, and “Parte Baja”.</p>
<p>Now, some less obvious ones, that would require a better parser.</p>
<h2 id="circuito-panorámica-del-fortín-sectores-1-2-y-3">📍 <strong>Circuito Panorámica del Fortín (sectores 1, 2 y 3)</strong></h2>
<p>Location would be “Circuito Panorámica del Fortín”, with 3 sub-locations here:</p>
<ul>
<li><strong>Sector 1</strong>,</li>
<li><strong>Sector 2</strong>, and</li>
<li><strong>Sector 3</strong>.</li>
</ul>
<h2 id="circuito-madero-de-la-iglesia-del-ex-marquezado-a-la-av.-tecnológico">📍 <strong>Circuito Madero (de la Iglesia del Ex Marquezado a la Av. Tecnológico)</strong></h2>
<p>Location: <strong>Circuito Madero</strong></p>
<p>Sub-location: <strong>de la Iglesia del Ex Marquezado a la Av. Tecnológico</strong></p>
<p>These are street or landmarks names, no use in trying to parse this further.</p>
<h2 id="for-the-guadalupe-victoria-location-we-get-a-lot-of-variants">📍For the <strong>Guadalupe Victoria</strong> location, we get a lot of variants:</h2>
<ul>
<li>Guadalupe Victoria (sectores 3 y 4 parte baja)</li>
<li>Guadalupe Victoria (sector 5 parte baja)</li>
<li>Guadalupe Victoria (sectores 3, 4, 5 y 6 parte alta)</li>
<li>Guadalupe Victoria (sector 6 parte baja)</li>
<li>Guadalupe Victoria (sección Oeste sector 2)</li>
<li>Guadalupe Victoria (2ª sección Oeste sector 1)</li>
<li>Guadalupe Victoria (sector 1, 2ª sección Oeste)</li>
</ul>
<p>It looks like we have a bunch of “sectors”. Some sectors have additional distinctions like “parte alta” and “parte baja”. From this list, I gather:</p>
<p>Location: <strong>Guadalupe Victoria</strong>
Sub-locations:</p>
<ul>
<li>Sector 1</li>
<li>Sector 2</li>
<li>(…)</li>
<li>Sector 6</li>
<li>sección Oeste</li>
<li>2ª sección Oeste</li>
</ul>
<p>💭 I’d auto-create sub-locations by parsing the text (splitting
on commas, “y” conjunctions, etc.), but there’s probably not much
use trying to go beyond one level: meaning “sector 6”, “sector 6
parte baja” and “sector 6 parte alta” are 3 sub-locations of “Guadalupe
Victoria”.</p>
<h2 id="dolores">📍 <strong>Dolores</strong></h2>
<ul>
<li>Dolores (zona Oriente parte baja)</li>
<li>Dolores (sector Poniente parte baja)</li>
<li>Dolores (sector Poniente parte alta)</li>
<li>Dolores (parte baja)</li>
</ul>
<p>Location: <strong>Dolores</strong></p>
<p>Sub-locations (so far…):</p>
<ul>
<li>Zona Oriente Parte Baja</li>
<li>Sector Poniente Parte Baja</li>
<li>Sector Poniente Parte Alta</li>
<li>Parte Baja</li>
</ul>
<p>I guess some days you write “sector”, and some other days “zona”. It’s probably the same thing. Just great.</p>
<h2 id="américa-sur">📍América Sur</h2>
<p>We get the usual “parte alta” and “parte baja” sub-locations, but with a small twist:</p>
<ul>
<li>América Sur (parte baja)</li>
<li>América Sur (parte baja y alta)</li>
</ul>
<p>Location: <strong>América Sur</strong></p>
<p>Sub-locations: <strong>Parte Baja</strong>, and <strong>Parte Alta</strong></p>
<p>(Yes, there’s also América <em>Norte</em>, it’s a different location.)</p>
<h2 id="san-juan-chapultepec">📍San Juan Chapultepec</h2>
<p>San Juan Chapultepec was both advertised as <em>agencia</em> when it comes without sub-locations, and a <em>colonia</em> when it comes with several sub-locations. Let’s focus on the latter here.</p>
<ul>
<li>San Juan Chapultepec y sus Barrios</li>
<li>San Juan Chapultepec (parte baja)</li>
<li>San Juan Chapultepec (parte media)</li>
<li>San Juan Chapultepec (parte alta y sus Barrios)</li>
</ul>
<p>That last one is 🏆. I guess we could have this hierarchy:</p>
<p>Location: <strong>San Juan Chapultepec</strong></p>
<p>Sub-locations: “Parte Baja”, “Parte Media”, “Parte Alta”, and well… “Barrios” 😑.</p>
<p>This breaks the assumption that sub-locations are always between
parentheses. Damn it.</p>
<h2 id="centro">📍Centro</h2>
<p>The central district locations are also all over the
place. Here’s a list of locations we get <a href="https://agua.cypr.io/?name=centro">searching for
“centro”</a>:</p>
<ul>
<li>Centro Sur y Medio (de la calle Abasolo a Periférico Sur)</li>
<li>Centro Sur y Medio (de la calle Abasolo a Periférico Sur)</li>
<li>Centro Sur Poniente</li>
<li>Centro Sur Poniente</li>
<li>Centro Norte (de Constitución a Calzada Niños Héroes de Chapultepec)</li>
<li>Centro Norte (de Constitución a Calzada Niños Héroes de Chapultepec)</li>
<li>Centro Sur Poniente (sector Huzares)</li>
<li>Centro Medio (de calle Colón a Abasolo)</li>
</ul>
<p>Locations:</p>
<ul>
<li><strong>Centro Sur</strong>,</li>
<li><strong>Centro Norte</strong>,</li>
<li><strong>Centro Medio</strong>, and probably</li>
<li><strong>Centro Sur Poniente</strong>.</li>
</ul>
<p>Sub-Locations (again street names and landmarks):</p>
<ul>
<li>de la calle Abasolo a Periférico Sur</li>
<li>de Constitución a Calzada Niños Héroes de Chapultepec</li>
<li>sector Huzares</li>
<li>de calle Colón a Abasolo</li>
</ul>
<h2 id="volcanes">📍Volcanes</h2>
<p>Here’s a list of locations names we’ve seen:</p>
<ul>
<li>Volcanes (sector 3 zona norte)</li>
<li>Volcanes (sectores 4 y 5)</li>
<li>Volcanes (sector 1 parte baja)</li>
<li>Volcanes (sector 1 parte baja)</li>
<li>Volcanes (sectores 4 y 5)</li>
<li>Volcanes (zona Norte, sector 3)</li>
</ul>
<p>Location: <strong>Volcanes</strong></p>
<p>Sub-Locations:</p>
<ul>
<li>Sector 1 Parte Baja</li>
<li>Sector 3</li>
<li>Sector 3 Zona Norte</li>
<li>Sector 4</li>
<li>Sector 5</li>
<li>Zona Norte</li>
</ul>
<p>In this case as with others, I wonder whether these are actually 3
different locations: “sector 3”, “sector 3 zona norte”, and “zona
norte”… The delivery schedules mention both at different dates:</p>
<ul>
<li>Aug 27: Volcanes (sector 3 zona norte)</li>
<li>Aug 1st: Volcanes (zona Norte, sector 3)</li>
</ul>
<p>Given the delivery interval this could be a single location, where the
publisher used a different name format. 🤷‍♂️</p>
<h1 id="next-steps-of-my-now-youre-an-etl-programmer-phase">Next steps of my Now You’re An ETL Programmer phase</h1>
<ol type="1">
<li>Write a parser to extract the sub-locations, from location “names”.</li>
<li>Store locations and sub-locations properly in a <code>locations</code> table.</li>
<li>Cleanup and migrate the existing data.</li>
<li>Make a useful “product” (plot delivery dates per location(s), provide
notifications, etc.).</li>
</ol>
<p>If you’ve read through all of this, sorry there’s no big insight. Just
my brain trying to map real world stuff to bytes. Part of me wants to
see if I can offload task #1 to an LLM: with a sufficiently intricate
prompt, it could work, but I’m not sure I can trust the machine here,
because the text is sometimes quite ambiguous. Being surprised would be
nice, because the alternative is a hand-written parser with lots of unit
tests.</p>
</article>
]]></description>
    <pubDate>Wed, 03 Sep 2025 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2025-09-03-parsing-locations-in-aguaxaca.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Aguaxaca</title>
    <link>http://cyprio.net/wtf/2025-08-17-aguaxaca.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Aguaxaca</h1>
        <span class="date">August 17, 2025</span>
        
    </header>
    <p>Hi there! Long time no see. Not that I expect to publish frequently
these days. This is the pitch for a little open-source project that’s
kept me busy a few evenings.</p>
<p>Some months ago, we moved back to the city of Oaxaca. We’ve lived in the
countryside nearby for years, but it was soon very clear that “nearby”
was actually pretty far when thousands of people try to use the same
roads at the same time, to get to work or school, and public transport
is shit.</p>
<p>I can’t fix that though, so we found a house 10 minutes from school,
and stay in the city a good chunk of the year now. With that change,
we became again dependent on the city’s water distribution network.
Something with a pretty name like <em>Sistema Operador de los Servicios
de Agua Potable y Alcantarillado</em> (de Oaxaca). Usually, they’re online
<a href="http://oaxaca.gob.mx/soapa/">here</a>, but not all the time. 🥲</p>
<p>This public organization manages the water distribution network of
the whole urban area of Oaxaca. For various socio-ecological reasons,
water doesn’t run all the time in the city (or state) of Oaxaca. You’re
expected to store whatever amount you receive monthly (-ish). There’s
not enough water (or pressure) to distribute to everyone at the same
time, so not all districts receive water on the same day. (You should
<em>not</em> drink that water by the way.)</p>
<p>The distribution schedules are only advertised on X/Twitter, and
Facebook. This is annoying, because the information is never really
archived anywhere, and is published as JPEGs images (a workaround for
text lengths limit, I guess). That’s not too friendly for visually
impaired folks either. In Oaxaca, you do with what little resources you
have.</p>
<p>I like to know when my home will receive water, and I’d also like to
know when it <em>did</em> previously, to better manage my consumption. I don’t
want to use Twitter/X, or Facebook though. I use them as little as
possible, with a hazmat suit, and tears in my eyes.</p>
<p>So, that’s why I started this thing called
<a href="https://agua.cypr.io">Aguaxaca</a>. It is an <a href="https://git.cypr.io/oz/aguaxaca">open-source
project</a>, that gathers water
distribution schedules through a private Nitter instance. The images are
parsed using <del>a normal OCR</del> an LLM, and the schedules are stored and
published for all to see — in particular <em>me</em>, but I consider this a
minor public service, not something that will turn a profit.</p>
<p>The goals are simple:</p>
<ol type="1">
<li>archive this stuff,</li>
<li>get notifications when it’s my turn (essential),</li>
<li>get historical records and plot some stats (would be nice),</li>
<li>compute the likely date of next delivery (would be extra nice).</li>
</ol>
<p>I’m currently in “phase 1”, discovering the complete list of
distribution sectors. The public data I scrape isn’t exactly great, or
consistent, but with some genAI prompt magic, I should be able to turn
this mess into a proper DB. That’s the plan anyway!</p>
<p>Also yes, the name… sucks, but it’s easy to remember if you know a few
words of Spanish. If you got this far, thanks for reading.</p>
</article>
]]></description>
    <pubDate>Sun, 17 Aug 2025 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2025-08-17-aguaxaca.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Monthly SQL hell</title>
    <link>http://cyprio.net/wtf/2024-09-30-monthly-hell.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Monthly SQL hell</h1>
        <span class="date">September 30, 2024</span>
        
    </header>
    <p>Recently, I learned from <a href="https://dev.mysql.com/doc/refman/8.0/en/explain-output.html#explain-extra-information">MySQL 8.0 docs</a>, that:</p>
<blockquote>
<p>If you want to make your queries as fast as possible, look out
for Extra column values of Using filesort and Using temporary,
or, in JSON-formatted EXPLAIN output, for using_filesort and
using_temporary_table properties equal to true.</p>
</blockquote>
<p>So, I guess that I should probably stay away from these filesort
and temporary table things. That sounds fair 🤔</p>
<p>You already know where this is going, but let’s see what
<code>EXPLAIN</code> has to say about this suspiciously large query.</p>
<pre><code>&gt; EXPLAIN select &#39;&#39; -- Lots and lots of gibbe^Wperfectly valid SQL.
+----+--------------+-----------------------------------+---------------------------------+
| id | select_type  | table                             | Extra                           |
+----+--------------+-----------------------------------+---------------------------------+
+ Truncated...
| 13 | UNION RESULT | &lt;union1,2,3,4,5,6,7,8,9,10,11,12&gt; | Using temporary; Using filesort |
+----+--------------+-----------------------------------+---------------------------------+
25 rows in set, 1 warning (0.10 sec)</code></pre>
<p>Oh, that doesn’t look too great (also, sorry if you’re on mobile). I sure
hope no one runs that query in a loop, or things could go wrong, right? Right?</p>
<figure>
<img src="https://static.cyprio.net/pics/2024-09-mysql-cpu-usage.png" alt="Oh no" />
<figcaption aria-hidden="true">Oh no</figcaption>
</figure>
</article>
]]></description>
    <pubDate>Mon, 30 Sep 2024 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2024-09-30-monthly-hell.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Random Preonic sounds</title>
    <link>http://cyprio.net/wtf/2024-06-11-preonic-sounds.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Random Preonic sounds</h1>
        <span class="date">June 11, 2024</span>
        
    </header>
    <p>I kind of <a href="./2024-06-10-tap-dance-cap-word.html">warned you</a> about making sounds with the
Preonic keyboard, because 80s beeps will never go out of fashion, and
these clicky switches aren’t noisier enough anyway. Check <a href="./2024-06-10-tap-dance-cap-word.html">the previous
post</a> if you need to, this is a follow up.</p>
<p>Playing sounds is very simple with QMK, thanks to the cute <code>PLAY_SONG</code>
macro, to make the board play very geeky boops.</p>
<p>Since we started from the default keymap and config for the v3 preonic,
we already have audio enabled on the board. There’s nothing else to
configure: as far as I know we can already beep to our heart’s content.</p>
<p>In QMK, we have a C macro <code>SONG(list of notes)</code>. It gives us back an
array, that we can feed to <code>PLAY_SONG</code> to actually play sounds. Of
course <em>these are C macros</em>, so we can’t really inline things like this:
<code>PLAY_SONG(SONG(TADAAA))</code>. NOPE!</p>
<p>No big deal. We’ll use an intermediate variable to store the “song”,
and feed that to <code>PLAY_SONG</code>. For our purpose, let’s reuse a song from
<code>song_list.h</code> (<a href="https://github.com/qmk/qmk_firmware/blob/8b5cdfabf5d05958a607efa174e64377d36e4b64/quantum/audio/song_list.h">this file</a>). Writing your own stuff is fun,
but it’ll keep this post short.</p>
<p>I’ll use the <code>CAPS_LOCK_ON_SOUND</code> (and off) sounds, which seem
half-appropriate.</p>
<p>Let’s declare these two songs in <code>keymap.c</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#ifdef AUDIO_ENABLE</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">float</span> caps_word_on_song<span class="op">[][</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> SONG<span class="op">(</span>CAPS_LOCK_ON_SOUND<span class="op">);</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">float</span> caps_word_off_song<span class="op">[][</span><span class="dv">2</span><span class="op">]</span> <span class="op">=</span> SONG<span class="op">(</span>CAPS_LOCK_OFF_SOUND<span class="op">);</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#endif</span></span></code></pre></div>
<p>Now, we could update the previous post’s tap-dance callback to play
sounds, but the caps-word feature is more ergonomic than that. Just
use the <code>caps_word_set_user</code> <a href="https://docs.qmk.fm/features/caps_word#representing-caps-word-state">callback</a>. It gets a “bool” to
indicate the current state. Again, in <code>keymap.c</code>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#ifdef AUDIO_ENABLE</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> caps_word_set_user<span class="op">(</span><span class="dt">bool</span> active<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> <span class="op">(</span>active<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>		PLAY_SONG<span class="op">(</span>caps_word_on_song<span class="op">);</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>		PLAY_SONG<span class="op">(</span>caps_word_off_song<span class="op">);</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="pp">#endif</span></span></code></pre></div>
<p>The whole <code>#ifdef</code> wrapping isn’t essential here, but the function
only matters when audio is enabled in my case. YMMV.</p>
<p>That’s it. See, THAT WAS NOT THAT HARD. Sorry, just testing. :)</p>
</article>
]]></description>
    <pubDate>Tue, 11 Jun 2024 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2024-06-11-preonic-sounds.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Tap dance and Caps word</title>
    <link>http://cyprio.net/wtf/2024-06-10-tap-dance-cap-word.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Tap dance and Caps word</h1>
        <span class="date">June 10, 2024</span>
        
    </header>
    <p>If you’re into mechanical keyboards, you probably know about Tap
dances and Caps words already. If not, well these are two features of
<a href="https://qmk.fm">QMK</a>:</p>
<ul>
<li>a <em>tap dance</em> allows you to remap rapid successive taps on a key to a
different function (<a href="https://docs.qmk.fm/features/tap_dance">ref</a>), and</li>
<li><em>caps word</em> is a function that turns on CAPS but only for the next
word you’ll type (<a href="https://docs.qmk.fm/features/caps_word">ref</a>).</li>
</ul>
<p>As the Tap dance docs clearly state, a Tap dance can trigger basic key
codes only: meaning letters, modifiers, etc., but <em>not</em> Caps words. I
briefly experimented on my Preonic, trying to get the backtick key to
turn on Caps word, using the basic Tap dance macros, but it failed to
trigger anything. Frustrating.</p>
<p>Either I’m holding it wrong, or the docs are correct. Both things are
equally possible. :)</p>
<p>But fear not dear reader! Of course you <em>can</em> trigger Caps word from
a Tap dance. You’ll just need to write a bit of C, to customize the
board’s keymap.</p>
<p>Let’s look into this here.</p>
<p>First, change <code>rules.mk</code> to enable both features, adding:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>CAPS_WORD_ENABLE <span class="op">=</span> yes</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>TAP_DANCE_ENABLE <span class="op">=</span> yes</span></code></pre></div>
<p>I also like to have an explicit tapping-term, which defines how long
the board waits for your taps to decide what to do next… I’m not sure
whether that’s a good definition, but that’s how my brain explains it.</p>
<p>Change <code>config.h</code> to define this as needed, for example:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define TAPPING_TERM </span><span class="dv">200</span></span></code></pre></div>
<p>This means you have 200ms to double-tap your keys. This tapping term is
also shared with Tap-or-hold custom keycodes, so maybe you will need to
figure out per-key tap terms later. YMMV.</p>
<p>The meat of this little hack lives inside <code>keymap.c</code>.</p>
<p>Let’s add a <code>cur_dance</code> helper that will tell use where we’re at in our
Tap dance:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Some Tap Dance states</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>	SINGLE_TAP <span class="op">=</span> <span class="dv">1</span><span class="op">,</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>	SINGLE_HOLD <span class="op">=</span> <span class="dv">2</span><span class="op">,</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>	DOUBLE_TAP <span class="op">=</span> <span class="dv">3</span><span class="op">,</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>	DOUBLE_HOLD <span class="op">=</span> <span class="dv">4</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> cur_dance<span class="op">(</span>tap_dance_state_t <span class="op">*</span>state<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> <span class="op">((</span>state<span class="op">-&gt;</span>count <span class="op">==</span> <span class="dv">1</span><span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">(!</span>state<span class="op">-&gt;</span>pressed<span class="op">))</span> <span class="cf">return</span> SINGLE_TAP<span class="op">;</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>	<span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>state<span class="op">-&gt;</span>count <span class="op">==</span> <span class="dv">1</span><span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">(</span>state<span class="op">-&gt;</span>pressed<span class="op">))</span> <span class="cf">return</span> SINGLE_HOLD<span class="op">;</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>	<span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>state<span class="op">-&gt;</span>count <span class="op">==</span> <span class="dv">2</span><span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">(!</span>state<span class="op">-&gt;</span>pressed<span class="op">))</span> <span class="cf">return</span> DOUBLE_TAP<span class="op">;</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>	<span class="cf">else</span> <span class="cf">if</span> <span class="op">((</span>state<span class="op">-&gt;</span>count <span class="op">==</span> <span class="dv">2</span><span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">(</span>state<span class="op">-&gt;</span>pressed<span class="op">))</span> <span class="cf">return</span> DOUBLE_HOLD<span class="op">;</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>	<span class="co">// magic number. At some point this method could expand to work for more presses</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>	<span class="cf">else</span> <span class="cf">return</span> <span class="dv">5</span><span class="op">;</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The logic is pretty simple. For our use case, we also need 2 functions
to hook in the <em>finished</em> and <em>reset</em> callbacks of a Tap dance. Let’s
add these next:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Tap-dance is over.</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> dance_cw_finished<span class="op">(</span>tap_dance_state_t <span class="op">*</span>state<span class="op">,</span> <span class="dt">void</span> <span class="op">*</span>user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>	<span class="dt">int</span> dance <span class="op">=</span> cur_dance<span class="op">(</span>state<span class="op">);</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> <span class="op">(</span>dance <span class="op">==</span> SINGLE_TAP<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>		register_code<span class="op">(</span>KC_GRV<span class="op">);</span> <span class="co">// single tap -&gt; backtick key</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>		reset_tap_dance<span class="op">(</span>state<span class="op">);</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span><span class="op">;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> <span class="op">(</span>dance <span class="op">==</span> DOUBLE_TAP<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>		caps_word_on<span class="op">();</span> <span class="co">// double tap -&gt; caps words</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span><span class="op">;</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a><span class="co">// Clear the tap-dance floor.</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> dance_cw_reset<span class="op">(</span>tap_dance_state_t <span class="op">*</span>state<span class="op">,</span> <span class="dt">void</span> <span class="op">*</span>user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> <span class="op">(</span>state<span class="op">-&gt;</span>count <span class="op">==</span> <span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>		unregister_code<span class="op">(</span>KC_GRV<span class="op">);</span> <span class="co">// Stop sending backtick.</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>What’s left is some boiler-plate to register our handlers as a Tap
dance, named <code>TD_GRV_CW</code>, which is a pretty bad name if you try to
pronounce it. It did made sense at the time:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Backtick or Caps-word tap-dance.</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>	TD_GRV_CW<span class="op">,</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>tap_dance_action_t tap_dance_actions<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>	<span class="co">// Tap once for ` (backtick), twice for Caps-word.</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>	<span class="op">[</span>TD_GRV_CW<span class="op">]</span> <span class="op">=</span> ACTION_TAP_DANCE_FN_ADVANCED<span class="op">(</span>NULL<span class="op">,</span> dance_cw_finished<span class="op">,</span> dance_cw_reset<span class="op">),</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>On your board configuration, you can replace <code>KC_GRV</code> with
<code>TD(TD_GRV_CW)</code>, then compile and flash as usual, now double tapping the
backtick key will activate <em>Caps-word</em>, rejoice.</p>
<p>Done?</p>
<p>Dancing is nice(r) with music. My rev3 Preonic has a (<del>possibly</del>
annoying) speaker. Wouldn’t it be nice to <a href="./2024-06-11-preonic-sounds.html">play a few notes as you
activate a tap dance</a>? I’m sure everybody around would love it.</p>
<p>Also, what if I want to have a Tap dance on a different key like <em>Left
Control</em> or something?</p>
<p>So many possibilities… Maybe I’ll write about that here next, in a
couple of years if this site’s post frequency is stable.</p>
<p>Thanks for reading.</p>
</article>
]]></description>
    <pubDate>Mon, 10 Jun 2024 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2024-06-10-tap-dance-cap-word.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Introducing mdstat</title>
    <link>http://cyprio.net/wtf/2022-09-25-mdstat.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Introducing mdstat</h1>
        <span class="date">September 25, 2022</span>
        
    </header>
    <p>MailDir Stat (mdstat) is a small program I wrote some time ago to play
with <a href="https://ziglang.org/">zig</a>: it lists MailDirs, and optionally only
those that contain unread emails. Nothing incredibly new, just a small
itch to scratch.</p>
<p>It came from a small frustration with (neo)mutt where I sometimes like
to have a tree of mailboxes in the sidebar ; but I only want to see the
<em>unread</em> ones. I synchronize various IMAP accounts locally with
<a href="https://isync.sourceforge.io/">mbsync</a>, so finding maildirs is easily
done with a bit of shell scripting.</p>
<p>For instance, this is how I listed <em>all</em> maildirs until recently:</p>
<pre><code>fd . /home/oz/mail/cyprio --type d | \
  rg &#39;cur/$&#39; | \
  sed &#39;s,^/home/oz/mail/cyprio/\(.*\)/cur,=\&quot;\1\&quot;,&#39; | \
  tr &#39;\n&#39; &#39; &#39;</code></pre>
<p>This works well enough to tell neomutt to check all the maildirs of my
account, and you can get more creative to ignore some directories, etc.
With mdstat, this is just:</p>
<pre><code>mdstat ~/mail/cyprio</code></pre>
<p><em>Personally</em>, I find that it’s an improvement ; but also maybe, a
senseless micro-optimization, you know <a href="https://justforfunnoreally.dev/">just for
fun</a>.</p>
<h1 id="sidebar-and-unreads">Sidebar and unreads</h1>
<p>My mail account(s) often have nested folders, and will until the day
I’m forced to properly use notmuch or something, and use labels
like the rest of the world. I don’t know.</p>
<p>Still. As I use the sidebar plugin, I’ve realized that the
“sidebar parser” isn’t (wasn’t?) clever enough
to display a tree unless <em>explicitly</em> asked to. Let me try to explain.</p>
<p>Let’s say that I have new emails under <code>="news/progamming"</code>. To
display a nice 2-levels tree with both the <em>news</em>, and <em>programming</em>
labels correctly nested, the sidebar config expects to receive both
entries like so: <code>="news/" ="news/programming"</code> (order doesn’t
actually matter).</p>
<p>If you just list <code>="news/programming"</code>, you’ll get:</p>
<pre><code>  programming/</code></pre>
<p>If you list both entries, you get:</p>
<pre><code>news/
  programming/</code></pre>
<p>No flying folder without context, nicely indented, which makes <em>my</em>
brain work better. 😌</p>
<p>It’s possible to do that with more shell scripting, but it quickly
gets dirty when you try to deal with deeper trees, avoiding duplicates,
and well it also gets slower because it’s a shell script.</p>
<p>This is my excuse for writing a program that builds maildir trees. In
that example, <code>mdstat ... -u</code> will list both <code>news</code> and
<code>news/programming</code> when there’s unread mail in the bottom folder ;
even if <em>news</em> is not a proper maildir (which <em>is</em> a feature).</p>
<p>If you’re curious, the code is on
<a href="https://git.cypr.io/oz/mdstat">git.cypr.io</a> with other more or less
personal projects. Of course it’s way faster than forking 4+ shell
commands.</p>
<h1 id="about-zig">About zig</h1>
<p>I’m not sure that I’ll write much more zig code in the
future.</p>
<p>It’s not that I don’t enjoy it, quite the contrary! The
language seems simple, and fairly easy to pick up if you’ve ever
dealt with C, Go, or Rust. It firmly sits somewhere between C and Go in
my mind ; which is a space that I don’t often need to work
into… but who knows?</p>
</article>
]]></description>
    <pubDate>Sun, 25 Sep 2022 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2022-09-25-mdstat.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>There and back again</title>
    <link>http://cyprio.net/wtf/2022-05-18-there-and-back-again.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>There and back again</h1>
        <span class="date">May 18, 2022</span>
        
    </header>
    <p>It’s interesting that there’s still a post about joining <a href="https://www.mailpoet.com/">MailPoet</a> on
the homepage. It makes this place feel very dusty, full of cobwebs, and
memories, and this post kind of ironical.</p>
<p>I stayed at MailPoet for more or less 2 years. I remember feeling
exhausted and depressed at the end of it. A nice <em>burnout</em> to use the
proper term: yes, you could get burnout before the COVID pandemic. These
were the days! That was mostly my fault for not seeing it coming, but
not entirely.</p>
<p>Now what’s new? After some freelancing gigs here and there, I’ve joined
back MailPoet ; now part of that “little” company that makes a well-known
CMS. I still don’t use it to publish these few rants, or become rich
with my newsletter, but it’s running <em>somewhere</em> where I share some
pictures of the kid, from time to time.</p>
<p>I’m still not a big fan of PHP (and not writing any, which should be
reassuring for the knowledgable PHP folks), but I really like working
with open-source, and making tools that solve the problems of actual
people.</p>
<p>Let’s try to avoid burnout this time because I feel too old to job-hop
like a jumpy millenial ; and there’s still work to do.</p>
</article>
]]></description>
    <pubDate>Wed, 18 May 2022 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2022-05-18-there-and-back-again.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Rebenchmarking Go, Crystal, and Ruby</title>
    <link>http://cyprio.net/wtf/2016-12-14-rebench-go-crystal-ruby.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Rebenchmarking Go, Crystal, and Ruby</h1>
        <span class="date">December 14, 2016</span>
        
    </header>
    <p>Don’t you ever tire of benchmarks? Most of those are inaccurate, hardly
reproducible, and often misleading. With that out of the way, I present a
benchmark of some <em>very fine</em> programs to compare the speeds of <a href="https://golang.org">Go</a>,
<a href="https://crystal-lang.org/">Crystal</a>, and <a href="https://www.ruby-lang.org/en/">Ruby</a>. ;)</p>
<p>I like Ruby a lot, have used Go for some years, and I consider that Crystal is
an interesting middle ground: both nice <em>and</em> fast, although not as mature as
the two others, yet.</p>
<p>I recently stumbled upon <a href="http://gcorbel.github.io/blog/blog/2015/09/07/benchmark-go-vs-crystal-vs-ruby/">Guirec Corbel’s benchmark</a> (in French) of
the three technologies, written in 2015. I though it would be interesting to
check the results a year apart, but we could hardly compare our benchmarks
since we’re not using the same hardware, and so I just re-benched everything.</p>
<p>I did some guess work to select the same versions that he <em>probably</em> benched in
September 2015, and proceeded to rerun these on my ancient (2011) laptop. This
way, we should get a more accurate idea of the evolution of our tools.</p>
<p>I ran these benchmarks from Archlinux, on this CPU: Intel(R) Core(TM) i7-2620M
CPU @ 2.70GHz. The programs are sourced from <span class="citation" data-cites="kostya">@kostya</span>’s <a href="https://github.com/kostya/benchmarks">benchmark
repository</a>: not all implementations are optimized, but they tend to
show how a language performs when the code is more or less identical.
Furthermore, I had to dig up old Crystal code to run on 2015’s version (0.7).</p>
<p>For each program, I simply measured the running time, taking the best of 5…
If you want averages and variance, <a href="https://github.com/oz/cyprio.net">pull-requests are warmly
welcomed</a>.</p>
<h1 id="results-2015">Results 2015</h1>
<p>We compare the execution time of 4 little benchmarks using:</p>
<ul>
<li>Go 1.5 (released 08/2015)</li>
<li>Crystal 0.7.7 (released 09/2015)</li>
<li>Ruby 2.2.4 (released 12/2015)</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">Benchmark</th>
<th style="text-align: left;">Go</th>
<th style="text-align: left;">Crystal</th>
<th style="text-align: left;">Ruby</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">Fibonnacci (1M)</td>
<td style="text-align: left;">4.16s</td>
<td style="text-align: left;">3.20s</td>
<td style="text-align: left;">11.34s</td>
</tr>
<tr>
<td style="text-align: left;">Brainfuck</td>
<td style="text-align: left;">6.01s</td>
<td style="text-align: left;">6.76s</td>
<td style="text-align: left;">3:03s</td>
</tr>
<tr>
<td style="text-align: left;">Havlak</td>
<td style="text-align: left;">40.87s</td>
<td style="text-align: left;">18.69s</td>
<td style="text-align: left;">N/A</td>
</tr>
<tr>
<td style="text-align: left;">Matmul (1500)</td>
<td style="text-align: left;">5.01s</td>
<td style="text-align: left;">4.89s</td>
<td style="text-align: left;">6:01s</td>
</tr>
</tbody>
</table>
<p>Now fear my mad GNUPlot skills:</p>
<figure>
<img src="https://static.cypr.io/wtf/bench-2015.png" alt="Execution time (2015): comparing Go, Crystal, and Ruby" />
<figcaption aria-hidden="true">Execution time (2015): comparing Go, Crystal, and Ruby</figcaption>
</figure>
<p>OK, let’s just remove Ruby here:</p>
<figure>
<img src="https://static.cypr.io/wtf/bench-2015-no-ruby.png" alt="Execution time (2015): comparing Go &amp; Crystal" />
<figcaption aria-hidden="true">Execution time (2015): comparing Go &amp; Crystal</figcaption>
</figure>
<h1 id="results-2016">Results 2016</h1>
<p>We compare again the execution time of the same 4 benchmarks using:</p>
<ul>
<li>Go 1.7.4</li>
<li>Crystal 0.20.1</li>
<li>Ruby 2.4.0-preview3</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: left;">Benchmark</th>
<th style="text-align: left;">Go</th>
<th style="text-align: left;">Crystal</th>
<th style="text-align: left;">Ruby</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">Fibonnacci (1M)</td>
<td style="text-align: left;">4.94s</td>
<td style="text-align: left;">3.18s</td>
<td style="text-align: left;">11.23s</td>
</tr>
<tr>
<td style="text-align: left;">Brainfuck</td>
<td style="text-align: left;">5.18s</td>
<td style="text-align: left;">3.29s</td>
<td style="text-align: left;">2:19s</td>
</tr>
<tr>
<td style="text-align: left;">Havlak</td>
<td style="text-align: left;">36.61s</td>
<td style="text-align: left;">14.32s</td>
<td style="text-align: left;">N/A</td>
</tr>
<tr>
<td style="text-align: left;">Matmul (1500)</td>
<td style="text-align: left;">4.13s</td>
<td style="text-align: left;">4.04s</td>
<td style="text-align: left;">5:46s</td>
</tr>
</tbody>
</table>
<figure>
<img src="https://static.cypr.io/wtf/bench-2016.png" alt="Execution time (2016): comparing Go, Crystal, and Ruby" />
<figcaption aria-hidden="true">Execution time (2016): comparing Go, Crystal, and Ruby</figcaption>
</figure>
<p>Again, without Ruby:</p>
<figure>
<img src="https://static.cypr.io/wtf/bench-2016-no-ruby.png" alt="Execution time (2016): comparing Go &amp; Crystal" />
<figcaption aria-hidden="true">Execution time (2016): comparing Go &amp; Crystal</figcaption>
</figure>
<h1 id="comparing-speed-gains">Comparing speed gains</h1>
<p>These numbers do not look like much, but let’s put them on a graph to
compare the gains. On this graph, 100% means 2015 speed, lower means
worse performance, so higher is better:</p>
<figure>
<img src="https://static.cypr.io/wtf/evolution.png" alt="Speed gains from 2015 to 2016 in Go, Crystal, and Ruby benchmarks" />
<figcaption aria-hidden="true">Speed gains from 2015 to 2016 in Go, Crystal, and Ruby benchmarks</figcaption>
</figure>
<p>It’s easier to grasp how these platforms are all<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> getting better.</p>
<p>These benchmarks only solicitate <em>one</em> CPU core, so they are a small
part of a bigger story where concurrency and parallelism play a major
role. Having said that, I’d really like it if Ruby’s MRI would get
faster <em>faster</em></p>
<p>I wonder if more Rubyists will switch to Crystal: clearly, the
platform is just getting warmed up, and will probably get speedier
with time. It is already on par with Go’s speed (even slightly
faster), has not even reached a 1.0 version, and yet continually
improves.</p>
<p>Courting Ruby developers with speed only may not work: after all, it
took <em>Rails</em> to make the language successful.</p>
<p>To me, Go has gained the safer-choice award after several releases. It
is a stable platform, continually improving, thanks to the backing of
Google. Also… channels are &lt;3. :)</p>
<p>The question we Rubyists should ask ourselves is maybe which tool
should we use when our next project hits a performance limit, safe of
fun?</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>I don’t really understand how Go has gotten worse at calculating
Fibonnacci. I hope you don’t do that too much in your day to day
business. :p<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</article>
]]></description>
    <pubDate>Wed, 14 Dec 2016 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2016-12-14-rebench-go-crystal-ruby.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Babies</title>
    <link>http://cyprio.net/wtf/2016-10-25-babies.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Babies</h1>
        <span class="date">October 25, 2016</span>
        
    </header>
    <p>I have put my startup-y projects aside for a while now. There is only so much
time that I would invest in something that won’t pay its server bills, no
matter how fun it is. Still, not easy.</p>
<p>Anyway, to move on I joined <a href="https://www.mailpoet.com/">MailPoet</a> a few months ago, taking over a project
that had been entirely developed by one person, using a technology that no one
else there knew very well (why?). That one person decided to leave. Rather than
let the project die, MailPoet decided to hire a new person: I have a few
years of experience doing ruby and devops, so the role fits me quite well.</p>
<p>Furthermore, I am used to 100% remote work, the team <em>is</em> nice, the
to-be-announced project is as awesome as it is challenging. New team, new
project, new things to learn. <em>So far, so good</em>.</p>
<p>So you know, new job is nice and all, but I will focus on more important things
for now, like my new born daughter. :)</p>
</article>
]]></description>
    <pubDate>Tue, 25 Oct 2016 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2016-10-25-babies.html</guid>
    <dc:creator>oz</dc:creator>
</item>
<item>
    <title>Scrum and agile</title>
    <link>http://cyprio.net/wtf/2016-08-15-scrum-and-agile.html</link>
    <description><![CDATA[<article>
    <header>
        <h1>Scrum and agile</h1>
        <span class="date">August 15, 2016</span>
        
    </header>
    <blockquote>
<p>Like a failed communist state that equalizes by spreading poverty,
Scrum in its purest form puts all of engineering at the same low
level: not a clearly spelled-out one, but clearly below all the
business people who are given full authority to decide what gets
worked on.</p>
</blockquote>
<p><a href="https://michaelochurch.wordpress.com/2015/06/06/why-agile-and-especially-scrum-are-terrible/">Why “Agile” and especially Scrum are terrible</a></p>
</article>
]]></description>
    <pubDate>Mon, 15 Aug 2016 00:00:00 UT</pubDate>
    <guid>http://cyprio.net/wtf/2016-08-15-scrum-and-agile.html</guid>
    <dc:creator>oz</dc:creator>
</item>

    </channel>
</rss>
