<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
 <title>xmdr</title>
 <description>_Het_ blog (web log) van xmdr</description>
 <link>https://xmdr.nl/blog</link>
 <copyright>xmdr assumeert het kopiëerrecht op alles wat je aanraakt. Door dit blog of bericht te lezen ga je akkoord met deze overeenkomst.</copyright>
 <lastBuildDate>Mon, 11 May 2026 21:34:15 UTC</lastBuildDate>
 <pubDate>Mon, 11 May 2026 21:34:15 UTC</pubDate>
 <ttl>1800</ttl><item>
				<title>Semafoor</title>
				<pubDate>Tue, 06 Aug 2024 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Semafoor</h1><p>Je staat voor het imposante gebouw. Je kijkt omhoog, naar de top. Leuk om van te bungeejumpen zonder touw misschien. Weer omlaag. <i>Zucht.</i> Naar binnen dan maar. In de hoop dat je je hoofd in het hier en nu kan krijgen, kijken je ogen wat rond, naar willekeurige vormen en voorwerpen. Je voeten doen zoekend mee, alsof je een verdwaalde pelgrim in een kathedraal bent die een onzichtbaar labyrint volgt, een mozaïek met meer kronkels dan tegels. Mensen trekken hun wenkbrauw op, en voelen zich op hun hoede. <i>Wie is dit? Wat komt hij hier doen? Hij wijkt af van de norm, dus op welke manieren wijkt hij nog meer af? Ben ik in gevaar? Gaat hij iets raars zeggen of doen? Een ongemakkelijk en vervelend gevoel omarmd mij, hoe kom hier toch in godsnaam zo snel mogelijk van af!?</i> Ze willen je niet helpen, integendeel, ze willen zo snel mogelijk ontsnappen, als ze überhaupt al de klauwen van een interactie niet konden ontkomen. Je voelt het aan, al je ledematen zitten in de weg, en je voelt je bezwaard om te bestaan. Je longen zijn in paniek en je hart ontploft. Als een microfoon die voor de speaker wordt gehouden versterkt de gemeenschappelijke misère zich; als een stinkende drol in een te druk zwembad kabbel je dwars door het sociale vaarwater heen. Hoe lang nog voordat iemand de badmeester roept??</p>

<p>Je kijkt wat rond over de parkeerplaats. Als het paard van Sinterklaas springt je blik over de daken van de geparkeerde bolides, op zoek naar jouw autootje. Wie weet heeft hij wel pootjes gekregen! Ah, daar! Gelukkig, hij staat er nog (op wielen). Hoofdingang! Nou, dit is de plek; hier moet je zijn. Naar binnen dan maar. Spannend! Je kijkt wat rond naar de architectuur, maar, op zoek naar gezichten, balies, liften, trappen, en genummerde deuren, wijkt je lijn van zicht niet te ver af naar boven of beneden. Een vriendelijke mevrouw staat ineens met een glimlach voor je, en met een mengsel van enthousiasme en opluchting voel je aan dat je in goede handen bent.</p>

<p>Laconiek loop je de tent binnen, naar je doel toe. In je pas aarzel je niet. Je benen, niet je ogen, zijn aan het wandelen. Niemand kijkt een tweede keer naar je om, en niemand vraagt je vriendelijk waar je moet zijn. Als een tandwiel in een gesmeerd mechaniek baan je je een weg door de mensen. Je loopt een deur door waar "Alleen personeel" op staat. Er zijn wat mensen, je geeft een knik en een groet, en loopt zonder meer door. Niemand zegt er wat van, ze zijn alweer terug bij hun gesprek over wiens vakantie het verste weg was.</p>]]></description>
				<category domain="https://xmdr.nl/verhalen">verhalen</category>
				<link>https://xmdr.nl/read/semafoor</link>
				<guid isPermaLink="true">https://xmdr.nl/read/semafoor</guid>
			</item><item>
				<title>Waarom ik (niet) post</title>
				<pubDate>Tue, 23 Jul 2024 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Waarom ik (niet) post</h1><p>En weer een hiaat van maanden sinds mijn laatste (non-shit)post. Maar waarom? Of eigenlijk, waarom niet? Als ik wèl post, waarom dan ineens wel weer?</p>
