<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://datakinds.github.io//feed.xml" rel="self" type="application/atom+xml" /><link href="https://datakinds.github.io//" rel="alternate" type="text/html" /><updated>2026-04-01T22:29:13+00:00</updated><id>https://datakinds.github.io//feed.xml</id><title type="html">DK’S ABODE</title><subtitle>&quot;; DROP TABLE *;</subtitle><entry><title type="html">Some Random Stuff: A 2025 In Review</title><link href="https://datakinds.github.io//2025/12/31/some-random-stuff-a-2025-in-review" rel="alternate" type="text/html" title="Some Random Stuff: A 2025 In Review" /><published>2025-12-31T21:12:00+00:00</published><updated>2025-12-31T21:12:00+00:00</updated><id>https://datakinds.github.io//2025/12/31/some-random-stuff-a-2025-in-review</id><content type="html" xml:base="https://datakinds.github.io//2025/12/31/some-random-stuff-a-2025-in-review"><![CDATA[<p>Hi gang. I haven’t been writing in a while. And that’s okay! I’ve been reading. And stepping away from the computer. Sometimes even crafting. Buying a house, and building stuff for the house.</p>

<p>This post is going to be the quickest whirlwind of everything that comes to mind that I’m excited about this year. It won’t be in chronological order, and it might not even all be 2025~ I’ve been spending a lot of</p>

<h2 id="new-jobs">New jobs!</h2>

<p>First half of the year, I was writing Golang for $BIG_FINANCIAL_COMPANY. Second half of the year, I’m writing Golang for $BIGGER_FINANCIAL_COMPANY. Have you <em>ever</em> seen me write Golang? No… sometimes you find the niche, sometimes the niche finds you.</p>

<h2 id="new-servers-new-hardware">New servers! New hardware!</h2>

<p>I’m building a data storage rack. I’ve currently got:</p>

<ul>
  <li>the rack itself</li>
  <li>a power strip</li>
  <li>two Pi 4’s</li>
  <li>a shoebox as their containment unit</li>
  <li>a fancy network switch</li>
  <li>and two DVD drives for, you know, ripping old shovelware that’s run out its copyright. And other activities of that nature.</li>
</ul>

<p>Please please please – if you know of a large 6-10 drive 12in depth 2-3u cost effective drive bay please let me know! It’s fine if I get ‘em used, I just hardly know where to even look to buy something like this. I used Synology at an old job, but the company’s went down the shitter and they lock their new systems down to only use their drives…</p>

<h2 id="new-languages">New languages!</h2>

<p>I’ve been working on a LOT of language projects this year. Esolanging has yet again captured my imagination, binding it with a knotty fervor unrivaled by the best sailors. My mind drips sweat. It pants and begs to be let—- wait, where was I? Esolanging, right. I got carried away.</p>

<p>Most of this is imported from Discord. It’s much easier for me to pop in there, wax poetic about some fake language for five minutes, and then disappear back into the ether. I’ve spent a lot of time in the <a href="https://discord.gg/9qdcu4X8Be">concatenative discord server</a> lately. Join there and join the server in my blog footer, then finish reading.</p>

<h3 id="rosin">Rosin</h3>

<p>Hey, this rings a bell! I started <a href="2024-12-02-2024-december-adventure-log.md">this during last year’s december adventure</a>. Which also happens to be my last blog post. Oh no, has this blog become a once-a-year venture? I hope not! Writing is invigorating. It just so happens to also be tiring.</p>

<p>Anyway, Rosin has come incredibly far. <a href="https://github.com/DataKinds/tree-rewriter/blob/main/EXECUTION_MODEL.md">There is a formal spec</a>, there’s a <a href="https://github.com/DataKinds/tree-rewriter/blob/main/test/Spec.hs">pretty large test suite</a>, eager variables are actually useful now in that they make the interpreter run until fixpoint on a particular tree. There’s a README that is like 6 months out of date (oops! hehe), I’ve covered most of the features I’m interested in covering.</p>

<p>The compiler’s gone through a bunch of iterative improvements that have drastically improved the codebase. Big ups to <code class="language-plaintext highlighter-rouge">@microchipsndip</code> on Discord and <a href="https://evincar.com/">evincar</a> for the suggestions. I would have never learned how I’m actually supposed to use <code class="language-plaintext highlighter-rouge">mtl</code> without these fellas.</p>

<p>A few functions I’m extra proud of:</p>

<ul>
  <li>The core pattern matching function: <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Core.hs#L237-L276">https://github.com/DataKinds/tree-rewriter/blob/main/src/Core.hs#L237-L276</a>. I daresay this function almost reads like pseudocode.</li>
  <li>The runtime stepping function: <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Runtime.hs#L243-L287">https://github.com/DataKinds/tree-rewriter/blob/main/src/Runtime.hs#L243-L287</a>. Finally reaping that sweet, sweet monadic DSL glory.</li>
</ul>

<p>Next is support for subinterpreters and parallelism (think <code class="language-plaintext highlighter-rouge">Ruby::Box</code>). Maybe also finally using my Trie implementation to match patterns.</p>

<p>Also, I named this language after an OC. She/they’re a demon-woman-changeling with black hair and gray skin that can take the form of a swarm of white-fur-red-eyed mice. Oy vey!</p>

<h3 id="objective-thue-thue-thue-plus-parallel-thue">Objective Thue (Thue++? Thue Plus? Parallel Thue?)</h3>

<p>Here’s another language idea I’ve been excited about: a version of Thue where you can pass messages back and forth between different rewriting engines that contain their own internal state. I’ve been playing around with the syntax and semantics, here’s some snippets ripped from discord messages:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">invoker</span> <span class="p">{</span>
    <span class="k">default</span> <span class="n">a</span>            <span class="c1">// If this scope is invoked without an input, what state do we default to?</span>
    <span class="n">strategy</span> <span class="n">pick</span><span class="o">-</span><span class="n">first</span>  <span class="c1">// Apply the very first matching rule as close to the start of the string as possible</span>
    <span class="n">b</span><span class="o">+</span> <span class="o">:~</span> <span class="err">$</span>              <span class="c1">// This rule (using :~) passes the RHS as a return value of the `invoker`, deleting (this is slow... what do?) the LHS from the internal state.</span>
    <span class="n">a</span> <span class="o">:=</span> <span class="n">ab</span>              <span class="c1">// This rule (using :=) matches the LHS and substitutes the RHS into the internal state of the `invoker`</span>
<span class="p">}</span>

<span class="n">counter</span> <span class="p">{</span>               <span class="c1">// Counts the number of a's it's been passed</span>
    <span class="n">strategy</span> <span class="n">pick</span><span class="o">-</span><span class="n">first</span>
    <span class="n">a</span> <span class="o">:=</span> <span class="n">I</span>
    <span class="p">.</span> <span class="o">:=</span>                <span class="c1">// VERY VERY VERY basic regex, anything I can easily compile to a fast FSM. </span>
                        <span class="c1">// In this case: match any character that's not `a` and delete it from the internal state.</span>
    <span class="n">I</span><span class="o">+</span> <span class="o">:::=</span> <span class="n">got</span> <span class="err">$</span> <span class="n">As</span>    <span class="c1">// LHS (I+) is another abuse of basic regex. :::= gives a rule that writes to stdout (similar to Thue proper). Deletes the LHS from the internal state.</span>
                        <span class="c1">// $ gives the entire match on the LHS. Think regex substitution syntax: $0, $1, $2, ...</span>
<span class="p">}</span>

<span class="n">invoker2</span> <span class="o">=</span> <span class="n">new</span> <span class="n">invoker</span> <span class="s">"aaa"</span> <span class="c1">// `new` is its own scoped object, just provided automatically by the compiler</span>
<span class="n">counter2</span> <span class="o">=</span> <span class="n">new</span> <span class="n">counter</span>
<span class="n">counter2</span> <span class="n">invoker2</span> 

<span class="c1">// Prints "got III As", "got IIII As", "got IIIII As", "got IIIIII As", ...d</span>
</code></pre></div></div>

<blockquote>
  <p>There are so few knobs to turn here that actually make sense:</p>
  <ul>
    <li>Where does the rewrite head start</li>
    <li>What does the state default to</li>
    <li>How does the rewrite head move when it fails a match</li>
    <li>How does the rewrite head move when it matches</li>
    <li>How does the rewriter decide which of many matching rules to select</li>
  </ul>
</blockquote>

<p>And a tiny Thue implementation in Raku:</p>

<pre><code class="language-raku">#!/usr/bin/env raku

grammar Thue {
    token TOP { 
        :my $*PAST-DIVIDER = False;
        \v* [&lt;line&gt; \v*]+ 
    }
    token line {
    || &lt;?{ $*PAST-DIVIDER }&gt; &lt;slurp&gt;
    || &lt;divider&gt;
    || &lt;rule&gt;
    }
    token slurp { (.+) $ }
    token divider { 
        ^^ '---' $$ 
        {$*PAST-DIVIDER = True}
    }
    regex rule { ^^ (.*?) (&lt;becomes&gt;) (\V*) $$ }
    proto token becomes {*}
          token becomes:sym&lt;regular&gt; { ':=' }
          token becomes:sym&lt;output&gt; { ':~' }
}

class RunThue {
    has %.rules;
    method slurp ($/) { 
        my @lhses = %!rules.keys.pick: *;
        my $did = False;
        my $state = $/;
        for @lhses -&gt; $lhs {
            $state.=subst($lhs, { $did = True; %!rules{$_} });
        }
        say $state;
        samewith(self, $state) if $did 
    }
    method rule ($/) { 
        say "got rule!! ", ($0 =&gt; $2);
        %!rules{$0} = $2; 
    }
}

Thue.parse: '
1_:=1++
0_:=1

01++:=10
11++:=1++0

_0:=_
_1++:=10

---

_1111111111_', actions =&gt; RunThue.new
</code></pre>

<h3 id="some-apl-with-lazy-function-application-adts-and-a-query-based-compiler">Some APL with lazy function application, ADTs, and a query-based compiler</h3>

<p>As always, my title does the language perfect justice &gt;:)</p>

<p>I’ve written way more about this in the readme for the language, you ought to just defer to that: <a href="https://github.com/DataKinds/apl">https://github.com/DataKinds/apl</a>.</p>

<h3 id="drill">DRILL</h3>

<p>It’s a subset of <a href="http://sam.cat-v.org/">Sam</a> that I wrote in 2 days for a silly Discord game. The code can be found <a href="https://github.com/DataKinds/drill">here</a>, and I often come back and stare at it because it’s the best C99 I wrote this year.</p>

<h3 id="a-forth-with-blackjack-hookers-and-row-types">A Forth, with blackjack, hookers, and row types</h3>

<p>You wanted me to write something here? I never promised that. Here’s a discord screenshot: <img src="/assets/imgs/2025-12-31-05-36-00.png" alt="Big ol discord screenshot that essentially amounts to me reinventing row types from first principals" /></p>

<p>I’m sorry to those who use screen readers, I am trading speed of writing this article for accessibility here. I gotta run to a NYE party after I post this, and I don’t even look cute yet. It’s kind of messed up if you really think about it.</p>

<p>Here are the two links from the screenshot. Good stuff!</p>

<ul>
  <li><a href="https://dl.acm.org/doi/pdf/10.1145/3290325">https://dl.acm.org/doi/pdf/10.1145/3290325</a></li>
  <li><a href="https://thunderseethe.dev/series/making-a-language/">https://thunderseethe.dev/series/making-a-language/</a></li>
</ul>

<h3 id="game-jams">Game Jams</h3>

<p>I signed up for 3, produced 0 games. I need to set my sights lower next time. Bummer!</p>

<h2 id="other-projects">Other projects!</h2>

<h3 id="lil-weirdo">Lil’ Weirdo</h3>

<p>Crap, everyone’s doing AI! <strong>Good thing I did it first.</strong> Predating character.AI, Sora, Meta’s fake girlfriends, or that one other discord bot that got banned for endagering kids, I present <a href="https://github.com/DataKinds/lilweirdo">Lil Weirdo</a>.</p>

<p>Lil Weirdo will keep a configurable message log in its special little brain, and it’ll use that message log in order to either insult or aggresively try to flirt with other users. Because it also remembers the messages that it previously sent, it usually ends up going all tsundere on you.</p>

<p>It can do a handful of other things too, like generate random video game achievements or random crafting recipies. I think I honed in the prompts pretty well, check them out here: <a href="https://github.com/DataKinds/lilweirdo/blob/main/src/templater.py#L81-L508">https://github.com/DataKinds/lilweirdo/blob/main/src/templater.py#L81-L508</a>.</p>

<p>It’s also set up to only use locally run LLMs through Ollama (which I think is adware now, so I gotta find something else).</p>

<h3 id="media-ive-consumed">Media I’ve consumed!</h3>

<p>Over the last few months I have felt my attention span return to me like a corpse-carrying eagle descending from the sky. It splays out its bounty – a ton of paperbacks.</p>

<p>I’ve been super into horror and sci-fi. Bird Box by Josh Malerman was great. Didn’t breathe the whole time. Three Body Problem by Cixin Liu was killer and I’m reading the second one now. I re-read There Is No Antimemetics Department by qntm.</p>

<p>I’m also gaming. Deadlock has been a godsend this year – finally, a good MOBA that isn’t League of Legends or DOTA 2! I love how they blended the chaos of hero shooters with the chess-ness of MOBAs. This year was another great one for roguelikes: Megabonk, Ball X Pit, and Risk of Rain 2 all had their moments.</p>

<h1 id="thank-you">Thank you!</h1>

<p>Thank you to all who read this site, thank you to the friends I’ve made on Discord, and thank the stars that we are afforded another beautiful year. Life would not be as rich without any of this fun stuff to talk about. See ya 2025, happy 2026!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Hi gang. I haven’t been writing in a while. And that’s okay! I’ve been reading. And stepping away from the computer. Sometimes even crafting. Buying a house, and building stuff for the house.]]></summary></entry><entry><title type="html">2024 December Adventure Log</title><link href="https://datakinds.github.io//2024/12/02/2024-december-adventure-log" rel="alternate" type="text/html" title="2024 December Adventure Log" /><published>2024-12-02T16:28:00+00:00</published><updated>2024-12-02T16:28:00+00:00</updated><id>https://datakinds.github.io//2024/12/02/2024-december-adventure-log</id><content type="html" xml:base="https://datakinds.github.io//2024/12/02/2024-december-adventure-log"><![CDATA[<p>Hello all! I will be participating in the 2024 <a href="https://eli.li/december-adventure">December Adventure</a>, and this is my captain’s log for the month. From Eli’s page:</p>

<blockquote>
  <p>The <a href="https://adventofcode.com/">Advent of Code</a> is cool, but a lot, and not everyone’s jam.
The December Adventure is low key. The goal is to write a little bit of code every day in December.</p>
  <h2 id="how">How?</h2>
  <p>Pick a project, or projects and work on them a little bit every day in December.</p>
</blockquote>

<h1 id="day-2-12224">Day 2 (12/2/24)</h1>

<p>Got this page set up and did a little brainstorming for what I want to work on this month. I know that there are two things off the top of my head I wanted to hammer out:</p>

<ul>
  <li>I wanted to get an 88x31 badge set up for my website, along with a little area to display others’ 88x31s. <a href="https://zptr.cc/">yui</a> asked me last month and I still have not bothered…</li>
  <li>I want to work more on <a href="https://github.com/DataKinds/tree-rewriter">Rosin</a> – specifically, fleshing out the pattern variables to allow any variable to specify its eagerness, and adding syntax for rewriting a global state on top of the s-expr rewrite pointer. Something like</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(; $comment) ~&gt; 

(; (This rewrite has no state attached))
hello ~&gt; world