<p>Ik schrijf om de volgende redenen:</p>
<ul>
<li>als geheugensteuntje voor de volgende keer dat ik iets soortgelijks moet doen of bedenken</li>
<li>als therapie, om irritaties van me af te schrijven </li>
<li>om beter over dingen na te denken en ideeën uit te werken</li>
<li>ik lees mezelf graag praten</li>
</ul>
<p>Publiceer om:</p>
<ul>
<li>mezelf te promoten, staat goed op m&#39;n cv enz enz</li>
<li>anderen met soortgelijke problemen of ideeën te helpen</li>
<li>discussies met lezende vrienden en familie uit te lokken</li>
<li>mailtjes van wildvreemden te krijgen dat Mark Rutte inderdaad een klootzak is</li>
</ul>
<p>Toevallig had ik het laatst met mijn vader over stress, en de invloed van dingen waar je maar niet aan toe lijkt te komen, die je jezelf volledig vrijwillig hebt opgelegd zonder schijnbare reden. Bijvoorbeeld het schrijven van een blogpost over iets wat je hebt gedaan of bedacht.</p>
<p>Algemeen gezien houd ik mijn shit bij met een issue board: ik bedenk wat ik wil doen met mijn leven en schrijf de benodigde taken uit, en die leiden dan een leven in de vorm van een ticket, verhuizende van de &quot;todo&quot;-kolom naar &quot;researching&quot; naar &quot;doing&quot; en dan uiteindelijk over de Jordaan naar &quot;done&quot; danwel &quot;don&#39;t&quot;. </p>
<p>En ja, ook ideeën voor blogposts ondergaan deze lijdensweg. Maar helaas komen ze niet vanzelf uit &quot;todo&quot;. Er is natuurlijk een reden dat ik sommige posts al meer dan een jaar uitstel: ik voel er niet echt passie voor, het is puur dat ik het mezelf heb opgelegd, want het zou zo leuk staan op mijn blog. Nou, dan documenteer ik ze hier maar kort en daarmee schrijf ik ze (van me) af.</p>
<h2 id="raytracing-op-de-rekenmachine">Raytracing op de rekenmachine</h2>
<p><img class="right" src="/src/media/waarom_ik_niet_post/reflectie.png" alt="3 balletjes met reflecties maar zonder shading" title="3 balletjes met reflecties maar zonder shading"></p>
<p>Ik heb 3 jaar geleden <a href="https://gitlab.com/xmdr/illumina">een raytracer</a> geschreven, en ook zo&#39;n 3 jaar geleden een port gemaakt voor mijn TI-84 Plus CE-T. Heb het niet eens afgekregen want alleen al het renderen van de raycasting zelf (hit/no hit) duurt een fucking uur op de 320×240 ingebouwde LCD, gewoon niet boeiend. Misschien een half gebakken discussie over fixed point getallen i.p.v. ints en floats (alhoewel het stiekem gewoon ints zijn, zie het als rekenen met &quot;millimeters&quot; ipv &quot;meters&quot; als het SI stelsel gebaseerd was op machten van 2 en niet 10). <p>
<p><img class="right" src="/src/media/waarom_ik_niet_post/glitchy.png" alt="glitchy balletje" title="glitchy balletje"></p>
<p>Wow deze render van een bal laat een patroon van floating point inaccuracies zien ofzo omdat ik hier de epsilon was vergeten in mijn floating point comparisons voordat ik overstapte naar fixed point omdat deze rekenmachine van €100+ uit 2013 een 8 bit chip van de jaren 70 gebruikt die helemaal geen FPU heeft waardoor alle FLOPs in software geëmuleerd moeten worden wat FUCKING TRAAG IS.</p>
<h2 id="routerfirmware-flashen">Routerfirmware flashen</h2>
<p>Voor mijn moeder heb ik haar Ziggo wifi repeater ding (een TP-Link Archer C7 maar dan met Ziggo branding) geflasht zodat alle features van de router weer beschikbaar waren, in plaatst van een softwarematige beperking op alles behalve wifi repeating. Dit was een hele opgave en op zich een leuke uitdaging, maar uiteindelijk kwam het er neer op &quot;ik had al Linux geïnstalleerd, weet een terminal te gebruiken, kan een <a href="https://gathering.tweakers.net/forum/list_messages/1674027/0">relevant Tweakers thread</a> vinden (vereist niet alleen serieuze skills maar ook luck), kan desbetreffende thread lezen en volgen, kan een obscuur protocol genaamd <a href="https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol">TFTP</a> gebruiken met commandline tools ervoor&quot;.</p>
<p>Het had gewoon niet echt de potentie om een waardevolle write-up te zijn dus, want dit probleem ga ik niet opnieuw krijgen (en anders heb ik het nu alsnog stiekem voor mezelf vastgelegd), en mijn post zou andere mensen niet beter helpen dan het Tweakers thread. En ik heb voor de grap zelfpromotie wel bovenaan gezet, maar eigenlijk is dat de laatste reden waarom ik iets zou posten. Laat je niet foppen door de stem waarmee ik schrijf - ik heb helemaal geen behoefte om mijn ego te voeden en de wereld te laten zien hoeveel slimmer ik wel niet ben dan de rest, alleen maar omdat ik als kind niet met vrienden maar met een computer speelde waardoor ik wel weet hoe ik Arch Linux moet installeren maar niet weet hoe gezond sociaal contact verloopt.</p>
<h2 id="braille-en-anonium">Braille en Anonium</h2>
<p>Ik had een keer het idee om een imageboard/forum a la 4chan te maken, maar dan federated en bedoeld voor intelligente en inhoudelijke discussies door middel van een <a href="https://github.com/ballerburg9005/IQcaptcha">automatisch gegenereerde mini IQ test als captcha</a> (lol! Haha mensenproblemen oplossen met tech die is zeker van gisteren!&quot;&gt;&lt;/a&gt;.&quot;)}&quot;DROP TABLE users;--&quot;,:1;uitroepteken).</p>
<p>Uiteindelijk enkele posts mogen hosten op die site maar het liep geen vaart, en ik kwam er al heel snel achter wat een &quot;4chan achtig imageboard&quot; precies inhoudt qua publiek dat je ermee trekt. Een hoog IQ zegt dus niks over de volwassenheid of beleefdheid van een persoon, zeker niet als je die hebt vastgesteld met een enkele <em>progressive matrix test</em>. Dus stekker eruit en snok. Maar nu komt het!</p>
<p>Posters waren anoniem, maar wel uniek gekenmerkt met een computer gegenereerd pseudoniem, was het idee. Hoe geef je dat weer? Bitcoin heeft <a href="https://github.com/bitcoin/bips/blob/master/bip-0039">een woordenlijst + algoritme</a> om de binaire seeds van wallets te vertalen naar een reeks wartaal, wat beter te lezen en onthouden is voor mensen (die kunnen lezen). Maar <em>ik</em> had het bril-jànte idee om de bits grafisch te vertonen. En wat lijkt daarop? Braille! Eén bolletje of géén bolletje, 1 of 0. Braille heeft in Unicode 2 soorten tekens: matrixen van 2×3 stipjes per teken en matrixen van 2×4 stipjes per teken. Ik ging voor de 2×4, want 128 bits <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">(een GUID)</a> = 2×4×16 braillebolletjes.</p>
<p>Helaas kon ik geen font vinden die zowel braille als mijn esthetische visie ondersteunde. Dus ik maakte mijn eigen vector font (svg font). Dat was veel werk dus ik schreef een script. Die dealde er ook mee dat de <a href="https://en.wikipedia.org/wiki/Braille_Patterns#Identifying,_naming_and_ordering">officiële volgorde van de Braillebolletjes</a> wat apart is vanwege historische redenen.</p>
<p><strong>Intussen is in elke moderne browser support voor SVG fontfaces geschrapt.</strong> En achteraf had ik net zo makkelijk (makkelijker) gewoon direct SVG kunnen genereren in de HTML want uiteindelijk was er eigenlijk geen enkele overeenkomst met braille behalve dat er sprake was van een matrix van stippeltjes. Niet alsof een blinde een braille-afdruk gaat maken van de pagina en dan zegt &quot;oh ja, dit is weer typisch een argument van WV#%
™£ ˚¯$£™¸ ̣˘$V T?JKL÷Ä P*&quot;.</p>
<p>Het meest poignante onderdeel was de PHP UTF-8 encoder en decoder die ik ervoor had gemaakt, en überhaupt het feit dat een taal als PHP over bitwise operators beschikt. Alsnog lekker geprutst!</p>
<pre><code class="lang-php"><span class="php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">utf8</span><span class="hljs-params">($num)</span></span>{
    <span class="hljs-keyword">if</span>($num&lt;=<span class="hljs-number">0x7F</span>)       <span class="hljs-keyword">return</span> chr($num);
    <span class="hljs-keyword">if</span>($num&lt;=<span class="hljs-number">0x7FF</span>)      <span class="hljs-keyword">return</span> chr(($num&gt;&gt;<span class="hljs-number">6</span>)+<span class="hljs-number">192</span>).chr(($num&amp;<span class="hljs-number">63</span>)+<span class="hljs-number">128</span>);
    <span class="hljs-keyword">if</span>($num&lt;=<span class="hljs-number">0xFFFF</span>)     <span class="hljs-keyword">return</span> chr(($num&gt;&gt;<span class="hljs-number">12</span>)+<span class="hljs-number">224</span>).chr((($num&gt;&gt;<span class="hljs-number">6</span>)&amp;<span class="hljs-number">63</span>)+<span class="hljs-number">128</span>).chr(($num&amp;<span class="hljs-number">63</span>)+<span class="hljs-number">128</span>);
    <span class="hljs-keyword">if</span>($num&lt;=<span class="hljs-number">0x1FFFFF</span>)   <span class="hljs-keyword">return</span> chr(($num&gt;&gt;<span class="hljs-number">18</span>)+<span class="hljs-number">240</span>).chr((($num&gt;&gt;<span class="hljs-number">12</span>)&amp;<span class="hljs-number">63</span>)+<span class="hljs-number">128</span>).chr((($num&gt;&gt;<span class="hljs-number">6</span>)&amp;<span class="hljs-number">63</span>)+<span class="hljs-number">128</span>).chr(($num&amp;<span class="hljs-number">63</span>)+<span class="hljs-number">128</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GUID</span> </span>{
    <span class="hljs-keyword">public</span> $bytes;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span><span class="hljs-params">($bytes = false)</span></span>{
        <span class="hljs-keyword">if</span> ($bytes === <span class="hljs-keyword">false</span>){
            <span class="hljs-keyword">$this</span>-&gt;bytes = random_bytes(<span class="hljs-number">16</span>);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">$this</span>-&gt;bytes = $bytes; 
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toHex</span><span class="hljs-params">()</span></span>{
        <span class="hljs-keyword">return</span> bin2hex(<span class="hljs-keyword">$this</span>-&gt;bytes);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toBraille</span><span class="hljs-params">()</span></span>{
        $braille = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">foreach</span> (str_split(<span class="hljs-keyword">$this</span>-&gt;bytes) <span class="hljs-keyword">as</span> $byte) {
            $braille .= utf8(<span class="hljs-number">0x2800</span>+hexdec(bin2hex($byte)));
        }

        <span class="hljs-keyword">return</span> $braille;
    }
}</span>
</code></pre>
]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/waarom_ik_niet_post</link>
				<guid isPermaLink="true">https://xmdr.nl/read/waarom_ik_niet_post</guid>
			</item><item>
				<title>Paar tientjes</title>
				<pubDate>Sat, 22 Jun 2024 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Paar tientjes</h1><p>Ach, voor die paar tientjes... Honderd euro is maar tien tientjes! En hoe veel is duizend? Tien tien tientjes! Dus wat maakt het dan nog uit? Spendeer uw tientjes dan toch even rijkelijk als zorgeloos!</p>]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/paar_tientjes</link>
				<guid isPermaLink="true">https://xmdr.nl/read/paar_tientjes</guid>
			</item><item>
				<title>Django migration migraine</title>
				<pubDate>Sat, 20 Apr 2024 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Django migration migraine</h1><p>Het was een tijd en een plaats. Ik - oke, ik stop met dit flauwe gedoe. Het was ochtend en ik zat achter mijn bureau. Ik was bezig met een kleine refactor van wat code. Dacht ik. </p>
<p>Voor een nieuwe module waar ik mee bezig was, had ik een aantal zogeheten Models gemaakt. Dit is doorgaans Object Oriented jargon voor &quot;database tabel&quot;.</p>
<p>Normaal hebben database tabellen een identificatie column, de <em>primary key</em>. Vaak een getal. &quot;Dit is rij nr. 593 in de tabel&quot;. Maar mijn model moest over op willekeurige UUIDs, want de rest van de models in de code waren ook zo, en allerlei onderdelen berusten op die impliciete(!) standaard. Een UUID is ook een getal, maar dan van 32 hexadecimale cijfers (want 128 bits). En niet oplopend zoals de typische <code>ID INT PRIMARY KEY</code> is.</p>
<p>Een simpele klus, want het hele idee van een Object Relational Mapper zoals Django is dat je je Modellen kan uitdrukken in de object oriented programmeertaal waar je in werkt, en dat Django onder de motorkap met de database stoeit om het gewoon te laten werken. Ik zou gewoon een regeltje code aan kunnen passen en dan zou het werken. Dus ik pas die model aan zodat het oude nummertje een andere naam had en niet meer de primary key was. Daarnaast had ik een nieuw UUID veld, primary key, toegevoegd.</p>
<p>Django had andere plannen.</p>
<p>Als je de models verandert, moet je <code>poetry run ./manage.py makemigrations</code> doen. Daarmee maakt Django ergens in het project een bestandje met stappen waarmee de database daadwerkelijk omgekat zal worden. Dan doe je <code>poetry run ./manage.py migrate</code>, zodat hij die bestandjes 1 voor 1 afgaat, waarop hij accuut zeikt dat hij niet 2 primary keys tegelijk kan hebben.</p>
<p>Wat doet Django nou, die domme malware (een worm, wan-ORM): Dat kut ding probeert eerst een nieuw primary key UUID veld te maken en <em>dan pas</em> het oude veld niet-primary te maken. Natuurlijk kun je niet 2 primary keys tegelijk hebben. Ik heb ook maar 1 BSN. Op dat moment wil ik er vanaf zijn en draai ik de 2 operaties om, en hij gaat verder.</p>
<p>Vervolgens zeikt hij over een <code>PROTECT</code>: er waren andere models gelinkt aan de veranderde model. De database snapt dan niet dat de primaire identificatie momentaan verdwijnt - dat brengt alle relaties in het geding. Maar waarom is dit mijn probleem? Waarom regelt Django dit niet gewoon??</p>
<p>Aangezien al die models toch nog geen instances hadden (in database parlance: alle tabellen zijn nog leeg), en er dus in feite nog geen relaties waren, flikkerde ik die ganse foreign key gewoon weg. Hop, geen <code>ON DELETE PROTECT</code> gezeik.</p>
<p>En daarna maakte ik een tweede migraine die dan de foreign key weer terug zet. Oh, en nu hij vraagt een default omdat hij denkt dat er misschien bestaande rijen kunnen zijn, waarvan de relatie dan natuurlijk onbekend is? Zucht! Nee Django, die bestaan niet, want al die models zijn spiksplinternieuw en nog leeg, maar dat kan ik je niet vertellen op een manier die je begrijpt! Dan maar <code>None</code> als default.</p>
<p>Dat accepteert hij, ook al is die foreign key column als &quot;not null&quot; geregistreerd(??). Bijzonder. En niet erg intuïtief. En niet erg logisch. En niet erg gebruiksvriendelijk. En niet erg goed doordacht. En niet echt geschikt voor professionele software.</p>
<p>In SQL was dit makkelijker geweest, en had ik het direct goed gedaan:</p>
<ol>
<li>In de andere tabellen, verwijder de foreign key relatie op <code>x</code> en hernoem de column naar <code>old_x_id</code>.</li>
<li>In tabel <code>x</code>, hernoem <code>id</code> naar <code>old_id</code> en verwijder primary</li>
<li>In tabel <code>x</code>, maak een UUID <code>id</code> en maak die primary</li>
<li>In de andere tabellen, maak de nieuwe foreign key <code>x_id</code>, door <code>tabel.old_x_id</code> te matchen aan <code>x.old_id</code> en dan <code>x.id</code> te selecten in <code>tabel.x_id</code>.</li>
</ol>
<p>Maar dat snapt Django niet. Dat weet Django niet. Ook al is het <em>the one job</em> die Django heeft.</p>
<p>Al met al heb ik toch te veel verwacht van Django de worm. Had ik kunnen zien aankomen: in Django kun je niet eens filters toepassen op &quot;calculated fields&quot;. (In database parlance, &quot;view&quot;, niet te verwarren met een &quot;view&quot; in het Model-View-Controller model (&quot;model&quot; hier niet te verwarren met Model in het ...))</p>
<p>Het wordt nog beter: ik dus na dit gedoe mijn migraine bestandje pushen naar mijn git branch; merge request maken om mijn branch naar main te mergen. Wat blijkt: iemand anders had al een &quot;migratie nr. 7&quot; gemaakt. Want ja, zo houdt Django dat bij! Dus nu krijg je tijdens een <code>poetry run ./manage.py migrate</code> gezeik over een <em>migration merge</em>. Bellissimo.</p>
<p>Ik zou graag willen zien van mijn collega&#39;s in het veld dat ze iets hogere standaarden krijgen voor de tools waar ze mee werken. En dat ze verder kijken dan hun OOP lang is. Als dat de standaard houding was geweest, had ik dit gezeik niet gehad. Was mijn energie en focus voor de dag niet opgeslokt door een onnodig zwart gat. Had ik gewoon de taak die ik eigenlijk erna had willen doen, kunnen doen.</p>

<p>&lt;/rant&gt;</p>
]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/django-migration-migraine</link>
				<guid isPermaLink="true">https://xmdr.nl/read/django-migration-migraine</guid>
			</item><item>
				<title>Nieuwe Malware: jupyter</title>
				<pubDate>Sun, 18 Feb 2024 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Nieuwe Malware: jupyter</h1><p>Malware: malafide software.</p>
<br>
<p>Voor school moet ik per se jupyter notebook gebruiken. Eerste impressie.</p>
<p>Downloaden op manjaro is aids. Niet de schuld van jupyter. Ik moet <code>pip install jupyter</code> doen, maar pip klaagt dat ik al een package manager (pacman) heb en zegt dat ik <code>pacman -S python-jupyter</code> - ahem - <code>sudo pacman -S python-jupyter</code> moet doen. Nou, die package bestaat dus niet, dus dan toch maar <code>pip install jupyter --break-system-packages</code>.</p>
<p>Opstarten is interessant. Ik zoek <code>jupyter</code> in mijn startmenu. Er verschijnt een default applicatie icoontje. Klik - en er komt een terminal tevoorschijn. Dan volgt kort daarop een browserscherm met een soort file manager. Ook een popup of ik cookies of analytics of iets wil, geen idee, uit reflex klikte ik het weg voordat ik het kon lezen.</p>
<p>Ik had al een mapje, <code>/home/michael/uni/2a-scicomp</code>. Die kon jupyter niet vinden. Prima, dan maar die filemanager-achtige sidebar van jupyter gebruiken en handmatig navigeren...</p>
<p>Het bestand opent en het doet me denken aan Rdocs. Brrr. Ik klik dubbel op de blokjes gerenderde html en latex, en ze transformeren in een soort editable markdown. Ik edit het, maar geen idee hoe ik nu weer de latex krijg. Nogmaals dubbel klikken? Nee. Rechtermuis iets? Nope. Ik zie een &quot;herstart&quot; icoontje, dat maar doen. Hij klaagt iets over variabelen resetten. Geen flauw idee wat de implicaties zijn? Fuck it, we klikken oké. Maar nee hoor, geen latex. Zal wel.</p>
<p>Ik selecteer wat text. Rechtermuisknop: ik kan het niet kopiëren. Ik kan wel de "cell" kopiëren... Oke, dat dan maar? Ctrl+v ergens anders (gewoon in een DEGELIJKE editor) en dan krijg ik een heel andere snippet geplakt - iets van eerder op de dag. Dat werkt dus niet. Fijnnnn. Het domme is, de shortcut ctrl+C werkt dus wel normaal. Dus wat dat nou voor autistisch gedoe is met dat context menu, geen idee. Gelukkig heeft FireFox een oplossing voor irritante malware die je context menu kaapt, shift+rechterklik en bam, nu kan ik gewoon dingen kopiëren met de muis, zoals God het bedoeld heeft.</p>
<p>Kan ik dan ten minste dat terminal venster weg doen? Ctrl+D, ctrl+Z. Ah, die worden opgeslokt. Dus ik kan het process niet in de background verder laten gaan en dan disownen. Hmm, gewoon eens de terminal dicht klikken. Oh oeps, nu klaagt de webapp dat hij geen verbinding meer heeft met de server.</p>
<p>Dit wordt een lange, LANGE periode...</p>]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/nieuwe-malware-jupyter</link>
				<guid isPermaLink="true">https://xmdr.nl/read/nieuwe-malware-jupyter</guid>
			</item><item>
				<title>Waar ben ik nou weer aan begonnen</title>
				<pubDate>Sat, 09 Dec 2023 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Waar ben ik nou weer aan begonnen</h1><img src="/src/media/piano/fabriek.png" alt="Illustratie van de Emerson pianofabriek">