(; (This rewrite only runs if there's a `ready` in the global bag))
hello |ready|~|done| world

(; (So we need new syntax for "lambda rules", rules which only run once. We use a backslash in place of the tilde.))
(; (Here's a "lambda rule" which adds a `ready` to the bag. The following rules are equivalent.))
\|ready|
||\|ready|
</code></pre></div></div>

<p>DAY 2 UPDATE 1: I ended up finishing a pretty good majority of the refactor that was required to flesh out pattern variables in Rosin! Check out the big ol’ git diff <a href="https://github.com/DataKinds/tree-rewriter/pull/1/files">here</a>.</p>

<h1 id="day-3-1232024">Day 3 (12/3/2024)</h1>

<p>I may or may not have stayed up past my bedtime working on Rosin… I’m passing all my regression tests on the new pattern variable branch!! Now we can swap eagerness on and off on all variables, even special accumulators. Tomorrow (today, after a sleep) I should write some more regression tests, for the pack/unpack accumulators and for the universal eagerness switch.</p>

<p>On the left, some of my regression tests. On the right, my passing test dashboard!</p>

<p><img src="/assets/imgs/december-adventure/2-regressionsuite.png" alt="A snippet of my regression suite" width="400" /> <img src="/assets/imgs/december-adventure/2-regressionpass.png" alt="My passing regression tests!" width="700" /></p>

<p>UPDATE 1: it’s 10:18pm, I just finished up a minor redesign of my site. Hopefully this color scheme is easier to read, and the header is no longer the size of the screen + it’s no longer begging for your attention. The mobile version of the site was also improved, so it should no longer be impossibly narrow on narrow screens. Let me know how it works!!</p>

<p>Update 2: Merged in my pattern variable refactor in Rosin and that simplified things nicely. I also <a href="https://github.com/DataKinds/tree-rewriter/tree/main/sample/regression">split apart the regression tests to make them stop sharing state</a> and updated the readme to reflect the new syntax.</p>

<h1 id="day-4">Day 4</h1>

<p>You know, I really don’t have to put the dates on the headings of each of these entries.</p>

<p>Today I worked a bit more on souping up the tests in Rosin. I made tests be <a href="https://github.com/DataKinds/tree-rewriter/commit/7a8da5a748d455e11f78e04ef16417323614cfdf#diff-0a88c502e6b2fded6c22871f555f64a0da4a9fc0213e4b7b067f15f51f2780be">printed to standard out</a> instead of requiring the compiler to output the final state. I also separated all the <a href="https://github.com/DataKinds/tree-rewriter/tree/main/sample/regression">test files into multiple different files</a> so they run in their own contexts.</p>

<p>All of this makes running tests quite nice. The Makefile lets me run specific sets of tests too.</p>

<p><img src="/assets/imgs/december-adventure/4-tests1.png" alt="Makefile running just one set of tests" />
<img src="/assets/imgs/december-adventure/4-tests2.png" alt="Makefile running all my tests" /></p>

<p>I also fixed some (but not all) bugs with the accumulators I uncovered while writing extra tests. For example, the Pack and Unpack accumulators don’t work quite how I want them to. They don’t quite walk the s-expression fully and it’s quite buggy.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((pack ?@) ~&gt; ?@)
(pack (this (is a (strangely) nested) list)) 
 --- became --- 
(this is a) 
</code></pre></div></div>

<p>I’ll have to deal with that soon. For something I fixed… turns out if you matched multiple values against the negate accumulator, they’d all come back backwards.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((negate ?- ?- ?-) ~&gt; ?-)
(negate 1 2 3) 
 --- became ---
(-3 -2 -1)
</code></pre></div></div>

<p>That’s fixed now. Progress!</p>

<h1 id="day-5">Day 5</h1>

<p>Didn’t do a lot of coding today. Between moving, work, and having a house guest over tonight I simply wasn’t sitting at the computer for very long.</p>

<p>I got about an hour to myself. I started thinking a bit about setting up a runtime state abstraction in Rosin. This will let me add a multiset bag, support one time lambda rules, and set up a better more explicit ordering for how rules are ingested and then applied.</p>

<p>My changes are still very preliminary, <a href="https://github.com/DataKinds/tree-rewriter/pull/2/files">I posted them up onto a separate branch</a>.</p>

<p>For background entertainment, I was watching this today:</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/GlPREQ55kzc?si=_2kTb6gSJyVrsogX" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<h1 id="day-9">Day 9</h1>

<p>Whew. I am sorry for the lack of updates, but the last couple of days flew by. On the 6th and the 7th, my partner and I were going back and forth to the house we’re moving into. On the 7th and 8th our power was out. To be more specific: the power was out in exactly half of our unit – and my computer’s outlet was unfortunately affected. All the while I have work and other shenanigans that get in the way. Le sigh… we cringe on…</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/NmACjr4mAIY?si=DdYx7fkfBZh3H7mf" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>I worked on Rosin a little bit tonight. I fixed a bug in the new <a href="https://wiki.haskell.org/Zipper">tree zipper</a> datatype <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Zipper.hs#L81-L90">where moving the zipper down to the first child</a> would <a href="https://github.com/DataKinds/tree-rewriter/commit/9aebbc03a45af3b47bc615feac96be6e9368a0ca#diff-37b5a1b9dce90ec661a8a9b74adc0b64c58e8c2c62b9717cbcacefff2b45074eL51">reverse the right hand side of the context</a>. I suppose it made sense at the time but hindsight is 50-50 and I cursed my past self. So the zipper works correctly now which is pretty cool. It worked well enough <a href="https://github.com/DataKinds/tree-rewriter/commit/5285ea99ed5b5acc8dbd584e12eef09563c3d0f0">so I merged the zipper into main :3</a></p>

<p>I have been listening to a lot of Remi Wolf.</p>

<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/1Wi1XpdZzGVIdRTzlTrIEF?utm_source=generator" width="100%" height="152" frameborder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>

<h1 id="day-10">Day 10</h1>

<p>Rosin progresses little by little. You can now define single use rules with <code class="language-plaintext highlighter-rouge">~</code>, like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rosin -p
(/evil/ ~ "$&lt;good$&gt;") "the evil wizard" "the evil tower"


+-----------------+
| Final transform |
+-----------------+
"the good wizard"
"the evil tower"
</code></pre></div></div>

<p>So that will definitely allow for some fun stuff.</p>

<p>I want to start writing more interesting programs in Rosin, but I keep going down development rabbitholes. The final rabbithole is nearing its conclusion: I have hashed out how I want multiset state rules to look. You will be able to interact with the multiset bag on the rewrite head with the <code class="language-plaintext highlighter-rouge">(:needs | :gives)</code> pattern. The same function that matched <code class="language-plaintext highlighter-rouge">~&gt;</code> rules WILL SOON know how to match all variations of these <code class="language-plaintext highlighter-rouge">|</code> rules… <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Runtime.hs#L105">but it’s not quite there yet</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ rosin-can't-do-this-quite-yet -p

(|(apple pear orange peach)) puts an apple, pear, orange, and peach into the bag
(apple |) takes an apple out of the bag
(apple |) doesn't match a second time 

+-----------------+
| Final transform |
+-----------------+
puts an apple, pear, orange, and peach into the bag
takes an apple out of the bag
(apple |) doesn't match a second time
</code></pre></div></div>

<p>Both sides do not need to be specified. <code class="language-plaintext highlighter-rouge">|</code> indicates a multiset matching rule that will only run once, and <code class="language-plaintext highlighter-rouge">|&gt;</code> rules will run ad infinitum.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>( (pear apple grape peach) |&gt; ((fruit salad)) )
now the combination of pear, apple, grape, and peach will always become (fruit salad)
</code></pre></div></div>

<p>Wait, I heard you say: <code class="language-plaintext highlighter-rouge">(fruit salad)</code>? Like, you put an S-expression into the multiset? Heck yeah I did! <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Runtime.hs#L45">The full range of Rosin values can go into the multiset</a> and this means we’ll be able to hijack the pattern variable machinery to do some very cute things:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>( (sword (stone :name)) | ((sword :name)) )

( | (stone "King's stone"))
( | sword)
will produce one thing in the bag: (sword "King's stone")
a sword's strength is derived from its legendary name
</code></pre></div></div>

<p>Last but not least – you’ll be able to devise a rule that only matches the S-expression tree when something is in the multiset bag. Or a tree rewrite rule that adds something into the bag. The <code class="language-plaintext highlighter-rouge">&amp;</code> symbol will be able to combine a multiset rewriting rule with a tree rewriting rule, such that the combined rule only matches when both conditions are satisfied. This has some crazy potential imo, and I just nailed down the syntax tonight:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(| n &amp; hello ~&gt; world) 
produces an n to the bag every time it matches `hello`

(n n | m &amp; (; :comment) ~&gt;) 
limits the amount of comments you can write to half the `hello`s you write... better be polite!
</code></pre></div></div>

<p>Some of this is implemented, some of it isn’t… but I feel good having this all theorycrafted and the way forward from here seems clear to me.</p>

<h1 id="day-11">Day 11</h1>

<p>Only posting a quick update today because I want to head to sleep early!!</p>

<p>A few new Rosin updates for ya….</p>

<p>I added a new regression test for the multiset state:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(Test a "Plain terms can be parsed and consumed"
    Given (
        ((pomme pomegranate) |&gt; weird-fruit-salad)
        (|pomme)
        (|pomegranate)
        (|(pomme pomegranate))
        (weird-fruit-salad|)
        (weird-fruit-salad |)
    )
    We expect ())
</code></pre></div></div>

<p>I finished setting up <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Runtime.hs#L135">the consumption of rules with multiset action</a>, see <code class="language-plaintext highlighter-rouge">recognizeDef</code>. So Rosin will now really recognize and consume all these forms of rules:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(pattern ~&gt; template) a tree rewriting rule
(pattern ~ template) a one time tree rewriting rule
((things i want) |&gt; (things i will get)) a multiset rewriting rule
((things i want) | (things i will get)) a one time multiset rewriting rule
(want | get &amp; pattern ~ template) a one time combined tree+multiset rewriting rule
(want | get &amp; pattern ~&gt; template) a combined tree+multiset rewriting rule
</code></pre></div></div>

<p>Note that the shortened forms from the regression test (like <code class="language-plaintext highlighter-rouge">(weird-fruit-salad |)</code>) won’t currently work.</p>

<p>Very soon Rosin will be able to apply tree rewrite rules conditionally based on the state of the multiset, <a href="https://github.com/DataKinds/tree-rewriter/blob/main/src/Runtime.hs#L180">but for now I left a hole in the function</a> where it will be filled with dedicated <code class="language-plaintext highlighter-rouge">Multiset</code> methods. Soon. I’m so close to having this working.</p>

<div class="language-hs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Filters a list of defs down to only those which are satisfied by the current state of the multiset</span>
<span class="n">filterByMultiset</span> <span class="o">::</span> <span class="kt">Monad</span> <span class="n">m</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="kt">EatenDef</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">RuntimeTV</span> <span class="n">m</span> <span class="p">[</span><span class="kt">EatenDef</span><span class="p">]</span>
<span class="n">filterByMultiset</span> <span class="n">defs</span> <span class="o">=</span> <span class="kr">do</span>
    <span class="n">pocket</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="n">runtimeMultiset</span> 
    <span class="n">pure</span> <span class="o">$</span> <span class="n">filter</span> <span class="p">(</span><span class="n">ok</span> <span class="n">pocket</span><span class="p">)</span> <span class="n">defs</span>
    <span class="kr">where</span> <span class="n">ok</span> <span class="n">ms</span> <span class="o">=</span> <span class="n">undefined</span>
</code></pre></div></div>

<p>For anyone still reading, thanks for sticking with me on this journey. I have really been looking forward to writing these entries every night.</p>

<h1 id="day-15">Day 15</h1>

<p>I spent the day today on the phone with banks and electricians making preparations for the new house. A few days ago my partner and I hosted a packing party. All of our friends came over, ate pizza, and put our belongings into boxes. Progress on my blog or projects has taken a bit of a backseat to moving.</p>

<p>All that being said, I worked on Rosin a bit more tonight!</p>

<p>There is a full fledged <code class="language-plaintext highlighter-rouge">Multiset</code> type now. All I’ve got implemented is reading things out of the multiset but putting things in will come soon enough.</p>

<div class="language-hs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">module</span> <span class="nn">Multiset</span> <span class="kr">where</span>
<span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.Map</span> <span class="k">as</span> <span class="n">M</span>
<span class="kr">import</span> <span class="nn">Control.Monad</span> <span class="p">(</span><span class="nf">foldM</span><span class="p">)</span>


<span class="kr">newtype</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">=</span> <span class="kt">Multiset</span> <span class="p">{</span> <span class="n">unmultiset</span> <span class="o">::</span> <span class="kt">M</span><span class="o">.</span><span class="kt">Map</span> <span class="n">a</span> <span class="kt">Int</span> <span class="p">}</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Show</span><span class="p">)</span>

<span class="n">fromList</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">[(</span><span class="n">a</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)]</span> <span class="o">-&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span>
<span class="n">fromList</span> <span class="o">=</span> <span class="kt">Multiset</span> <span class="o">.</span> <span class="kt">M</span><span class="o">.</span><span class="n">fromList</span>

<span class="n">toList</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="p">[(</span><span class="n">a</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)]</span> 
<span class="n">toList</span> <span class="o">=</span> <span class="kt">M</span><span class="o">.</span><span class="n">toList</span> <span class="o">.</span> <span class="n">unmultiset</span>

<span class="n">empty</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span>
<span class="n">empty</span> <span class="o">=</span> <span class="kt">Multiset</span> <span class="kt">M</span><span class="o">.</span><span class="n">empty</span>

<span class="n">null</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="n">null</span> <span class="o">=</span> <span class="kt">M</span><span class="o">.</span><span class="n">null</span> <span class="o">.</span> <span class="n">unmultiset</span>

<span class="c1">-- Is this element inside the multiset in a sufficient quantity? </span>
<span class="n">inside</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="n">inside</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">=</span> <span class="n">maybe</span> <span class="kt">False</span> <span class="p">(</span><span class="n">n</span> <span class="o">&lt;=</span><span class="p">)</span> <span class="o">.</span> <span class="kt">M</span><span class="o">.</span><span class="n">lookup</span> <span class="n">x</span> <span class="o">.</span> <span class="n">unmultiset</span> 

<span class="c1">-- Are these elements inside the multiset in a sufficient quantity? </span>
<span class="n">allInside</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="n">allInside</span> <span class="n">xns</span> <span class="n">ms</span> <span class="o">=</span> <span class="n">all</span> <span class="p">(`</span><span class="n">inside</span><span class="p">`</span> <span class="n">ms</span><span class="p">)</span> <span class="p">(</span><span class="n">toList</span> <span class="n">xns</span><span class="p">)</span>

<span class="c1">-- Take out this many copies of this element from our multiset, if we can!</span>
<span class="n">grab</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Maybe</span> <span class="p">(</span><span class="kt">Multiset</span> <span class="n">a</span><span class="p">)</span>
<span class="n">grab</span> <span class="n">xn</span><span class="o">@</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">n</span><span class="p">)</span> <span class="n">ms</span> <span class="o">=</span> <span class="kr">if</span> <span class="n">inside</span> <span class="n">xn</span> <span class="n">ms</span> <span class="kr">then</span> <span class="kt">Just</span> <span class="o">.</span> <span class="kt">Multiset</span> <span class="o">.</span> <span class="kt">M</span><span class="o">.</span><span class="n">adjust</span> <span class="p">(</span><span class="n">subtract</span> <span class="n">n</span><span class="p">)</span> <span class="n">x</span> <span class="o">.</span> <span class="n">unmultiset</span> <span class="o">$</span> <span class="n">ms</span> <span class="kr">else</span> <span class="kt">Nothing</span>

<span class="c1">-- Take out this many copies of these elements from our multiset, if we can!</span>
<span class="n">grabMany</span> <span class="o">::</span> <span class="kt">Ord</span> <span class="n">a</span> <span class="o">=&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Multiset</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="kt">Maybe</span> <span class="p">(</span><span class="kt">Multiset</span> <span class="n">a</span><span class="p">)</span>
<span class="n">grabMany</span> <span class="n">xns</span> <span class="n">ms</span> <span class="o">=</span> <span class="kt">Multiset</span> <span class="o">&lt;$&gt;</span> <span class="n">foldM</span> <span class="p">(</span><span class="nf">\</span><span class="n">m</span> <span class="n">xn</span> <span class="o">-&gt;</span> <span class="n">unmultiset</span> <span class="o">&lt;$&gt;</span> <span class="n">grab</span> <span class="n">xn</span> <span class="p">(</span><span class="kt">Multiset</span> <span class="n">m</span><span class="p">))</span> <span class="n">ms'</span> <span class="n">xns'</span>
    <span class="kr">where</span> <span class="n">ms'</span> <span class="o">=</span> <span class="n">unmultiset</span> <span class="n">ms</span><span class="p">;</span> <span class="n">xns'</span> <span class="o">=</span> <span class="n">toList</span> <span class="n">xns</span>
</code></pre></div></div>

<p>The Rosin runtime now checks that the multiset is satisfactory for any given function before using it to rewrite. Check out that sick ass runtime:</p>

<div class="language-hs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Filters a list of defs down to only those which are satisfied by the current state of the multiset</span>
<span class="n">filterByMultiset</span> <span class="o">::</span> <span class="kt">Monad</span> <span class="n">m</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="kt">EatenDef</span><span class="p">]</span> <span class="o">-&gt;</span> <span class="kt">RuntimeTV</span> <span class="n">m</span> <span class="p">[</span><span class="kt">EatenDef</span><span class="p">]</span>
<span class="n">filterByMultiset</span> <span class="n">defs</span> <span class="o">=</span> <span class="kr">do</span>
    <span class="n">pocket</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="n">runtimeMultiset</span> 
    <span class="n">pure</span> <span class="o">$</span> <span class="n">filter</span> <span class="p">(</span><span class="n">ok</span> <span class="n">pocket</span><span class="p">)</span> <span class="n">defs</span>
    <span class="kr">where</span> 
        <span class="n">ok</span> <span class="o">::</span> <span class="kt">MS</span><span class="o">.</span><span class="kt">Multiset</span> <span class="p">(</span><span class="kt">Tree</span> <span class="kt">RValue</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">EatenDef</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
        <span class="n">ok</span> <span class="n">ms</span> <span class="n">def</span> <span class="o">=</span> <span class="kt">MS</span><span class="o">.</span><span class="n">allInside</span> <span class="p">(</span><span class="n">defWantsFromBag</span> <span class="n">def</span><span class="p">)</span> <span class="n">ms</span>

<span class="c1">-- Carry out one step of Rosin's execution. This essentially carries out the following:</span>
<span class="c1">--   1) We check for a definition at the current rewrite head and ingest it if there's one there</span>
<span class="c1">--   2) We try to apply our rewrite rules at the current rewrite head</span>
<span class="c1">--   3) We move onto the next element in the tree in DFS order. If we're at the end, we loop back to the start</span>
<span class="c1">-- Gives back (the amount of rules applied, whether we jumped back to the top of the tree). </span>
<span class="n">runStep</span> <span class="o">::</span> <span class="kt">RuntimeTV</span> <span class="kt">IO</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">Bool</span><span class="p">)</span>
<span class="n">runStep</span> <span class="o">=</span> <span class="kr">do</span>
    <span class="n">verbose</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="n">runtimeVerbose</span>
    <span class="c1">-- Begin 1</span>
    <span class="n">eatDef</span> 
    <span class="c1">-- Begin 2</span>
    <span class="c1">-- Apply single use tree rewriting rules</span>
    <span class="c1">-- !!! ===== We check the multiset here!!! Take a look....  ===== !!! --</span>
    <span class="n">defs</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="n">runtimeSingleUseRules</span> <span class="o">&gt;&gt;=</span> <span class="n">filterByMultiset</span>
    <span class="n">maybeTreeRewrite</span> <span class="o">&lt;-</span> <span class="n">applyTreeDefs</span> <span class="n">defs</span>
    <span class="n">when</span> <span class="p">(</span><span class="n">isJust</span> <span class="n">maybeTreeRewrite</span><span class="p">)</span> <span class="o">$</span> <span class="kr">do</span> <span class="c1">-- A single use rule matched once, we gotta delete it!</span>
        <span class="kr">let</span> <span class="p">(</span><span class="kt">Rewrite</span> <span class="n">patternToDelete</span> <span class="kr">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">fromJust</span> <span class="n">maybeTreeRewrite</span>
        <span class="n">modifyRuntimeSingleUseRules</span> <span class="p">(</span><span class="n">filter</span> <span class="p">(</span><span class="n">patternDefEq</span> <span class="n">patternToDelete</span><span class="p">))</span>
    <span class="c1">-- Apply multi use tree rewriting rules</span>
    <span class="c1">-- !!! ===== We check the multiset here again!!! Wow!!!!!  ===== !!! --</span>
    <span class="n">defs'</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="n">runtimeRules</span> <span class="o">&gt;&gt;=</span> <span class="n">filterByMultiset</span>
    <span class="n">maybeTreeRewrite'</span> <span class="o">&lt;-</span> <span class="n">applyTreeDefs</span> <span class="n">defs'</span>
    <span class="c1">-- Begin 3 (I Love Laziness)</span>
    <span class="n">newZipper</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="p">(</span><span class="kt">Z</span><span class="o">.</span><span class="n">nextDfs</span> <span class="o">.</span> <span class="n">runtimeZipper</span><span class="p">)</span>
    <span class="n">modifyRuntimeZipper</span> <span class="p">(</span><span class="n">const</span> <span class="n">newZipper</span><span class="p">)</span>
    <span class="n">when</span> <span class="n">verbose</span> <span class="p">(</span><span class="n">lift</span> <span class="o">$</span> <span class="n">print</span> <span class="n">newZipper</span><span class="p">)</span>
    <span class="c1">-- Give back the value we use to assess termination</span>
    <span class="n">atTop</span> <span class="o">&lt;-</span> <span class="n">gets</span> <span class="p">((</span><span class="o">==</span> <span class="kt">[]</span><span class="p">)</span> <span class="o">.</span> <span class="kt">Z</span><span class="o">.</span><span class="n">_Ups</span> <span class="o">.</span> <span class="n">runtimeZipper</span><span class="p">)</span>
    <span class="n">pure</span> <span class="p">(</span><span class="n">sum</span> <span class="o">$</span> <span class="n">maybe</span> <span class="mi">0</span> <span class="p">(</span><span class="n">const</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;$&gt;</span> <span class="p">[</span><span class="n">maybeTreeRewrite</span><span class="p">,</span> <span class="n">maybeTreeRewrite'</span><span class="p">],</span> <span class="n">atTop</span><span class="p">)</span>
        <span class="kr">where</span>
            <span class="c1">-- True if the EatenDef has the same pattern as the tree</span>
            <span class="n">patternDefEq</span> <span class="o">::</span> <span class="kt">Tree</span> <span class="kt">RValue</span> <span class="o">-&gt;</span> <span class="kt">EatenDef</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
            <span class="n">patternDefEq</span> <span class="n">pat1</span> <span class="n">def</span> <span class="o">=</span> <span class="kr">case</span> <span class="n">defToRewrite</span> <span class="n">def</span> <span class="kr">of</span>
                <span class="kt">Nothing</span> <span class="o">-&gt;</span> <span class="kt">True</span>
                <span class="kt">Just</span> <span class="p">(</span><span class="kt">Rewrite</span> <span class="n">pat2</span> <span class="kr">_</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">pat1</span> <span class="o">/=</span> <span class="n">pat2</span> 
</code></pre></div></div>

<p>Sorry. Maybe it’s rude to throw a huge block of code at you at once. I ought to be more polite.</p>

<p>Anyways, Rosin will now refuse to match a rule if the multiset state is insufficient. Look!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat sample/small.tree; rosin -p sample/small.tree 
(fruit-salad | () &amp; hello ~&gt; world)
(1 2 3 hello 4 5 6)

+-----------------+
| Final transform |
+-----------------+
(defined "Multiset {unmultiset = fromList [(fruit-salad,1)]}| &amp; hello ~&gt; world")
(1 2 3 hello 4 5 6)
</code></pre></div></div>

<p>So that’s that. Hope you enjoyed! Next time maybe we’ll go less heavy on the Haskell and heavier on the Rosin. Talk to you soon!</p>

<h1 id="day-16">Day 16</h1>

<p>Tonight cost me a lot of my sanity and I will inevitably be gluing back together the pieces of my sleep-deprived codebase later.</p>

<p>That being said… <a href="https://github.com/DataKinds/tree-rewriter/commit/3a3e4fca7edae9fb754a0c312aeff2f4744cd50f">Rosin now has full support for rules which interact with multiset state</a>. It’s done oh my god it’s done. Done enough to start writing tests for and to start making more informed decisions on structuring the codebase. They don’t do anything fancy like match against regex but that can all happen soon. We have a foundation in place to build off of!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat sample/poc.rosin; rosin -p sample/poc.rosin 
(| pear)
(| pear)
(| pear)
(| orange)

(@ bag)

((pear pear) | pear-salad)

(@ bag)

+-----------------+
| Final transform |
+-----------------+
((orange 1) (pear 3))
((orange 1) (pear 1) (pear-salad 1))
</code></pre></div></div>

<p>What’s <code class="language-plaintext highlighter-rouge">(@ bag)</code>, you may ask? That’s a builtin! This particular builtin writes out the current state of the multiset bag.</p>

<p>There are a couple of builtins hanging around now. Most notably <code class="language-plaintext highlighter-rouge">(@ parse :str)</code> which parses a string into a Rosin tree, and <code class="language-plaintext highlighter-rouge">(@ cat :path)</code> which loads another file as a string. These together make my test files prettier.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((p ?&gt;) ~&gt; ?&gt;)
((t :desc :!got :wanted) ~&gt; (p (❌ :desc got :got wanted :wanted)))
((t :desc :!val :val) ~&gt; (p (✅ :desc :val)))
((Test a :desc Given :test We expect :val) ~&gt; (t :desc :test :val))

(Test a "Lambda rule"
    Given (((fruit basket) ~ apples oranges pears dragonfruit) 
           (fruit basket) 
           (fruit basket)) 
    We expect (apples oranges pears dragonfruit
               (fruit basket)))

(Test a "Confusing named rule"
       Given ((once ~ once once) once) We expect (once once))
</code></pre></div></div>

<p>now becomes</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(@ parse (@ cat "t.rosin"))

(Test a "Lambda rule"
    Given (((fruit basket) ~ apples oranges pears dragonfruit) 
           (fruit basket) 
           (fruit basket)) 
    We expect (apples oranges pears dragonfruit
               (fruit basket)))

(Test a "Confusing named rule"
       Given ((once ~ once once) once) We expect (once once))
</code></pre></div></div>

<p>which is way cuter if I do say so myself.</p>

<p>More to come soon!</p>

<h1 id="day-17">Day 17</h1>

<p>Nothing today. I played through a cute little game called <a href="https://store.steampowered.com/app/3361470/Digseum/">Digseum</a> after work tonight.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/E19QiXv27I8?si=WuYzU35pfNAjIFGN" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>It was absolutely adorable. Worth the $2 and quite satisfying to boot.</p>]]></content><author><name></name></author><category term="software" /><category term="december-adventure" /><summary type="html"><![CDATA[Hello all! I will be participating in the 2024 December Adventure, and this is my captain’s log for the month. From Eli’s page:]]></summary></entry><entry><title type="html">Stop Asking What, Start Asking Why</title><link href="https://datakinds.github.io//2024/11/13/stop-asking-what-start-asking-why" rel="alternate" type="text/html" title="Stop Asking What, Start Asking Why" /><published>2024-11-13T22:03:00+00:00</published><updated>2024-11-13T22:03:00+00:00</updated><id>https://datakinds.github.io//2024/11/13/stop-asking-what-start-asking-why</id><content type="html" xml:base="https://datakinds.github.io//2024/11/13/stop-asking-what-start-asking-why"><![CDATA[<p>It tends to be the case that doing something professionally can kill one’s drive to do so recreationally. Despite this, I still enjoy hobby programming and participating in online programming communities in my off-time. In these communities I have found a wealth and breadth of knowledge I would not have encountered otherwise. However, that comes with a risk: it is very easy to waste your time online. Even worse, the variance in how much you will get out of any given conversation is massive.</p>

<p>There is a lot of precedent for how to maximize the value of your time when you <em>ask questions</em> online. “<a href="https://solhsa.com/dontask.html">Don’t ask to ask</a>” and (perhaps more humorously) <a href="https://en.wikipedia.org/wiki/Ward_Cunningham#Law">Cunningham’s Law</a> are what immediately comes to my mind in this context. There is much less precedent for how to answer questions – or even why we choose to spend our time answering questions online.</p>

<p>Eric S. Raymond <a href="http://catb.org/~esr/faqs/smart-questions.html">has written at length about asking good questions</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and yet left the “<em>How To Answer Questions in a Helpful Way</em>” section stunted at 9 suggestions. Stack Overflow is the same: the page <a href="https://stackoverflow.com/help/how-to-ask">describing how to ask questions</a> is rich with direct suggestions on how to make the best use of your time asking questions. Most of the given advice is generally applicable to any development forum with tags and post titles. The <a href="https://stackoverflow.com/help/how-to-answer">page describing how to answer them</a> is about half the length and full of softer advice dedicated to SO’s specific position as a community question-archival site: writing with good grammar, providing context to avoid link bitrot, using the bounty system to ask someone to continue your partial answer.</p>

<p>There is an asymmetry here. Advice given to question-askers involves getting the most value out of the time you and the community spend on said question. This <em>is</em> ultimately the goal of the asker: to learn, to gain value from the question at hand. Contrast this with advice given to question-answerers, which is more scatter-shot and generally revolves around <em>producing a satisfactory answer</em>. This is unmotivating as <em>this is ultimately not the goal of the answerer!</em> I claim, supported by the text above, that there are really two reasons someone would want to answer questions:</p>

<ol>
  <li>
    <p><strong>To learn.</strong></p>

    <p>Common knowledge dictates that one does not understand something if they cannot explain it. Explaining things online is a fantastic way to find one’s own knowledge blind spots; collaborative problem-solving gives a very low-risk opportunity to dive deeper into topics just outside of one’s reach.</p>

    <p>Learning also happens at a community level. Raymond notes that a helpful answer will “<em>[h]elp your community learn from the question.</em> When you field a good question, ask yourself ‘How would the relevant documentation or FAQ have to change so that nobody has to answer this again?’ Then send a patch to the document maintainer.”.</p>
  </li>
  <li>
    <p><strong>To grow their community.</strong></p>

    <p>Raymond notes that “[Hackers] like answering questions for people who have demonstrated they can learn from the answers”. A person who grew after interacting with a community is going to come back for more. This is a positive feedback loop, and it’s multiplied by the near-realtime responses and casual atmosphere given by platforms such as Discord or IRC. Strong learners with interesting, insightful, novel questions make for great community members!</p>
  </li>
</ol>

<p>I am excluding any communities that set up gamified or otherwise perverse objectives to encourage participation (upvote this post if you agree!)</p>

<p>This brings me to my ultimate point: <strong>how do we maximize these incentives as question-answerers</strong>? What can we do to go above and beyond producing helpful answers, making the time we spend answering more valuable to us? Here are a couple of things that I have personally found to make my time participating online more valuable:</p>

<h2 id="the-great-nonexhaustive-framework-for-online-participation">The great, nonexhaustive framework for online participation</h2>

<ul>
  <li>Use realtime platforms to your advantage
    <ul>
      <li>As mentioned above, instant messaging platforms allow for instant feedback between question-askers and question-answerers. Many of the concerns about asking good questions come from a time of email lists and traditional forums, where a lack of information in the original post could take days to be rectified. Over Discord, getting clarifications from askers is often a matter of minutes. This drastically limits the frustration and wasted time associated with badly-framed questions.</li>
      <li>The conversational nature of chat platforms allows troubleshooting and stating a problem space to naturally become brainstorming. This is not the case on Q&amp;A platforms, for example, as there is a strict distinction between “question” and “answer”. Intermediate brainstorming and conversation is relegated to a comments section or a secondary chat room. On an IM platform this distinction does not exist – to maximize learning, one must take advantage of this.</li>
    </ul>
  </li>
  <li>Assume good faith
    <ul>
      <li>This is the titular “stop asking what, start asking why”.
        <ul>
          <li>Bad actors or low-effort posters in online communities are inevitable. That being said,</li>
          <li>I have seen enough instances in programming communities where someone will come in with a conversational style or set of ideas that resemble bad-faith posting. They’ll use ALL CAPS or a twyping qwirk or send / messages / with / one / word. They might be stubborn or rash or stuck on their own ideas.</li>
          <li>These posters often present some of the most unique ideas available, and I have found that engaging can often maximize learning (sometimes at the cost of frustration). As a question-answerer, maximizing learning mandates that one must assume good-faith in these situations and dig into the “why”s of their choices rather than being stuck on the “what”s of their possibly absurd current situation. This <em>does</em> often require skill in analyzing XY problems.</li>
        </ul>
      </li>
      <li>Assuming a given person is a bad actor is a losing-sum game. Consider the cases:
        <ul>
          <li>A good-faith question is met with an assumption of good-faith ➡️ everyone is happy, the question may get answered</li>
          <li>A bad-faith question is met with an assumption of good-faith ➡️ either the asker improves their question, gets bored and leaves, or they escalate, at which point you can disengage</li>
        </ul>
      </li>
      <li>Or, on the contrary:
        <ul>
          <li>A good-faith question is met with an assumption of bad-faith ➡️ a potential community member is turned away</li>
          <li>A bad-faith question is met with an assumption of bad-faith ➡️ time is saved as you may disengage immediately</li>
        </ul>
      </li>
      <li>To maximize community growth, we must therefore assume good-faith to avoid turning away potential community members.</li>
      <li>To maximize learning as a result of these interactions, we must consider that nothing is learned upon turning away community members or immediately disengaging with bad-faith questions. <em>Something</em> may be learned if the is answered or if the badly-posed question is improved. Attempting to assist with the low-effort poster’s issue may give the answerer an opportunity to gain a deeper knowledge of the system being badly asked about.</li>
      <li>Efficient moderation allows active community participants to more effectively gauge when to disengage with an actual bad actor, minimizing the time wasted.</li>
    </ul>
  </li>
  <li>Address XY problems through risk analysis
    <ul>
      <li>To address an XY problem, one must first learn to identify an XY problem.
        <ul>
          <li>Look for questions where the asker wants to know how to carry out a task using tooling that isn’t made for that task</li>
          <li>Look for questions where the asker insists there is only one way forward</li>
          <li>Look for questions where the asker thinks a process is <em>supposed</em> to work.</li>
        </ul>
      </li>
      <li>Address the problem fully if you choose to address it. Ask the asker what they’re trying to accomplish instead of entertaining their solution. Note that they only see trees where you may see the forest. Learn from the aspects of the situation that they’ve missed, and consider if there are any similar workflows that you have that are suffering from the same blind spots.</li>
      <li>Raymond states the fundamental point well: “<em>If you’re going to answer the question at all, give good value.</em> Don’t suggest kludgy workarounds when somebody is using the wrong tool or approach. Suggest good tools. Reframe the question.”</li>
    </ul>
  </li>
  <li>Askers must answer. Answerers must ask.
    <ul>
      <li>To maximize learning, you must be willing to have a conversation!</li>
      <li>Make use of the resource that other question-answerers provide.</li>
      <li>There must be no hierarchical distinction between those who ask and those who answer in a community. Maximization of learning requires that everyone is comfortable to both seek knowledge and to share their subset of knowledge.
        <ul>
          <li>The effect where longer standing members of a community are afforded more technical weight than newer members needs to be explicitly considered when the goal is the breakdown of hierarchy.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p>I hope that you’re able to get something out of the suggestions I’ve made above. If not, hopefully the reframing of online discussion as something potentially valuable encourages you to go out and participate :)</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Shout out to the footnote at <a href="https://dontasktoask.com/">https://dontasktoask.com/</a> for linking Raymond’s essay, I hadn’t seen it before and it’s a complete treasure trove of info. Without that this whole blog post was just going to be the <code class="language-plaintext highlighter-rouge">Assume good faith</code> bullet point. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="software" /><summary type="html"><![CDATA[It tends to be the case that doing something professionally can kill one’s drive to do so recreationally. Despite this, I still enjoy hobby programming and participating in online programming communities in my off-time. In these communities I have found a wealth and breadth of knowledge I would not have encountered otherwise. However, that comes with a risk: it is very easy to waste your time online. Even worse, the variance in how much you will get out of any given conversation is massive.]]></summary></entry><entry><title type="html">[old draft] Ride along wrapper-free game development in Haskell</title><link href="https://datakinds.github.io//2024/09/27/ride-along-game-development-in-haskell" rel="alternate" type="text/html" title="[old draft] Ride along wrapper-free game development in Haskell" /><published>2024-09-27T22:00:00+00:00</published><updated>2024-09-27T22:00:00+00:00</updated><id>https://datakinds.github.io//2024/09/27/ride-along-game-development-in-haskell</id><content type="html" xml:base="https://datakinds.github.io//2024/09/27/ride-along-game-development-in-haskell"><![CDATA[<p>Hey all! I was going through my blog post backlog and found this draft from <code class="language-plaintext highlighter-rouge">2019-06-05</code>. Thought I’d just throw it up on the blog: there’s some useful stuff about Haskell’s FFI here, and I also wax poetic about software development a little bit. This was written before the whole <a href="https://datakinds.github.io/2022/06/17/100-blog-posts">100 blog posts</a> thing so it doesn’t count.</p>

<p>The original title of this was to be “From Context to Cptr: Bare Bones Game Development in Haskell”. I will let 2019 me take it from here.</p>

<hr />

<p>I got into programming when I was 8 because I wanted to make games. I guess, deep down, I was never truly able to satiate that desire. I’ve never released a game, I’ve never completed a game, and hell – I’ve never gotten far enough to even draw up assets for a game.</p>

<p>But I ran into a library yesterday called Raylib that kind of rekindled the playful fire that lived deep down under my pragmaticism. Raylib lives at <a href="https://www.raylib.com/">https://www.raylib.com/</a>. I immediately fired up VS Code and hammered out some C for the first time in two or three years. Suffice to say, I had <em>fun</em>. Not fun like “wow, this code is elegant,” or fun like “wow I’m making a lot of money doing this,” but fun like “I’m giggling at the screen because I made a circle bounce back and forth.”</p>

<p>And that was an incredible feeling.</p>

<p>So, in the spirit of all things good: if it’s worth doing, it’s worth overdoing. That’s why I’m writing this blog post.</p>

<p>This won’t be like any other blog post that I’ve written. I am writing this post live as I’m experimenting with building a game from the bottom up in Haskell. You’ll see every single decision I make, and everything I find exciting enough to share, even if I go back on it five minutes later. No game frameworks, no library bindings – just me, Stack, a fresh install of <a href="https://www.glfw.org/">GLFW</a>, and a readiness to get hacking.</p>

<p>In one window, I have my blog post. In the other window, my code. Without further ado…</p>

<h2 id="923-pm-day-1">9:23 PM, day 1</h2>

<p>I came up with the idea and started writing this blog post.</p>

<h2 id="1119-pm">11:19 PM</h2>

<p>Finally got around to initializing the stack project!</p>

<p><img src="/assets/imgs/ride-along-game-dev/1119.png" alt="Stack project screenshot" /></p>

<h2 id="1257-am">12:57 AM</h2>

<p>Here’s my first attempt at defining enough of a wrapper around GL and GLFW in order to <a href="https://www.glfw.org/documentation.html">run the example code on the GLFW documentation page</a>. This is before I’ve even tried compiling it, so this is pre-iteration-1:</p>

<p><code class="language-plaintext highlighter-rouge">GLFW.hs</code>:</p>

<div class="language-hs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">{-# LANGUAGE ForeignFunctionInterface #-}</span>

<span class="kr">module</span> <span class="nn">GLFW</span> <span class="kr">where</span>

<span class="kr">import</span> <span class="nn">Control.Monad</span>

<span class="cd">-- | Init functions</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwInit"</span> <span class="n">init</span> <span class="o">::</span> <span class="kt">IO</span> <span class="kt">Int</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwTerminate"</span> <span class="n">terminate</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>

<span class="cd">-- | Window functions</span>
<span class="kr">data</span> <span class="kt">Window</span> <span class="o">=</span> <span class="kt">InternalWindow</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Storable</span><span class="p">)</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwCreateWindow"</span> <span class="n">createWindow</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">CString</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="p">(</span><span class="kt">Ptr</span> <span class="kt">Window</span><span class="p">)</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwWindowShouldClose"</span> <span class="n">_windowShouldClose</span> <span class="o">::</span> <span class="kt">Ptr</span> <span class="kt">Window</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="kt">Int</span>
<span class="n">windowShouldClose</span> <span class="o">::</span> <span class="kt">Ptr</span> <span class="kt">Window</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="kt">Bool</span>
<span class="n">windowShouldClose</span> <span class="o">=</span> <span class="n">fmap</span> <span class="p">(</span><span class="mi">0</span> <span class="o">/=</span><span class="p">)</span> <span class="o">.</span> <span class="n">_windowShouldClose</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwSwapBuffers"</span> <span class="n">swapBuffers</span> <span class="o">::</span> <span class="kt">Ptr</span> <span class="kt">Window</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwPollEvents"</span> <span class="n">pollEvents</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>

<span class="cd">-- | Context functions</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glfwMakeContextCurrent"</span> <span class="n">makeContextCurrent</span> <span class="o">::</span> <span class="kt">Ptr</span> <span class="kt">Window</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="nb">()</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">GL.hs</code>:</p>

<div class="language-hs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">{-# LANGUAGE CPP, ForeignFunctionInterface #-}</span>

<span class="kr">module</span> <span class="nn">GL</span> <span class="kr">where</span>

<span class="o">#</span><span class="n">include</span> <span class="o">&lt;</span><span class="kt">GL</span><span class="o">/</span><span class="n">gl</span><span class="o">.</span><span class="n">h</span><span class="o">&gt;</span>

<span class="kr">data</span> <span class="kt">ClearMask</span> <span class="o">=</span> <span class="kt">COLOR_BUFFER_BIT</span> <span class="o">|</span> <span class="kt">DEPTH_BUFFER_BIT</span> <span class="o">|</span> <span class="kt">STENCIL_BUFFER_BIT</span>
<span class="n">foreign</span> <span class="kr">import</span> <span class="nn">ccall</span> <span class="s">"glClear"</span> <span class="n">_clear</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">clear</span> <span class="o">::</span> <span class="kt">ClearMask</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">clear</span> <span class="kt">COLOR_BUFFER_BIT</span> <span class="o">=</span> <span class="n">_clear</span> <span class="kt">GL_COLOR_BUFFER_BIT</span>
<span class="n">clear</span> <span class="kt">DEPTH_BUFFER_BIT</span> <span class="o">=</span> <span class="n">_clear</span> <span class="kt">GL_DEPTH_BUFFER_BIT</span>
<span class="n">clear</span> <span class="kt">STENCIL_BUFFER_BIT</span> <span class="o">=</span> <span class="n">_clear</span> <span class="kt">GL_STENCIL_BUFFER_BIT</span>
</code></pre></div></div>

<h2 id="108-am">1:08 AM</h2>

<p>Turns out including header files in Haskell isn’t as easy as using <code class="language-plaintext highlighter-rouge">#include &lt;GL/gl.h&gt;</code>.</p>

<p><img src="/assets/imgs/ride-along-game-dev/108.png" alt="C preprocessor error" /></p>

<h2 id="638-pm-day-3">6:38 PM, day 3</h2>

<p>After a day long break where I was busy with some freelance work, I came back to this project and finally made some progress! After fixing some linker errors a couple Haskell bugs…</p>

<p>We have a window!</p>

<p><img src="/assets/imgs/ride-along-game-dev/638.png" alt="Hello, window!" /></p>

<h3 id="interlude-why-haskell">Interlude: Why Haskell?</h3>

<blockquote>
  <p>I believe that the monadic approach to programming, in which actions are first class values, is itself interesting, beautiful, and modular. In short, Haskell is the world’s finest imperative programming language.</p>
</blockquote>

<p><a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/mark.pdf">from https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/mark.pdf</a>.</p>

<p>I didn’t want to do this in C because C is the language that can directly interface with GLFW and OpenGL. I didn’t want to be able to do this because I wanted a reason to sit and ponder each function before I use it. Having to write sensible bindings for every single graphics function that I use encouraged me to take this time.</p>

<p>So the question remained of what langauge I should decide to use. I could have went with a run-of-the-mill scripting language, but something about the elegant abstractions of Haskell drew me toward using it for this project.</p>

<p>For one, Haskell is my favorite language, so it’s naturally one of my top choices for anything that I do. But further than that, the biggest roadblock that I hit trying to learn OpenGL the first time a while back was the difficulty of composing abstractions on top of the admittedly obtuse graphics pipeline. Haskell is notorious for being able to abstract. So abstract I shall.</p>

<h2 id="1153-pm-day-3">11:53 PM, day 3</h2>

<p>Started reading through <a href="https://learnopengl.com/Getting-started/Hello-Triangle">https://learnopengl.com/Getting-started/Hello-Triangle</a>. Buffers make a lot more sense than I remember them making.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Hey all! I was going through my blog post backlog and found this draft from 2019-06-05. Thought I’d just throw it up on the blog: there’s some useful stuff about Haskell’s FFI here, and I also wax poetic about software development a little bit. This was written before the whole 100 blog posts thing so it doesn’t count.]]></summary></entry><entry><title type="html">launching blog v2</title><link href="https://datakinds.github.io//2024/07/27/launching-blog-v2" rel="alternate" type="text/html" title="launching blog v2" /><published>2024-07-27T05:37:00+00:00</published><updated>2024-07-27T05:37:00+00:00</updated><id>https://datakinds.github.io//2024/07/27/launching-blog-v2</id><content type="html" xml:base="https://datakinds.github.io//2024/07/27/launching-blog-v2"><![CDATA[<p>Hello all</p>

<p>as you can see, the blog looks different!</p>

<p>it used to look like this</p>

<p><img src="/assets/imgs/v2/image.png" alt="the old blog, stardrew valley themed" /></p>

<p>it was themed after stardew valley. it was cute and i liked it a lot. The reasons I decided to give it a visual overhaul are as follows:</p>
<ul>
  <li>I didn’t know where to put other pages without it looking bizarre,</li>
  <li>I’d like to write more long form content <em>about</em> video games and I felt like having my page themed after a single game is a lil biased, and</li>
  <li>I didn’t feel like touching up the background lol</li>
</ul>

<p>I had a few goals with this redesign</p>
<ul>
  <li>Keep it quirky</li>
  <li>Keep it readable for old posts (including adding some extra goodies like syntax highlighting)</li>
  <li>Giving myself space to add more pages and do further experimentation</li>
</ul>

<p>I suppose this also counts as part of my <a href="/2022/06/17/100-blog-posts">100 blog posts challenge</a>, but I’m no longer numbering the titles because that was annoying lol</p>

<p>so, with this redesign I’ve given myself a good foundation to keep hacking away on. hope you like it! new long form posts and possibly other goodies soon…</p>]]></content><author><name></name></author><category term="100-blog-posts" /><category term="v2-site" /><summary type="html"><![CDATA[Hello all]]></summary></entry><entry><title type="html">nasin nanpa sin tan nasin nanpa Loman</title><link href="https://datakinds.github.io//2022/11/08/nasin-nanpa-sin-tan-nasin-nanpa-loman" rel="alternate" type="text/html" title="nasin nanpa sin tan nasin nanpa Loman" /><published>2022-11-08T02:14:00+00:00</published><updated>2022-11-08T02:14:00+00:00</updated><id>https://datakinds.github.io//2022/11/08/nasin-nanpa-sin-tan-nasin-nanpa-loman</id><content type="html" xml:base="https://datakinds.github.io//2022/11/08/nasin-nanpa-sin-tan-nasin-nanpa-loman"><![CDATA[<p>toki! mi wile e nasin pona pi toki nanpa lon toki pona. mi kepeken nimi pu taso. ni la mi ken ala toki e nimi san (3), e nimi likujo (7), e ante. mi pilin pona tawa nanpa ni taso:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* wan = 1
* tu = 2
* luka = 5
* mute = 20
* ale = 100
</code></pre></div></div>

<p>jan li toki e nimi “tu tu” la mi kute li sona e nanpa 4. jan li toki e nimi “tu wan” la mi sona e nanpa 3. jan pi mute suli a li sona sama. mi toki lon ilo Siko kepeken nasin ni la jan li sona e nanpa mi.</p>

<p>nasin ni li pona lukin. taso, o lukin tu. mi wile toki e nanpa 99. nasin ni la nanpa 99 li sama nimi “mute mute mute mute luka luka luka tu tu”. ni li suli ike a! mi wile ala toki e ni. mi toki e ni la jan li ken ala sona e nanpa 99. jan li kute e “… mute, mute, mute, …”. ni li pakala e lawa.</p>

<p>mi toki e nimi “luka wan” la jan li sona e nanpa 6. sama la jan Loman li sitelen e nimi “VI” la jan Loman ante li lukin li sona e nanpa 6. jan Loman li ken sitelen kin e nimi “IV”. jan ante li lukin e nimi “IV” la ona li sona e nanpa 4. taso… mi ken ala toki e nimi “wan luka”. mi tawa e nimi “luka” lon monsi pi nimi “wan” la jan li ken ala sona e nanpa 4. jan li sona e nanpa 6, anu, jan li sona e ijo “wan” pi mute 5. mi wile pana e sona pi nasin nanpa Loman ni la mi wile kepeken nasin ni a.</p>

<p>nasin nanpa Loman li wawa. mi wile toki e nanpa 99 la mi ken toki lili kepeken nasin ni. o lukin! nanpa 99 li sama nimi “wan ale” kepeken nasin ni. nimi “wan ale” li pakala ala li suli pona. kin la nanpa 4 li sama nimi “wan luka”.</p>

<p>lawa pi nasin nanpa Loman li pona (sama toki pona, tawa mi). ni la toki pona li wile e nasin nanpa la toki pona o kama jo e nasin ni. lawa pi nasin nanpa Loman li ni:</p>

<ol>
  <li>nanpa lili li lon monsi pi nanpa suli la o wan e ona. o lukin: nimi “luka (suli) wan (lili)” li sama nanpa 5 + 1 = 6.</li>
  <li>nanpa li lon monsi pi nanpa sama la o wan e ona. o lukin: nimi “luka luka (sama)” li sama nanpa 5 + 5 = 10.</li>
  <li>nanpa suli li lon monsi pi nanpa lili la o pakala e nanpa suli kepeken nanpa lili. o lukin: nimi “luka (lili) mute (suli)” li sama nanpa 20 - 5 = 15.</li>
</ol>

<p>lawa ni li lawa ale! mi toki e ni: nasin nanpa Loman li pona li wile lili e wawa sina. nasin ni li pona e nanpa. mi pilin e ni: nasin nanpa Loman li kute e nasin pi toki pona.</p>

<p>o tawa! o musi! o toki!</p>]]></content><author><name></name></author><category term="toki-pona" /><category term="100-blog-posts" /><summary type="html"><![CDATA[toki! mi wile e nasin pona pi toki nanpa lon toki pona. mi kepeken nimi pu taso. ni la mi ken ala toki e nimi san (3), e nimi likujo (7), e ante. mi pilin pona tawa nanpa ni taso:]]></summary></entry><entry><title type="html">[5/100] jan li pali e toki pona tan ni: toki pona li musi.</title><link href="https://datakinds.github.io//2022/08/31/jan-li-pali-e-toki-pona-tan-ni-toki-pona-li-musi" rel="alternate" type="text/html" title="[5/100] jan li pali e toki pona tan ni: toki pona li musi." /><published>2022-08-31T06:30:00+00:00</published><updated>2022-08-31T06:30:00+00:00</updated><id>https://datakinds.github.io//2022/08/31/jan-li-pali-e-toki-pona-tan-ni-toki-pona-li-musi</id><content type="html" xml:base="https://datakinds.github.io//2022/08/31/jan-li-pali-e-toki-pona-tan-ni-toki-pona-li-musi"><![CDATA[<h2 id="open-musi">open musi</h2>

<p>jan pi wile sona li toki tawa mi e ni: “sina toki kepeken toki pona tan seme? jan seme li toki sama?”.</p>

<p>mi toki e ni: “mi toki kepeken toki pona tan ni: ona li musi. kama pi sona toki li pona tawa mi.”</p>

<p>taso, jan pi wile sona li toki e ni: “sona pi toki pona li sona pi toki ala. sina o kama sona e toki <em>lon</em>.”</p>

<p>mi toki e ni: “toki pona li toki lon ala lon?”</p>

<p>jan pi wile sona li toki e ni: “toki pona li lon ala. sina o kama sona e toki Tosi e toki Sonko e toki Epanja. sina ken ala esun kepeken toki pona. “</p>

<p>mi toki e ni: “mi wile ala esun. ante la toki pona li <a href="https://www.semanticscholar.org/paper/How-learning-Toki-Pona-may-help-improving-in-a-or-Coluzzi/cdcd2e3c691092d9075a1e93122158cab22cfdea">ken pana e sona pi toki ante tawa sina</a>. taso, ni li pana e sona ala la ni li awen pona.”</p>

<hr />

<h2 id="ijo-suli">ijo suli</h2>

<p>ijo suli li ni: toki pona li musi. jan mute li toki e ni: “mi o kama sona e toki pona tan seme?” taso, toki pona li musi ala tawa sina la sina o kama ala sona e toki pona. tan ante li lon ala. musi li ale.</p>

<p>“musi li ale” li nasin wawa. jan mute li toki lawa e ni: “toki pona o wan e jan ale. toki pona o toki International Auxillary Language!” taso, ni li pakala e nasin wawa. toki IAL li musi ala! nasin pi wan e jan ale li musi ala! mi toki tu: toki pona li musi ala tawa jan la jan o kama ala sona e toki pona. taso, toki IAL li pona la toki IAL li wile e ni: jan ale li ken toki lon ona. ona li pona (jan ale li toki e ona) la jan mute li wile ala toki lon ona li musi ala toki. ni li ike suli. mi ale o kute e nasin wawa.</p>

<p>ante la kulupu pi toki pona li pana e pona mute tawa jan ale. jan suli li toki wawa e ni: “kulupu suli li ken ike”. ni li lon lili, taso, nasin wawa li ken awen e pona pi kulupu ni. kulupu li suli la toki pona li musi tawa jan ale kulupu la kulupu li awen pona. mi toki tu: musi li ale.</p>

<hr />

<h2 id="pini-musi">pini musi</h2>

<p>jan pi wile sona li toki e ni: “sina ken ala toki tawa jan lon linluwi ala.”</p>

<p>mi toki e ni: “sina ken ala toki tawa mama sina lon linluwi ala. mi en mama sina li toki lon tenpo pimeja ale.”</p>]]></content><author><name></name></author><category term="100-blog-posts" /><category term="toki-pona" /><summary type="html"><![CDATA[open musi]]></summary></entry><entry><title type="html">[4/100] nimi “pi” li sama ala nimi “of” tan seme? kin la toki sona ante.</title><link href="https://datakinds.github.io//2022/08/13/nimi-pi-li-sama-ala-pi-nimi-of-tan-seme" rel="alternate" type="text/html" title="[4/100] nimi “pi” li sama ala nimi “of” tan seme? kin la toki sona ante." /><published>2022-08-13T01:50:00+00:00</published><updated>2022-08-13T01:50:00+00:00</updated><id>https://datakinds.github.io//2022/08/13/nimi-pi-li-sama-ala-pi-nimi-of-tan-seme</id><content type="html" xml:base="https://datakinds.github.io//2022/08/13/nimi-pi-li-sama-ala-pi-nimi-of-tan-seme"><![CDATA[<p>toki. sina lukin e pakala toki mi la sina o toki tawa mi. mi o pona e lipu ni.</p>