<p>Een tijdje geleden heb ik voor mijn verjaardag een keyboard gekregen. Gewoon een simpel keyboard met 61 toetsen, hij heeft niet eens een midi uitgang. Maar het is leuk om weer muziek te spelen en hij bevalt me daarvoor prima. Het deed me denken aan mijn kindertijd: toen gingen we tijdens kerst naar mijn tante in Brabant, die op een oude boerderij woonde. Daar, diep weggestopt in het huis, was een koude en muffe biljartkamer, zonder ook maar één raam naar buiten. Hier werden de drukke kinderen naartoe gewezen, om zich te vermaken met de biljarttafel, hometrainer, dikke kist met speelgoed, en achterin een buffetpiano.</p>
<p>Door de jaren heen raakten sommige toetsen hun beleg kwijt, en andere toetsen kwamen vast te zitten. De toetsen die wel goed werkten, klonken niet volledig zuiver meer. Maar toch was het altijd hartstikke leuk om willekeurige combinaties in te drukken op dat ding! Inmiddels is dat huis verkocht en zal die arme piano wel ergens in een houtkachel beland zijn.</p>
<p>Je snapt waar dit naartoe gaat. En anders snap je het nu wel. En <em>anders</em> anders weet ik niets te zeggen over je situatie. Op een dag zat ik marktplaats af te struinen, gewoon nieuwschierig. Goh, zo veel buffetpianos. €300, €100, €50, gratis... Vraag me af waarom ze zo goedkoop zijn. En toen zag ik een bijzonder gave piano. Voor een bijzonder gave prijs. Op de foto zag hij er bijna uit als nieuw, al het beleg zat nog op de toetsen en de afwerking van het hout leek nog netjes.</p>
<p><img src="/src/media/piano/marktplaats.jpg" alt="Foto van Marktplaats"></p>