<hr />

<p>jan sin li toki sona e ni: “nimi ‘pi’ li sama ala sama nimi ‘of’ lon toki Inli?”. jan sona li toki e ni: “ala”.</p>

<p>kin la jan sin li toki sona e ni: “mi ken ala ken ante pi toki Inli e nimi ‘toki’ tawa nimi ‘talk’ [anu nimi ante…]”. jan sona li toki e ni: “ala”.</p>

<p>ni li nasin pona. taso… ona li sama ala tan seme? sina ken ala ante tan seme? mi pana e sona ni lon lipu ni.</p>

<p>ijo wan li ken weka e len sona la ijo wan li ni:</p>

<p><strong>nimi ale li jo e poki kon.</strong></p>

<p>poki kon li seme? o lukin:</p>

<p>mi sitelen e nimi “tomo” lon poki kon ona:</p>

<p><img src="/assets/imgs/2022-08-12-19-31-20.png" alt="" /></p>

<p>lon la poki kon ona li jo e ijo mute. mi ken sitelen e ijo:</p>

<p><img src="/assets/imgs/2022-08-12-19-44-50.png" alt="" /></p>

<p>mi ken sitelen e nimi ante e poki kon ante:</p>

<p><img src="/assets/imgs/2022-08-12-20-01-38.png" alt="" /></p>