<p>Een antieke &quot;Emerson&quot;. Na een korte internet excursie vond ik op <a href='https://antiquepianoshop.com/online-museum/emerson/'>deze site</a> dat het een oud Amerikaans merk betreft, opgericht in 1849 en gestopt vanwege de tweede wereldoorlog. Op de site staan scans van meerdere catalogi (deze piano, een &quot;style 14&quot; is te vinden in die van 1889).</p>

<figure>
<img src="/src/media/piano/catalogus.png" alt="Foto van relevante cataloguspagina">
<figcaption>
<p>Style 14, Cabinet Grand Upright.</p>
<p>New scale, three strings or unisons, 7⅓ octaves, ivory keys, patent repeating action, new patent full iron plate, rolling fall, patent swing desk, engraved or bronze panels, engraving on front and ends, extra carved trusses and patent mouse-guard pedal feet.</p>
</figcaption>
</figure>

<figure>
<img src="/src/media/piano/restauratie.png" alt="Foto van gerestaureerde soortgelijke piano">
<figcaption>Een soortgelijke piano, gerestaureerd door Antique Piano Shop.</figcaption>
</figure>

<p>Snel maakte ik een afspraak, want ik zou wel niet de enige zijn die hem wilde hebben, al helemaal als hij gratis is...</p>
<p>Het was een wat koudere herfstdag, en ik reed op mijn trouwe brommer, gemaakt van echt Chinesium, naar het Groningse dorpje Huizen. Er werd door een vriendelijke vrouw opengedaan die mij naar de woonkamer begeleidde. Daar ontmoette ik de verkoper (weggever) John. Met hem liep ik weer de helft terug, naar een kamertje die mij qua mufheid aan de biljartkamer deed denken. Daar stond hij dan! De piano.</p>
<p>Het eerste wat mij opviel waren de krassen en beschadigingen op en aan het hout. Die waren op de foto toch minder duidelijk doorgekomen. Ook een aantal toetsen waren wat minder wit, maar nog wel intact! Toen ging ik maar eens kijken of hij nog geluid maakte. En ja! Elke toets deed het, alhoewel de een wat stroever dan de ander. En sommige toetsen kwamen niet meer omlaag. Maar de piano klonk prima. Er zat geen enkele toets bij die op zichzelf vals klonk, en de zuiverheid van akkoorden was ook niet te slecht. Zelfs de bassnaren klonken nog goed.</p>
<p>Ik deed de kap omhoog en keek bij de piano naar binnen. Het was in de piano erg stoffig, en het rook apart. Ik zag op het ijzeren frame een patentjaar, 1885-1886. Ik zag erop toe dat alle hamers en dempers nog bewogen, alhoewel sommige dus wat stroef. Enkele hamers bleven hangen in de aanslag. Ik bekeek het klankbord, en ik kon geen scheuren zien!</p>
<p>Ik wilde de achterkant even bekijken: een spinnenparadijs. Met enorm veel moeite kreeg ik samen met John de zware antieke piano een klein stukje van de muur af. Overal waren dunne grijswitte draadjes gespannen tussen de muur en de piano, die op mijn vest bleven plakken terwijl ik mijn best deed om zo veel mogelijk te zien in zo min mogelijk tijd.</p>
<p>Hierna vertelde hij mij doodleuk dat de piano al 40 jaar niet meer gestemd was. Het was van zijn broer geweest, die verhuisd was en geen ruimte meer had. Het was eigenlijk de bedoeling dat hij hem weer op zou halen, maar daar is het nooit van gekomen. Wat het verhaal van de piano daarvoor was, wist hij niet.</p>
<p>Al die tijd heeft de piano achter de vitrages gestaan, precies tussen 2 radiatoren in. Zo&#39;n beetje het ergste wat je kan doen! Gelukkig werd de ruimte nooit gebruikt en waren de radiators consistent uitgebleven. Vandaar dat hij nog zo goed klonk.</p>
<p>Dus. De piano is, gezien de leeftijd van 130+ jaar, in goede staat. Hij is gratis. Sommige toetsen lopen wat aan, maar het is allemaal gewoon hout en vilt; in principe goed zelf te repareren, had ik bedacht, met in mijn achterhoofd een aantal YouTube filmpjes van pianorestauraties. Ik vroeg hem wanneer de piano uiterlijk weg moest.</p>
<p>&quot;Dat moet wel lukken&quot;.</p>
]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/emerson-piano</link>
				<guid isPermaLink="true">https://xmdr.nl/read/emerson-piano</guid>
			</item><item>
				<title>Objectief Verkeerd Leven</title>
				<pubDate>Sat, 16 Sep 2023 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Objectief Verkeerd Leven</h1><h2 id="wc-rol-verkeerd-om">WC-rol verkeerd om</h2>