<p>o lukin! nimi “tomo” en nimi “building” li jo e sitelen sama. mi ken wan e ni tu:</p>

<p><img src="/assets/imgs/2022-08-12-20-24-43.png" alt="" /></p>

<p>taso, mi ken ala wan e sitelen ale pi nimi “tomo” e sitelen ale pi nimi “building”. nimi “building” taso li jo e sitelen pi jan pali. kin la nimi “tomo” taso li jo e sitelen pi tomo telo e sitelen pi tomo lape.</p>

<p>nimi “tomo” li sama ala nimi “building” tan sitelen ni. poki kon ni tu li jo e sitelen ante.</p>

<p>mi ken sitelen e poki kon mute ante. taso, poki kon ante li ken ala sama poki kon pi nimi “tomo”.</p>

<p><img src="/assets/imgs/2022-08-12-20-59-15.png" alt="" /></p>

<p>nimi “bedroom” li lon insa poki kon pi nimi “tomo”. ni li wawa tan ni: bedroom ale li tomo. (taso, tomo ale ala li bedroom).</p>

<p>ijo ale li toki e ni: nimi “tomo” li ken sama pi nimi mute. nimi mute ni li lon poka poki kon pi nimi “tomo”.</p>

<p>sina sitelen e poki kon ale la sina pali e jaki. nanpa sitelen li suli ike. nanpa pi poki kon li suli ike. nimi li wawa!</p>