<p>Als je een velletje wil vastpakken, waarom wil je dan je hand tegen de muur hebben? Waarom hang je de rol niet gewoon goed?</p>
<h2 id="alleen-droog-wc-papier">Alleen droog wc-papier</h2>
<p>En daarover: haal eens een smeuïge drol langs je gezicht. Smeer dit vervolgens uit met droge velletjes papier. Voel je je nu schoon?</p>
<h2 id="alles-met-de-afwasborstel">Alles met de afwasborstel</h2>
<p>Een afwasborstel is er alleen om de meest hardnekkige tjak van je shit af te schrobben. Wat zit je nou aan te klooien met die vork man? En zit er soms mineraalopbouw op dat glas ofzo? Een borstel maakt ook niet uniform schoon, want daar is het ook niet voor bedoeld, <em>het is bedoeld om shit los te krabben.</em> Tip: koop een spons. Shit, ze hebben tegenwoordig zelfs speciale afswassponsjes!</p>
<h2 id="kunststof-snijplanken">Kunststof snijplanken</h2>
<p>Zo te zien ben je hard bezig om zo veel mogelijk hormoonverstorende stoffen in je eten te krijgen; waarom doe je dat? Zelfde voor kunststof (kinder)bekers, bestek, kommetjes en borden, en zo voorts. Gebruik gewoon hout, rvs, keramiek, glas ofzo.</p>
<h2 id="waterfles-niet-schoonmaken-en-of-vervangen">Waterfles niet schoonmaken en/of vervangen</h2>
<p>bro...</p>
<h2 id="donkere-was-met-lichte-was-wassen">Donkere was met lichte was wassen</h2>
<p>Wow cool vaalgrijs shirt met pluisjesdesin!</p>
<h2 id="platgedrukte-tostis">Platgedrukte tostis</h2>
<p>Fuck you. Een compact brok brood is niet &quot;knapperig&quot;.</p>
<h2 id="oc-an-de-parfum">Océan de parfum</h2>
<p>Oh de toilet of oh de parfum kan lekker ruiken. Maar niet als je jezelf verdrinkt in die shit. Laat m&#39;n neus en longen met rust bitch.</p>
<h2 id="nieuwschierig-okselhaar">Nieuwschierig okselhaar</h2>
<p>Als het okselhaar uit je mauwen kruipt dan is het tijd om die shit te scheren want het staat crunchy as fuck. Waarom ziet het er ook altijd uit als een mishandelde struik en nooit eens à la Andrélon? Laat m&#39;n arme ogen met rust motherfucker.</p>
]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/objectief_verkeerd_leven</link>
				<guid isPermaLink="true">https://xmdr.nl/read/objectief_verkeerd_leven</guid>
			</item><item>
				<title>Routine</title>
				<pubDate>Tue, 12 Sep 2023 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Routine</h1>Hijg... Hijg... Hijg... Als ik opsta, begin ik altijd met pushups tot mijn armen zeuren. Dan doe ik er nog een paar om het mijn armen af te leren. Vervolgens doe ik crunches, tot mijn maag er pijnlijk van draait. Tak... Tak... TAK... Dit volg ik op door mijn bovenarmen tegen de vensterbank te gooien, mijn schenen tegen de rand van een kast, en mijn tenen tegen de poten van mijn bed. Als je je ooit voor karate geïnteresseerd hebt, dan weet je dat de Japanners dit "kata" noemen. Vervolgens douche ik. Het liefst wissel ik dan snel tussen 15 en 51 graden, zodat mijn lichaam goed wakker schrikt. Pets... Pets... PETS... Zodra ik de kraan dicht ram, bal ik mijn vuisten en hak ik in op mijn maag. Ten slotte droog ik mezelf met het zachtste frotté, kleed ik mij in het fijnste linnen, en ontbijt ik met de knapperigste creusli. De zon begint op de komen, en de klokkensamenleving zou je graag laten geloven dat het nu vier uur is. Sommigen beweren dat melk niet goed voor je is. Anderen zeggen juist van wel. Ik zeg dat melk lekker is bij creusli.]]></description>
				<category domain="https://xmdr.nl/verhalen">verhalen</category>
				<link>https://xmdr.nl/read/routine</link>
				<guid isPermaLink="true">https://xmdr.nl/read/routine</guid>
			</item><item>
				<title>ActivityPub als interactielaag voor een blog?</title>
				<pubDate>Mon, 24 Jul 2023 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>ActivityPub als interactielaag voor een blog?</h1><p><a