<hr />

<p>mi wile e ni: sina pilin pona li pilin lawa wawa.</p>

<h1 id="o-pona-tawa">o pona tawa:</h1>

<ul>
  <li>sitelen tan linluwi <a href="https://www.flickr.com/photos/brent_nashville/4645745968">https://www.flickr.com/photos/brent_nashville/4645745968</a> CC BY-NC 2.0</li>
  <li>sitelen tan linluwi <a href="https://commons.wikimedia.org/wiki/File:Bed_Room.jpg">https://commons.wikimedia.org/wiki/File:Bed_Room.jpg</a> CC BY-SA 4.0</li>
  <li>sitelen tan linluwi <a href="https://commons.wikimedia.org/wiki/File:Construction_workers.JPG">https://commons.wikimedia.org/wiki/File:Construction_workers.JPG</a> CC BY-SA 3.0</li>
  <li>jan Ki lon ma pi kama sona. ona li pona e toki ni.</li>
</ul>]]></content><author><name></name></author><category term="toki-pona" /><category term="100-blog-posts" /><summary type="html"><![CDATA[toki. sina lukin e pakala toki mi la sina o toki tawa mi. mi o pona e lipu ni.]]></summary></entry><entry><title type="html">[3/100] nimi wan li taso suli lon toki pona</title><link href="https://datakinds.github.io//2022/08/07/nimi-wan-li-taso-suli-lon-toki-pona" rel="alternate" type="text/html" title="[3/100] nimi wan li taso suli lon toki pona" /><published>2022-08-07T18:50:00+00:00</published><updated>2022-08-07T18:50:00+00:00</updated><id>https://datakinds.github.io//2022/08/07/nimi-wan-li-taso-suli-lon-toki-pona</id><content type="html" xml:base="https://datakinds.github.io//2022/08/07/nimi-wan-li-taso-suli-lon-toki-pona"><![CDATA[<p>toki. mi wile pali e nimi suli kepeken nimi wan. sina lon ilo Siko la sina kute e nimi ni:</p>