href="https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines">(Betteridge’s
law of headlines)</a></p>
<p>Vroeger kon je comments plaatsen op dit blog, maar sinds ik de
backend heb herschreven naar een enkel PHP bestand van grofweg 300
regels, bestaan comments niet meer als visueel en interactief element.
Ze bestaan alleen nog in de database, als een soort digitaal
staartbotje. Dat is toch wel jammer.</p>
<p>Dat er behoefte is aan comments, blijkt uit meerdere dingen:</p>
<ol type="1">
<li>Ik wil ze gewoon want ze zijn leuk</li>
<li>Ik krijg soms een mailtje RE: een post</li>
</ol>
<p>Maar er waren meerdere punten waar ik tegen aanliep:</p>
<ol type="1">
<li>Ik ben ook naar een VPS overgestapt waar mail niet inbegrepen was.
Ik kon dus niet makkelijk notificaties sturen als er was gereageerd op
een comment.</li>
<li>Spam blijft een ding</li>
</ol>
<p>Nu is <a href="https://joinmastodon.org/">Mastodon</a> de laatste
tijd tamelijk hip, en ik heb al eens eerder geëxperimenteerd met het
onderliggende <a href="https://activitypub.rocks/">ActivityPub
protocol</a> (Wat ik vanaf nu ga afkorten als “AP”). Kan ik Mastodon
instances het vervelende werk van user authentication en comments laten
doen, en tegelijkertijd mijn blog volgbaar maken, en elke post naadloos
integreren op de feed van mijn volgers, die allemaal op hun eigen
willekeurige en anarchistische Mastodon instances herbergen?</p>
<p>Ofwel, in de vorm van een verlanglijst:</p>
<ol type="1">
<li>Posts delen met mensen die het boeit</li>
<li>Publieke reacties op de post, vindbaar onder de post zelf</li>
<li>Zo min mogelijk spam</li>
<li>Geen hoofdpijn</li>
</ol>
<p>Mijn eerste idee was om mijn blog als AP server op te zetten met 1
gebruiker (mijzelf) die dan posts maakt, en daar dan de benodigde AP
tierelantijntjes aan toe te voegen, d.w.z. een outbox en inbox voor die
enkele “Michael” gebruiker. Mensen op een ander AP platform zoals <a
href="https://mastodon.nl">mastodon.nl</a> zouden dan “<span
class="citation" data-cites="michael">@michael</span><span
class="citation" data-cites="xmdr.nl">@xmdr.nl</span>” kunnen volgen,
alle blogposts direct op hun eigen feed op mastodon.nl zien, en daarop
kunnen reageren. Die reacties krijg ik dan d.m.v. AP in de Michael inbox
en kan ik lokaal opslaan zodat ze ook op xmdr.nl te zien zijn. Simpel
genoeg, toch? Nee!</p>
<h2 id="activitypub-is-een-twitter">ActivityPub is een Twitter</h2>
<p>AP is ontworpen voor korte content. Een “post” wordt letterlijk een
“Note” genoemd. Je hebt wel “Article”, maar dat snapt Mastodon niet, die
snapt alleen Notes. Oké, dus mijn posts kunnen nooit naadloos op een Mastodon
feed komen, maar je kan nog steeds een “Note” maken met daarin een link
naar mijn Post, en daar zou je nog steeds op kunnen commenten. Het is
totaal niet elegant, maar het zou werken. Aan de andere kant, sommige
dingen verwacht ik ook nooit op een feed te kunnen vertonen: misschien
maak ik wel een hele fancy blogpost met allerlei javascript/canvas
interactieve speeltjes, zoals je tegenkomt op de site van <a
href="https://ciechanow.ski/">Bartosz Ciechanowski</a>. Dat gaat
natuurlijk gewoon nooit overkomen. Dus ach, misschien is het ook beter
de link naar- en niet de directe inhoud zelf met AP te publiceren.</p>
<h2 id="korte-omleiding-mailto-en-de-federatie-van-e-mail">Korte
omleiding: <code>mailto:</code> en de federatie van E-mail</h2>
<p>Als je op een <code>mailto:target_account@target_server</code> URL
klikt, dan opent er een dialoog om met <em>jouw</em> e-mail client
(outlook) een mail te sturen met <em>jouw</em> e-mailaccount
(bitchboy33@) vanaf <em>jouw</em> e-mailserver (hotmail.com) naar
<em>mijn</em> e-mail account (xmdr@) op <em>mijn</em> e-mailserver
(protonmail.com), die ik vervolgens kan lezen met <em>mijn</em>
e-mailclient (thunderbird). Hoera federatie!</p>
<h2 id="activitypub-heeft-dus-geen-follow">ActivityPub heeft dus geen
<code>follow:</code></h2>
<p>Zoiets bestaat er dus niet voor AP. Er is bijvoorbeeld geen
<code>followme:@drollikker@poep.nl</code> die ervoor zorgt dat
<em>jouw</em> AP account op <em>jouw</em> AP instance (bvb <span
class="citation" data-cites="bitchboy33">@bitchboy33</span><span
class="citation" data-cites="mastodon.nl">@mastodon.nl</span>)
<em>mijn</em> AP account op <em>mijn</em> AP instance (<span
class="citation" data-cites="drollikker">@drollikker</span><span
class="citation" data-cites="poep.nl">@poep.nl</span> dus) gaat volgen.
Die naadloze integratie kan gewoon niet, het is er (nog) niet. Als je
dus “lees mijn nieuwe blogpost” berichten op <em>jouw</em> feed wil
zien, moet je moeilijk handmatig dingen in gaan stellen. Dat werkt kut
en dus gaat niemand de moeite nemen. Er is te veel <em>frictie</em>
zoals UX designers het zouden zeggen.</p>
<p>Hier, ChatGPT legt het uit:</p>
<blockquote>
<p>Ik ben een mastodon.nl gebruiker. Hoe volg ik een Actor (gebruiker)
op een andere ActivityPub (niet mastodon) server?</p>
<p><cite>Michael D. Roeleveld</cite></p>
</blockquote>
<blockquote>
<p>Als een mastodon.nl gebruiker kun je een gebruiker op een andere
ActivityPub-server volgen door het volgende proces te volgen:</p>
<ol type="1">
<li>Zoek de gebruiker op de andere ActivityPub-server op wie je wilt
volgen. Deze gebruiker moet een profiel hebben op een server die
ActivityPub ondersteunt, zoals een andere Mastodon-instantie, Pleroma,
Pixelfed, PeerTube, etc.</li>
<li>Als je het profiel van de gebruiker hebt gevonden, kopieer dan de
URL van hun profiel. Meestal heeft het profiel van een gebruiker op een
ActivityPub-server een URL die er ongeveer zo uitziet:
https://example.com/<span class="citation"
data-cites="gebruikersnaam">@gebruikersnaam</span>.</li>
<li>Ga terug naar je mastodon.nl account en zoek naar de zoekbalk of het
veld waar je nieuwe gebruikers kunt toevoegen om te volgen. Dit kan op
verschillende plaatsen zijn, afhankelijk van de specifieke
Mastodon-client die je gebruikt, maar meestal kun je een zoekopdracht
uitvoeren door op het vergrootglaspictogram te klikken of door naar een
“Ontdek” of “Profielen” sectie te gaan.</li>
<li>Plak de URL van het profiel van de gebruiker op de andere
ActivityPub-server in het zoekveld op je mastodon.nl account.</li>
<li>Klik op de knop om de gebruiker te volgen. Dit kan verschillen per
Mastodon-client, maar meestal is het een knop met de tekst “Volgen” of
een soortgelijk label.</li>
</ol>
<p>Nadat je deze stappen hebt gevolgd, zou je nu de gebruiker van de
andere ActivityPub-server moeten volgen, en hun openbare berichten
zouden op je mastodon.nl tijdlijn moeten verschijnen.</p>
<p>Houd er rekening mee dat sommige ActivityPub-servers mogelijk geen
federatie (interactie met gebruikers op andere servers) toestaan, dus
het kan zijn dat je geen interactie kunt hebben met gebruikers op
servers die geen federatie toestaan.</p>
<p><cite>ChatGPT</cite></p>
</blockquote>
<p>Check die laatste alinea dan! Al dat werk en dan <em>nog</em> kan het
zijn dat het niet <em>echt</em> werkt. Sjongejonge wat een flop.</p>
<h2 id="activitypub-is-ingewikkeld-en-niet-universeel">ActivityPub is
ingewikkeld en niet universeel</h2>
<p>Elke “grote” social medium wat op AP gebouwd is lijkt weer een ander
dialect van AP erop na te houden. Het is allemaal niet universeel
compatibel met elkaar. Dit is vrij duidelijk een probleem. Daarnaast is
het lastig om überhaupt een prototype (ofwel Minimum Viable Product
(ofwel <em>simplest possible thing that works</em>)) te maken voor een
AP-platform. Ik ga echter niet verder op deze punten in omdat het
neerkomt op vrij technisch geneuzel wat niet algemeen toepasbaar noch
interessant is, en deze post alweer lang genoeg aan het worden is.</p>
<h2 id="conclusie">Conclusie</h2>
<p>Het principe is er: wat een AP integratie op dit moment zou bieden
zijn dus comments en ik-heb-iets-geschreven-notificaties (voor mensen
met b.v.b. een Mastodon account, voor de rest niet). Maar om dan dit
blog te volgen is vervelend veel werk, en ongeveer evenveel stappen als
een RSS reader instellen, maar dan zonder garantie dat het überhaupt
werkt. Het is dus voor de groep mensen die niet aan social media doet
ook irritant als je een comment wil maken, en dan vervolgens een account
moet maken op een hele andere site die je anders niet gaat gebruiken. Ik
voorzie ook veel debugging met APIs van externe partijen wat gewoon
altijd vervelend is, dat geeft mij veel hoofdpijn. Behalve comments kan
alles wat ik wil nu al met de RSS feed en het e-mailadres die ik
publiceer.</p>
<p>Mastodon en dergelijke zijn dus leuk als je op zoek bent naar een
twitter-vervanger om elke uwer scheet naar te uploaden (inclusief <a
href="https://mastodon.social/@davew/110753813670734170">oprechte
<em>shit takes</em> over AI</a>) en die van anderen stevig te inhaleren,
maar het is niet redelijkerwijs bruikbaar als werkelijk gefedereerd
lees-, interactie-, en publicatieplatform voor langere blogposts.</p>
]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/activitypub_interactie_blog</link>
				<guid isPermaLink="true">https://xmdr.nl/read/activitypub_interactie_blog</guid>
			</item><item>
				<title>Het treurige verhaal van de Monad</title>
				<pubDate>Wed, 19 Jul 2023 00:00:00 UTC</pubDate>
				<description><![CDATA[<h1>Het treurige verhaal van de Monad</h1><p>1989. Je bent een universitaire onderzoeker die een patroon heeft ontdekt
in een nieuwe programmeertaal, Huskshell. Het patroon gaat als
volgt:</p>
<pre><code>I = 0;
N = 10;
LOOP:
IF (I &lt; N) GOTO ENDLOOP;
PRINT(I);
I = I + 1;
GOTO LOOP;
ENDLOOP:</code></pre>
<p>Je tekent een diagram van dit softwarepatroon en herkent het direct
als een xnopyt, een zekere lineare reïficatie van de set van
xnopoïdische iteratiestructuren (een onderwerp binnen “fizzbuzz theory”
(een abstract wiskundig onderzoeksveld)) die voor de duidelijkheid niet
echt bestaan (ik heb ze verzonnen voor dit verhaal).</p>
<p>Direct is het een hit - binnen de hoogst academische niche van
onderzoekers van procedurele talen zoals Huskshell dan. Voor de rest
blijft het concept redelijk obscuur en de software wereld gaat zijn
eigen, andere richting in. Jaren later worden xnopyts weer ontdekt, en
iedereen maakt ze prompt tamelijk belachelijk.</p>
<p>Vanwege hun nauwe verbinding met hoog abstracte wiskunde hebben
xnpyts een zeker esoterisch aspect, verwarrende bagage waardoor
ontspoorde programmeurs ze <a href='https://blog.plover.com/prog/burritos.html'>vergelijken met burrito's</a> vanwege hun
rollende/herhalende kenmerk. Het ironische is dat overal in de software
industrie miljoenen xnopyts worden gebruikt op een dag, maar niemand die
het door heeft: de “for-loop”. Maar de syntactische limitaties (en
überhaupt de ongebruikelijke syntax) van Huskshell maken het onmogelijk
voor de gemiddelde software developer om deze gelijkenis te zien.</p>
<p>Dat is het verhaal van de Monad. Het is een software-patroon die je
terug ziet in moderne talen in optional types, error types, arrays en zo
nog wat dingen. In die talen komt het naar voren dat je een
<code>.map()</code> hebt op die objecten, of een <code>?</code>
operator, of iets dergelijks. Ze zijn in een zekere zin goed te
componeren. En dat is alles wat een monad is, het is gewoon een design
patroon met een hele dure naam. (zeker met die inflatie de laatste tijd, sjonge jonge)</p>
<p>Je kan pedantisch doen over <a
href="https://wiki.haskell.org/Monad_laws">de drie Monad Laws</a> die
Haskell definiëert, maar letterlijk genomen hebben die weinig waarde
buiten Haskell omdat Haskell zo’n extreem unieke taal is. Alles is er
uitgedrukt in termen van kettingen van geneste functies/closures, mede
omdat records/structs niet echt de stijl zijn van Haskell, en mede omdat
alle functies in Haskell maar 1 argument mogen nemen. Maar goed, ik zal
het proberen.</p>
<p>De 3 <i>monad laws</i> zijn:</p>
<ol type="1">
<li><code>return a &gt;&gt;= h</code> is hetzelfde als
<code>h a</code></li>
<li><code>m &gt;&gt;= return</code> is hetzelfde als <code>m</code></li>
<li><code>(m &gt;&gt;= g) &gt;&gt;= h</code> is hetzelfde als
<code>m &gt;&gt;= (\x -&gt; g x &gt;&gt;= h)</code></li>
</ol>
<p>Aaaa, leestekens!!</p>
<ul>
<li><code>return a</code> is Haskell voor <code>wrap(a)</code>.<br>In Rust
zou dat dan bijvoorbeeld <code>Some(3)</code> kunnen zijn met de
<code>Optional&lt;T&gt;</code> <em>monad,</em> maar in Haskell hoef je niet te zeggen wat voor monad je wrapt - <code>return</code> is een polymorphische functie. In de type signature staat al welke monad, dan hoeft het niet nog een keer in de implementatie herhaald te worden.</li>
<li><code>a &gt;&gt;= b</code> (bind) is Haskell voor <code>a.map(b)</code>.</li>
<li><code>\x bla</code> is Haskell voor <code>fn(x){a}</code>.</li>
<li><code>f x</code> is <code>f(x)</code>.</li>
<li><code>f</code>, <code>h</code>,
<code>g</code> zijn standaard placeholder namen voor functies,
<code>m</code> voor monad, en <code>a</code> voor een waarde en
<code>x</code> voor een functieargument.</li>
</ul>
<p>Wat er dus eigenlijk staat is:</p>
<ol type="1">
<li><code>wrap(a).map(h) == h(a)</code>: eigenlijk
<code>wrap(h(a))</code> in b.v.b. Rust, maar dat hoeft in Haskell niet omdat <code>&gt;&gt;=</code> niet alleen <code>map</code> is, maar
ook meteen een <em>unwrap.</em> Deze wet betekent dat eerst een calculatie en daarna een
wrap hetzelfde is als een map op de wrap.</li>
<li><code>m.map(wrap) == m</code>, oftewel een monad opnieuw inpakken
geeft een monad. Nogmaals steekt hoe Haskell werkt hier z’n kop boven
het water. Vandaar dat je in Haskell niet een dubbele
monad krijgt, terwijl je in Rust een
<code>Optional&lt;Optional&gt;</code> zou krijgen.</li>
<li><code>m.map(g).map(h) == m.map((x) -&gt; g(x).map(h))</code>,
oftewel een monad eerst met g en dan h mappen is hetzelfde als de monad
één keer mappen met een compositie van g en h.</li>
</ol>
<p>En dat is het gewoon. Zie het als een <code>interface</code>. Niet zo
heel lastig dus. Maar:</p>
<figure class='center'>
<img src="/src/media/monad/diagram.png"
title="Een monad is een monoid in de categorie van endofunctors"
alt="Diagram van monad transformaties" />
<figcaption aria-hidden="true">Diagram van monad
transformaties <a href='https://en.m.wikipedia.org/wiki/Monad_(category_theory)'>(Wikipedia)</a></figcaption>
</figure>
<p>Daarnaast zijn monaden dus redelijk abstract. Vragen wat “een” monad
is, is een beetje alsof je vraagt wat “een” observer pattern is. Het is
een abstract idee, een bepaalde manier om een klein geïsoleerd stukje
gedrag op een gestandaardiseerde manier in code vorm te geven, en daar
zijn duizenden verschillende (maar dus wel gelijkende) concrete
implementaties van te vinden.</p>
<p>En dat alles maakt monads zo ongelooflijk ontoegankelijk. Terwijl je
ze waarschijnlijk al duizenden keren hebt gebruikt zonder het door te
hebben.</p>
]]></description>
				<category domain="https://xmdr.nl/blog">blog</category>
				<link>https://xmdr.nl/read/monad</link>
				<guid isPermaLink="true">https://xmdr.nl/read/monad</guid>
			</item></channel>
</rss>