<blockquote>
  <p>mi tawa tawa tomo mi.</p>
</blockquote>

<p>nimi ni en nimi “mi tawa tomo mi” li pana e tan sama. nimi tu “tawa tawa” li pana e pilin pona tawa mi. mi o suli e ni.</p>

<p>kin la mi toki insa e ni:</p>

<blockquote>
  <p>mi ken tawa tawa tawa.</p>
</blockquote>

<p>sina toki e ni la sina ken tawa. pini tawa la sina lon ma tawa.</p>

<p>taso, mi ken suli mute. mi toki lawa mute. pini toki lawa la mi pali e ni:</p>

<blockquote>
  <p>mi lon lon lon lon lon.</p>
</blockquote>

<p>sina toki e ni la sina lon lon (lon la sina lon). lon la sina lon ma lon. ni ale la sina lon lon lon lon lon.</p>

<p>sina ken ala lukin e ni la mi ken sitelen e poki. o lukin:</p>

<blockquote>
  <p>mi (lon lon) lon (lon lon).</p>
</blockquote>

<p>sina ken toki e nimi suli mute anu seme? sina ken pakala e nimi suli mi anu seme? o toki tawa mi kepeken ilo Siko lon @at#0754.</p>

<hr />

<p>jan Kekan San li toki e nasin ni tawa mi.</p>

<p>nasin ni li pana sona tan nimi “tawa” mute mute. sina ken lukin e “tawa tawa tawa… tawa tawa” kepeken nasin pi nanpa ante lili <a href="https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position">https://en.wikipedia.org/wiki/Fourth,_fifth,_and_sixth_derivatives_of_position</a>.</p>

<blockquote>
  <p>nimi “tawa” li ken nimi “speed” li ken nimi “speeder” lon toki Inli</p>

  <p>nimi “tawa tawa” li ken nimi “accelerate” li ken nimi “accelerator” lon toki Inli</p>

  <p>nimi “tawa tawa tawa” li ken nimi “jerk” li ken nimi “jerker” lon toki Inli</p>

  <p>nimi “tawa tawa tawa tawa” li ken nimi “snap” li ken nimi “snapper” lon toki Inli</p>

  <p>nimi “tawa tawa tawa tawa tawa” li ken nimi “crackle” li ken nimi “crackler” lon toki Inli</p>

  <p>nimi “tawa tawa tawa tawa tawa tawa” li ken nimi “pop” li ken nimi “popper” lon toki Inli</p>

  <p>nimi “tawa” pi nanpa ale li ken pana sona kepeken nasin sama.</p>
</blockquote>

<p>kin la mi pali e nimi “mi (tawa tawa…) tawa (tawa tawa…)”. nasin ni la mi ken pali e nimi mute pona kepeken nimi wan.</p>

<p>o pona mute tawa jan Kekan San tan pana sona.</p>]]></content><author><name></name></author><category term="100-blog-posts" /><category term="toki-pona" /><summary type="html"><![CDATA[toki. mi wile pali e nimi suli kepeken nimi wan. sina lon ilo Siko la sina kute e nimi ni:]]></summary></entry><entry><title type="html">[2/100] toki pona and the semantic subset</title><link href="https://datakinds.github.io//2022/08/03/toki-pona-and-the-semantic-subset" rel="alternate" type="text/html" title="[2/100] toki pona and the semantic subset" /><published>2022-08-03T05:38:00+00:00</published><updated>2022-08-03T05:38:00+00:00</updated><id>https://datakinds.github.io//2022/08/03/toki-pona-and-the-semantic-subset</id><content type="html" xml:base="https://datakinds.github.io//2022/08/03/toki-pona-and-the-semantic-subset"><![CDATA[<p><a href="https://tokipona.org/">toki pona is a tiny conlang created by Sonja Lang</a>. I am not here to write a toki pona tutorial, <a href="https://devurandom.xyz/tokipona/">that has been done a hundred times over</a> <a href="https://en.wikibooks.org/wiki/Updated_jan_Pije%27s_lessons">by many people a hundred times smarter than I am</a>.</p>

<p>No, I am here to talk about sets and spaces.</p>

<p>toki pona has only around 120 words. Each of these words has a very general meaning. Take the word “soweli” for instance. <a href="https://lipu-linku.github.io/">lipu linku</a> notes that the definition of soweli is “animal, beast, land mammal”. But we rarely use soweli to refer to the group of all animals. Instead, soweli is often used to refer to specific animals. Such as large cows. Or my small cat Raven. soweli pimeja li pu!</p>

<p><img src="/assets/imgs/cats/raven.png" alt="soweli pimeja" /></p>

<p>This means you must combine words with very general meanings if you want to refer to specific objects. English will have a single word to refer to my “home”, but in toki pona it could be the</p>

<table class="tp-gloss">
    <tr>
        <td>tomo</td><td>pi</td><td>supa</td><td>lape</td>
    </tr>
    <tr>
        <td>BUILDING</td><td>(</td><td>SURFACE</td><td>SLEEP</td>
    </tr>
    <tr>
        <td colspan="4"><i>Home? Bedroom? Blanket drawer?</i></td>
    </tr>
</table>

<p>or perhaps depending on the context it could be</p>

<div style="margin: 0 auto;">
    <table class="tp-gloss">
        <tr>
            <td>tomo</td><td>lili</td><td>pi</td><td>jan</td><td>olin</td>
        </tr>
        <tr>
            <td>BUILDING</td><td>SMALL</td><td>(</td><td>PERSON</td><td>LOVE</td>
        </tr>
        <tr>
            <td colspan="4"><i>Home? Little bachelor pad? Lover's hut?</i></td>
        </tr>
    </table>
</div>

<p>In both cases, the phrase began with the word “tomo”. Note that toki pona puts modifiers after the noun (unlike English*) so we will consider “tomo” to be the main noun of the noun phrase, called the head noun.</p>

<p>Both these noun phrases are referring to the same object, my home. These noun phrases are only distinct in that I’ve described my home differently. I might use the first phrase talking about my home to my parents and the second phrase talking about my home to my friends. Although they take two different perspectives, we may say these noun phrases refer to the same object – they have the same <em>referent</em>.</p>

<p>The head noun gives the reader the first clue as to what the referent of the noun phrase might be. Sometimes, the noun phrase ends there and you’ll have to infer the referent from context. Let \(\operatorname{ref}(T)\) be the set of all concrete things that the toki pona phrase \(T\) can refer to. We may start playing this game immediately: \(\operatorname{ref}(\text{tomo}) \) could be my house, it could be your house, it could be the restaurant down the street or the kitchen in a ship at sea. All of these possible referents exist within the <em>semantic space</em> of “tomo”.</p>

<p>In order to narrow the semantic space of “tomo”, additional modifiers can be used. The set \(\operatorname{ref}(\text{tomo lili}) \) is approximately equal to the set of all small buildings. A naive observer (me, intially) would now jump to a false conclusion: appending modifiers to noun phrases always restricts their semantic space. Or, to use the notation we’ve just created:</p>

<p>\[(\forall A,B \in \text{NounPhrases})\;\;\operatorname{ref}(A\,B) \subseteq \operatorname{ref}(A)\]</p>

<p>This works for large classes of noun phrases. We may deconstruct the noun phrase “tomo lili pi jan olin” word by word, as grammar allows, and show that this concatenation rule holds.</p>

<p>\(\operatorname{ref}(\text{tomo}) \) is roughly the set of buildings. \(\operatorname{ref}(\text{tomo lili})\) is roughly the set of small buildings – a subset of the set of buildings. \(\operatorname{ref}(\text{tomo lili pi jan}) \) is roughly the set of small buildings associated with people – a subset of the set of small buildings. \(\operatorname{ref}(\text{tomo lili pi jan olin}) \) is roughly the set of small buildings associated with lovers – a subset of the set of small buildings associated with people.</p>

<p>These noun phrases, which I will call <em>restrictive</em> noun phrases, have modifiers which only serve to narrow down the set of possible referents. Each new modifier concatenated on the end shrinks the possible semantic space of the phrase, carving out and removing referents which don’t align with the meaning of the modifier.</p>

<p>toki pona’s equivalent of the proper noun uses restrictive noun phrases. My toki ponized name is “Tala”, but to simply be “Tala” is ungrammatical. I must instead be “jan Tala”: the person whom is Tala. “Tala” thus serves as a restrictive modifier on the head noun “jan”, such that \(\operatorname{ref}(\text{jan Tala}) \subseteq \operatorname{ref}(\text{jan}) \). A popular nasin toki allows for an entire noun phrase to stand in as a head noun, allowing something like “kili sike Lemon” (FRUIT CIRCLE “Lemon”). This is, too, a restrictive noun phrase. The proof is left to the reader.</p>

<p>As elegant as they are, not every noun phrase is restrictive. For example, the emphatic particle “a” does not change the referent of a noun phrase, such that \(\operatorname{ref}(\text{tomo a}) = \operatorname{ref}(\text{tomo}) \implies \operatorname{ref}(\text{tomo a}) \not\subseteq \operatorname{ref}(\text{tomo}) \).</p>

<p>One might think that “ala”, which roughly translates to “NOT”, can produce a non-restrictive noun phrase. However, consider the following sentence:</p>

<div style="margin: 0 auto;">
    <table class="tp-gloss">
        <tr>
            <td>jan</td><td>ala</td><td>li</td><td>kama</td><td>musi</td>
        </tr>
        <tr>
            <td>PERSON</td><td>NOT</td><td>(verb)</td><td>BECOME</td><td>FUN</td>
        </tr>
        <tr>
            <td colspan="5"><i>Nobody is going to start having fun.</i></td>
        </tr>
    </table>
</div>

<p>I contest that \(\operatorname{ref}(\text{jan ala}) \) is equivalent to \(\operatorname{ref}(\text{jan}) \) in this context. This sentence makes the claim \((\forall p \in \text{People})\, p \text{ will not have fun}\). The direct negation of this preposition is \((\exists p \in \text{People})\, p \text{ will have fun}\). This corresponds to the toki pona sentence “jan li kama musi” (<em>People will start having fun</em>), which is the direct negation of “jan ala li kama musi”. Vitally, using “jan ala” as a subject or a direct object makes a logical claim spanning all people. Therefore, \(\operatorname{ref}(\text{jan ala}) \) is the set of all people and must be equivalent to \(\operatorname{ref}(\text{jan}) \)</p>

<p>I suppose I leave this blog post searching for a more satisfying counterexample to the restrictive noun phrase. I don’t think that “ala” actually produces a non-restrictive noun phrase. If anything it produces an effect something similar to “a” where “ala” does not change the semantic space of the noun phrase at all. I additionally think that using the emphatic particle “a” is a cop out since “a” is treated specially in the grammar. Me and other toki ponaists across the Discords talked over some other interesting rules to consider, such as</p>

<p>\[(\forall A,B \in \text{NounPhrases})\;\;\operatorname{ref}(A\;B) = \operatorname{ref}(A) \cap \bigcup_{\forall x \in \text{NounPhrases}}\operatorname{ref}(X\;B) \]</p>

<p>but these rules and their (similarly surprising) effects may be outside the scope of this blog post.</p>

<p>o tawa pona.</p>

<hr />
<p>* Any noun phrases flipped like this would be the devil reincarnate.</p>]]></content><author><name></name></author><category term="toki-pona" /><category term="100-blog-posts" /><summary type="html"><![CDATA[toki pona is a tiny conlang created by Sonja Lang. I am not here to write a toki pona tutorial, that has been done a hundred times over by many people a hundred times smarter than I am.]]></summary></entry></feed>