<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="https://idiomdrottning.org/feed.css"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Idiomdrottning</title>
  <subtitle type="xhtml">
    <div xmlns="http://www.w3.org/1999/xhtml">
      <ol><li><a href="/blog">/blog</a></li>
      <li><a href="/blog/en">/blog/en</a></li>
      <li>/blog/programs</li>
      <li><a href="/blog/programs/en">/blog/programs/en</a></li>
</ol>
    </div>
  </subtitle>
  <link rel="self" href="https://idiomdrottning.org/blog/programs"/>
  <updated>2026-04-06T10:27:16+02:00</updated>
  <id>https://idiomdrottning.org/blog/programs</id>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/butlerian-hypocrisy"/>
    <id>https://idiomdrottning.org/butlerian-hypocrisy</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/butlerian-hypocrisy">My Butlerian hypocrisy</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>In the Butlerian Jihad<small> (from Dune but popularized by many smolnet posters like Alex Schroeder)</small> we rightly hate bots and scrapers but I’m in a bit of a <a href="https://en.wiktionary.org/wiki/throw_stones_in_a_glass_house" title="throw stones in a glass house - Wiktionary, the free dictionary">glass house</a> around that, since I’ve made a few scrapers for my own personal use as a way to get <abbr title="I used to think Atom was a competitor/successor to RSS but now that the 'Atom' name got more asnociated with that janky text editor, and RSS han remained a catchy and distinct name, I prefer to think of Atom as a version of RSS">RSS Atom</abbr> feeds out of sites that don’t have feeds. I love scraping and mashing.♥︎ The JS-laden SPA era was a nightmare for me. I hate browsers and server-side styling. I love getting texts from URLs.</p>

<h2 id="follow-ups">Follow-ups</h2>

<p><a href="gemini://carcosa.net/journal/20260326-re-butlerian.gmi">An Inhabitant in Carcosa responds</a>:</p>

<blockquote>
  <p>Bad in intent: it is intended to do something unethical, whether that be LLM training, denial of service, privatizing the commons, or immanentizing the eschaton. This is pretty subjective in an “I know it when I see it” kind of way. Scraping for a search index, scraping for a full-text RSS feed, and scraping for LLM training are all the same act as far as the server can tell, but only the last one is /evil/.</p>
</blockquote>

<p>Having a full-text RSS feed as a way to not have to deal with ads or paywalls—even when the reasons to not be able to otherwise handle ads and paywalls are 100% a11y issues—goes against the intent of the server owners.</p>

<p>And <a href="/fun-and-good">I’m not so sure LLMs are evil</a>.</p>

<blockquote>
  <p>It may ignore robots.txt, it may lie about being another user-agent</p>
</blockquote>

<p>Have done both those too!</p>

<blockquote>
  <p>Either bad intent or bad implementation is enough; a bot doesn’t need both to be bad.</p>
</blockquote>

<p>That’s <a href="/iorist" title="Iorist ethics">not exactly my philosophy</a>.</p>

<p>I love the open readable simple web where each document has one URL and you can read it on your own terms. I can’t deal with the junk web.</p>

        </div>
      </div>
    </content>
    <updated>2026-03-25T11:48:41+01:00</updated>
    <link href="https://idiomdrottning.org/butlerian-hypocrisy"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/cbv-push"/>
    <id>https://idiomdrottning.org/cbv-push</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/cbv-push">Call by value but the value is two pointers</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>With <code>push!</code> from <a href="https://wiki.call-cc.org/eggref/5/miscmacros" title="miscmacros - The CHICKEN Scheme wiki">miscmacros</a> you can cons onto that particular
name whereas with <code>mutate-cons!</code> defined like this:</p>

<pre><code>(define (mutate-cons! val lis)
  (set-cdr! lis (cons (car lis) (cdr lis)))
  (set-car! lis val))
</code></pre>

<p>You can change all instances of that particular list. Super dangerous.♥︎</p>

<p>Here, I’ll show you:</p>

<pre><code>(let* ((abc '(a b c))
       (abc2 abc)
       (fish '(f i s h))
       (fish2 fish))
  (push! 'nope abc)
  (mutate-cons! 'yeah fish)
  (list abc abc2 fish fish2))
</code></pre>

<p>Returns this:</p>

<pre><code>((nope a b c) (a b c) (yeah f i s h) (yeah f i s h))
</code></pre>

<p>That’s not a slag on <code>push!</code> which is more often what you want. It’s a
macro modifying what one particular <em>name</em> points to. Whereas
<code>mutate-cons!</code> is a procedure that changes the actual underlying
object.</p>

<p>Also <code>mutate-cons!</code> doesn’t work on the empty list. You can only push
onto lists with at least one element. They need to be actual cons pairs.</p>


        </div>
      </div>
    </content>
    <updated>2025-12-08T23:51:27+01:00</updated>
    <link href="https://idiomdrottning.org/cbv-push"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/scheme-do"/>
    <id>https://idiomdrottning.org/scheme-do</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/scheme-do">Scheme Do</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>I thought the <code>do</code> in in Scheme was a li’l hard to learn since the
examples I could find was a li’l too fancy and clever. Just like a lot
of my own documentation often is; sorry about that.</p>

<pre><code>(do ((a 0 (add1 a))
     (e 13)
     (i 7 (sub1 i)))
    ((zero? i) (print "ended ") (print a))
  (print "game cube")
  (print e)
  (print  a " wii"))
</code></pre>

<p>The first argument is a list of bindings like a let. But if you put in
three things they will be rebound every round. Like in the above
example, <code>a</code> will be rebound to <code>(add1 a)</code>, and <code>i</code> will be rebound to
<code>(sub1 i)</code>. And you can also put in just one thing like the example
here just binds <code>e</code> to 13 and then it just stays that.</p>

<p>The second argument is a list that starts with the ending condition
and the rest of the list are statements that will be evaled after.</p>

<p>And then all the remaining arguments are evaled every round.</p>

<p>Yes, this is backwards that the ending stuff comes before the main progn body.</p>

<p>Here’s a more basic example to just print happy birthday four times:</p>

<pre><code>(do ((i 4 (sub1 i)))
    ((zero? i))
  (print "happy birthday"))
</code></pre>

<p>Although for simple things like that, Chicken has <code>dotimes</code>:</p>

<pre><code>(dotimes (i 4) (print "happy birthday"))
</code></pre>

<p>Both <code>do</code> and <code>dotimes</code> are pretty side-effects oriented but in both cases you <em>can</em> put in a return value:</p>

<pre><code>(do ((i 4 (sub1 i)))
    ((zero? i) 'my-return-value)
  (print "happy birthday"))

(dotimes (i 4 'my-return-value) (print "happy birthday"))
</code></pre>

<p>And that return value can access your do scoped binds.</p>

<p>The same miscmacros that gives us <code>dotimes</code> also has the even simpler <code>(repeat 4 (print "happy birthday"))</code>.</p>

<p>I refer to my own <a href="/fold-maps" title="A fold is a map">fold write-up</a> all the time (the
<a href="/named-let-fold" title="Named Let → Fold">folds as named lets</a> version hasn’t been as useful) and maybe with
this, I can use <code>do</code> and <code>dotimes</code> more instead of making let loops
and consing up unnecessary iotas.</p>


        </div>
      </div>
    </content>
    <updated>2025-12-02T20:02:38+01:00</updated>
    <link href="https://idiomdrottning.org/scheme-do"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/aoc25"/>
    <id>https://idiomdrottning.org/aoc25</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/aoc25">Advent of Code, 2025</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Here is where I’ll post my solutions to <a href="https://www.adventofcode.com" title="Advent of Code 2025">Advent of Code</a> using zshbrev.
Spoilers ahead, and no promises that I’ll make it through the entire 12 days.</p>

<h2 id="date_2025-12-01_15:05">Dec 1st</h2>

<pre><code>(define (turn)
  (define-parameters zero 0)
  (fold
   (fn
    (with-result
     (when
         (zero?
          (save
           (modulo
            (+ (string-&gt;number
                (strse x "L" "-" "R" "")) y) 100)))
       (zero (add1 (zero))))))
   50
   (read-lines))
  (zero))
</code></pre>

<p>Last time I attempted Advent of Code, I got tangled up modifying the
step one solutions to handle step two and then I ended up wanting
to revisit step one but they were gone. So this year I’m going to
try to paste a second copy before modifying and I hope that works out
better.</p>

<pre><code>(define (dial acc r d)
  (with (+ d r)
    (when
        (or
         (zero? it)
         (= 100 it)
         (&lt; it 0 d)
         (&lt; d 100 it))
      (acc))
    (modulo it 100)))

(define (dial acc (? (fn (&lt; x -100)) r) d)
  (acc)
  (dial acc (+ r 100) d))

(define (dial acc (? (fn (&lt; 100 x)) r) d)
  (acc)
  (dial acc (- r 100) d))

(define (dial acc (? string? x) y)
 (dial acc (string-&gt;number (strse x "L" "-" "R" "")) y))

(define (turn)
  (define-parameters zero 0)
  (fold (c dial (fn (zero (add1 (zero))))) 50 (read-lines))
  (zero))
</code></pre>

<p>This was one of the hardest bugs I’ve ever had to debug.<br />
I’ve had to write log parsers, differs between different logs from
different versions, multiple implementations to check against each
other, Emacs highlight-regexp and count-matches and so on. It took
me ten tries on the Advent of Code website. I get paranoid that I
had mistyped my answer in there.</p>

<p>The line that now says <code>(&lt; it 0 d)</code>, originally I had it
as <code>(&lt;= it 0 d)</code> but it gave false positives on rotating from zero.</p>

<p>For a while I had it as <code>(and (&lt; it 0) (&lt;= 0 d))</code> which… doesn’t
fix that problem at all. Even after coming up with the fix <code>(&lt; it 0
d)</code>, that gives false <em>negatives</em> on rotating exactly one rotation
left from zero. But there’s no L100 in the data set? No, but my
code before I cleaned it up had:</p>

<pre><code>(dial acc (+ r 100)
      (dial acc -100 d))
</code></pre>

<p>where it now says:</p>

<pre><code>(acc)
(dial acc (+ r 100) d)
</code></pre>

<p>…leading to lots and lots of zero to zero turns which came with a
false positive in some versions and false positives in others.</p>

<p>A complete PEBCAK on my part but the hunt for the bug became a real
adventure of trying to sift through clues in logs that were
thousands of lines long.</p>

<h2 id="date_2025-12-02_11:53">Dec 2nd</h2>

<pre><code>(define (valid? x) #t)

(define (valid? (= -&gt;list x))
  (-&gt;* x (split-at (/ (require even? (length x)) 2)) equal? not))

(define (sum-not-valids-in-range
         (=
          (fn
           (map string-&gt;number (string-split x "-")))
          (current end)))
  (descend ((steps (- (add1 end) current)) current)
    (+ (if (valid? current) 0 current)
       (desc (sub1 steps) (add1 current)))))

(define (sum-not-valids)
  (-&gt;&gt;
   (-&gt; (read-string)
       (string-split ",\n"))
   (map sum-not-valids-in-range)
   (reduce + 0)))
</code></pre>

<p>After adapting that same idea to part two with a few minor tweaks,
it’s too slow! Works fine with the example data but not the full
input. I hate it when I have a working solution that
I really like beacuse it does something clever but have to write a
whole new one that’s faster. This is a “Project Euler” type
problem where I need to come up with a math solution instead of
just list procressing. But then I don’t really hate it because I
did come up with a good solution.</p>

<p>Inverting the puzzle by making an <code>is-in-any-range?</code> predicate and
then we can generate all invalid numbers up to the ceiling and see
if they’re in any range.</p>

<pre><code>(define (is-in-any-range? x)
  (or
   (&lt;= 1 x 19)
   (&lt;= 51 x 69)
   (&lt;= 72 x 85)
   (&lt;= 86 x 113)
   (&lt;= 411 x 466)
   (&lt;= 525 x 652)
   (&lt;= 660 x 782)
   (&lt;= 859 x 1056)
   (&lt;= 1626 x 1972)
   (&lt;= 2768 x 3285)
   (&lt;= 4002 x 4783)
   (&lt;= 4919 x 5802)
   (&lt;= 7025 x 8936)
   (&lt;= 9096 x 10574)
   (&lt;= 13004 x 15184)
   (&lt;= 32138 x 36484)
   (&lt;= 48548 x 61680)
   (&lt;= 69302 x 80371)
   (&lt;= 82984 x 100358)
   (&lt;= 126397 x 148071)
   (&lt;= 193276 x 237687)
   (&lt;= 266408 x 302255)
   (&lt;= 333117 x 414840)
   (&lt;= 431250 x 455032)
   (&lt;= 528410 x 680303)
   (&lt;= 726807 x 764287)
   (&lt;= 779543 x 880789)
   (&lt;= 907442 x 983179)
   (&lt;= 2558912 x 2663749)
   (&lt;= 5117615 x 5149981)
   (&lt;= 7702278 x 7841488)
   (&lt;= 9231222 x 9271517)
   (&lt;= 13413537 x 13521859)
   (&lt;= 32295166 x 32343823)
   (&lt;= 49829276 x 50002273)
   (&lt;= 67606500 x 67729214)
   (&lt;= 99990245 x 100008960)
   (&lt;= 146086945 x 146212652)
   (&lt;= 4747426142 x 4747537765)
   (&lt;= 5552410836 x 5552545325)
   (&lt;= 5858546565 x 5858614010)
   (&lt;= 7454079517 x 7454227234)
   (&lt;= 8764571787 x 8764598967)
   (&lt;= 9999972289 x 10000034826)))
</code></pre>

<p>Okay, great! I checked that there’s no overlapping ranges in this
particular data set. That means we can make an idempotent summer so
we don’t add the same number twice.</p>

<pre><code>(define summer (memoize (call-key* proc: + initial: 0)))
</code></pre>

<p>Now for a generator. The spine is just incrementing the numbers and
the ribs are repeating them.</p>

<pre><code>(define roof (biggest 2558912 2663749 1 19 72 85 82984 100358 86 113
                      193276 237687 51 69 779543 880789 13004 15184 2768 3285 4002 4783
                      7702278 7841488 7025 8936 5858546565 5858614010 5117615 5149981 4919
                      5802 411 466 126397 148071 726807 764287 7454079517 7454227234 48548
                      61680 67606500 67729214 9096 10574 9999972289 10000034826 431250
                      455032 907442 983179 528410 680303 99990245 100008960 266408 302255
                      146086945 146212652 9231222 9271517 32295166 32343823 32138 36484
                      4747426142 4747537765 525 652 333117 414840 13413537 13521859 1626
                      1972 49829276 50002273 69302 80371 8764571787 8764598967 5552410836
                      5552545325 660 782 859 1056))

(define (add-all-repeats seed big-number) (void))

(define (add-all-repeats seed number)
  (with (require (c &gt; roof) (string-&gt;number (conc number seed)))
   (when (is-in-any-range? it) (summer it))
   (add-all-repeats
    seed it)))
</code></pre>

<p>Let’s hard code it to five-digit numbers which is okay for this
particular input.</p>

<pre><code>(define (generate-the-answer)
  (do ((num 1 (add1 num)))
      ((&lt; 100000 num) (summer))
    (add-all-repeats num num)))
</code></pre>

<p>Okay, that’s a relief! Today was a lot easier to debug. I
originally had the summer see the numbers even before they were
repeating. But that bug was easy enough to find and fix.</p>

<!-- And for true nerds checking the source code and my git repo
you might see that I've kept on editing this one even long since I
had a working answer. If anything I should be tweaking yesterday's
entry since that one is more embarassing. But there's just
something fun in polishing something that I'm already proud of. -->

<h2 id="date_2025-12-03_09:24">Dec 3rd</h2>

<p>Okay, here we have a similar dilemma of “extracting” vs building up
possible joltages and filtering for them like <code>(strse? "9.*9")</code>.
Maybe if I start with extracting, that will still be useful as a
fallback for any stragglers after a filtering solution.</p>

<pre><code>(define (extract bank)
  (with (find-tail
         (is?
          (biggest (butlast bank))) bank)
    (list (car it) (biggest (cdr it)))))

(define (sum-joltages) (fold (fn (+ ((as-list extract) x) y)) 0 (read-list)))
</code></pre>

<p>Okay, that worked fine. I’m always remarkably bad at predicting
what step two is gonna be. I feel like I’m gonna try extracting for
step two also.</p>

<pre><code>(define ((extract amount) bank)
  (with (find-tail
         (is?
          (biggest (drop-right bank amount))) bank)
    (cons (car it) ((extract (sub1 amount)) (cdr it)))))

(define ((extract 0) bank) (list (biggest bank)))

(define (sum-joltages) (fold (fn (+ ((as-list (extract 11)) x) y)) 0 (read-list)))
</code></pre>

<p>That worked! Weird feeling how Monday took all day because I was
chasing a bug and even Tuesday took more than an hour, maybe closer
to three hours, but this one my idea worked right away and the
solution for part 1 was also the right direction for part 2. I
lucked out! And/or am actually good at programming especially when
it’s straight-forward list-processing like this.</p>

<h2 id="date_2025-12-04_10:27">Dec 4th</h2>

<p>This time around<small> (it’s my second time attempting Advent of
Code; I tried it in 2023 but quit before the end)</small> I’m
paying more attention to the story and I’m really getting into the
Matt Groening–like shenanigans.</p>

<p>As for the puzzle, this type of 2d, maps-and-neighbors stuff is
something I don’t have as much of a standard library for. SRFI-1
doesn’t really cover it so I’m starting more from scratch here and
I’m buckling in, accepting that it might take a li’l more time and
what write here I’ll get use out of later too. I actually thought
to work a li’l bit ahead and look up some array stuff in the latter
SRFI’s but then I didn’t have time to do that in November.</p>

<pre><code>(define (count-accessible)

  (define nodes (call-list (map call-string (read-lines))))

  (define (get-node x y) (void))

  (define (get-node x y)
    (handle-exceptions exn (void)
                       ((require procedure?
                                 (nodes (require (c &lt; -1) y)))
                        (require (c &lt; -1) x))))

  (define (get-neighbors x y)
    (parse (c apply get-node)
           (list-ec (: dx -1 2)
                    (: dy -1 2)
                    (if (not (= 0 dy dx)))
                    (list (+ x dx) (+ y dy)))))
  (let ((width (length (nodes)))
        (height (string-length ((nodes 0)))))

    (sum-ec (: x 0 width)
            (: y 0 height)
            (if (eq? #\@ (get-node x y)))
            (if (&gt; 4 (count (is? #\@) (get-neighbors x y))))
            1)))
</code></pre>

<p>Okay I like it when it works first try because I hate to put in
more than one guess but this was right. Good. Also didn’t have any bugs.</p>

<p>Now onto part 2. I really have to give Advent of Code a stern
scolding when it comes to accessibility: the dark grey text on dark
grey background is really really really hard to read so I use
einkbro’s light mode but that mode didn’t show the highlighted @
signs in the part 2 example. I had to toggle off the mode but then
I almost can’t see anything on the screen. Bad bad elves!</p>

<p>But okay, I figured out from what the text says what to do.</p>

<pre><code>(define (count-accessible)

  (define nodes (call-list (map call-string (read-lines))))

  (define (get-node x y) (void))

  (define (get-node x y)
    (handle-exceptions exn (void)
                       ((require procedure?
                                 (nodes (require (c &lt; -1) y)))
                        (require (c &lt; -1) x))))

  (define (get-neighbors x y)
    (parse (c apply get-node)
           (list-ec (: dx -1 2)
                    (: dy -1 2)
                    (if (not (= 0 dy dx)))
                    (list (+ x dx) (+ y dy)))))

  (let* ((width (length (nodes)))
         (height (string-length ((nodes 0))))
         (get-accessible
          (lambda ()
            (sum-ec (: x 0 width) (: y 0 height)
                    (if (memq (get-node x y) '(#\x #\@)))
                    (if (&gt; 4 (count (fn
                                     (memq x '(#\x #\@)))
                                    (get-neighbors x y))))
                    (begin
                      ((nodes y) x #\x)
                      1)))))
    (descend ((accessible (get-accessible)))
      (do-ec (: x 0 width) (: y 0 height)
             (if (eq? #\x (get-node x y)))
             ((nodes y) x #\.))
      (+ accessible (desc (get-accessible))))))
</code></pre>

<p>Okay. That worked. No wrong entries today either which always feels
great. I could spot my bugs on the example output. The bug today
was that while I realized right away that I need to count X as
neighbors, I forgot that I needed to count X as self too. So I was
done in a li’l less than an hour (three quarters rather) which is
fine. More than yesterday but that’s OK. I had to implement all
this 2D neighbors stuff. I liked the idea of using <code>parse</code> since it
just elides voids.</p>

<h2 id="date_2025-12-05_08:57">Dec 5th</h2>

<pre><code>(define ((in-ranges? ranges) ingredient)
  (any (fn (&lt;= (first x) ingredient (second x))) ranges))

(define (count-fresh)
  (receive (ranges ingredients)
      (break number?
             (with (read-list)
               (strse* it
                       (: (=&gt; start integer) "-" (=&gt; end integer))
                       (conc "(" start " " end ")"))))
    (count (in-ranges? ranges) ingredients)))
</code></pre>

<p>Today was a real head-scratcher because it seemed to me part 1 is a
subset of December 2nd and part 2 is even easier than part one.
Then I realized that the difference is that unlike December 2nd,
this time our input data have overlapping ranges (something I
checked for on Dec 2nd but almost forgot to do here). I’m grateful
that the test input also did, or I would’ve wasted a guess on the
real thing. Joining the ranges is just a smop once you know that
it’s there.</p>

<pre><code>(define (join-ranges single) single)

(define (join-ranges (and ((had hadd) (nak nadk) . tail) (hd . tl)))
  (if (&lt;= nak hadd)
      (join-ranges (cons (list had (biggest hadd nadk)) tail))
      (cons hd (join-ranges tl))))

(define (count-fresh)
  (fold
   (fn
    (+ y 1 (second x) (- (first x)))) 0
   (join-ranges
    (sort
     (take-while
      list?
      (with (read-list)
        (strse* it
                (: (=&gt; start integer) "-" (=&gt; end integer))
                (conc "(" start " " end ")"))))))))
</code></pre>

<h2 id="date_2025-12-06_08:35">Dec 6th</h2>

<pre><code>(define (pivot table) (cons (map car table) (pivot (map cdr table))))

(define (pivot (? (c every null?) table)) '())

(define (cephaluate)
  (reduce + 0
          (map (o eval (c map string-&gt;read) reverse)
               (pivot
                (map string-split (read-lines))))))
</code></pre>

<p>Oh, wow, here’s what I’ve been dreading: an easy part 1 followed by
a seemingly completely different part 2!</p>

<pre><code>(define ((space-pad gl) str)
  (conc str (make-string (- gl (string-length str)) #\space)))

(define (cephaluate)
  (let* ((lines (read-lines))
         (gl (biggest (map string-length lines))))
    (reduce + 0
            ((over (eval
                    (map string-&gt;read
                         (cons* ((as-list list last) (car x))
                                ((as-list butlast) (car x))
                                (cdr x)))))
             (parse (?-&gt; string? (fn (if (strse? x "^ +$") (values close: open:) x)))
                    (append
                     (map list-&gt;string
                          (pivot
                           (map (o string-&gt;list (space-pad gl)) lines)))
                     (list close:)))))))
</code></pre>

<p>I live for this convoluted maps of maps of maps of maps stuff! Very
fun problem.</p>

<p>Uh but if I were to try to explain how my program works… Hmm. From the inside out:<br />
Reads all lines as lines.<br />
Adds extra spaces to the end so all lines are the same length.</p>

<p>Pivots the lists so columns become rows and rows become columns.</p>

<p>Then with Acetone’s parse I split the problems into their own
lists.</p>

<p>I split out the operator (that’s the <code>list last</code>, <code>butlast</code> stuff)
and put it first then read and eval each problem.</p>

<p>Then finally I sum all those answers up.</p>

<p>Didn’t have any bugs today.<small> I did put in two redundant
reverses that still gave me the right answer; I found them and
removed them after getting the star while making this write
up.</small></p>

<h2 id="date_2025-12-07_10:32">Dec 7</h2>

<pre><code>(define-parameters splits 0)

(define (tachyon-count (prev current next . beams))
  ((over
    (when (and (eq? x #\S) (eq? y #\.)) (current i #\S))
    (when (and (eq? x #\S) (eq? y #\^))
      (splits (add1 (splits)))
      (next (sub1 i) #\S)
      (next (add1 i) #\S)))
   (prev) (current))
  (tachyon-count
   (cons* current next beams)))

(define (tachyon-count (last exit))
  (splits))

(define (tachyon-count)
  (tachyon-count (map call-string (read-lines))))
</code></pre>

<p>Now this is what I’m talking about! This is the longest I’ve spent
on a part 1 so far. Even Dec 1st, which was my longest day, part 1
wasn’t where I got stuck. Here I knew what to do, it was just tricky
to keep track of everything. Now onto part two of this wonderful
puzzle!</p>

<p>After reading part two… what a let down! It’s just the
non-idempotent version. Although smopping that together on a
tired-brain day like today is easier said than done.</p>

<p>I apologize to the makers of Advent of Code for calling their hard
work a let down, it’s just that the non-idempotent “naively
recursive” version is what I almost wrote by accident for part 1. I
checked myself in time before making that version so actually
implementing it did take some time.</p>

<pre><code>(define ((list-&gt;indices pred) lis)
  (filter-map (fn (and (pred x) y)) lis (iota (length lis))))

(define (tachyon-count prev (next . beams))
  (if (memq prev next)
      (+
       (tachyon-count (sub1 prev) beams)
       (tachyon-count (add1 prev) beams))
      (tachyon-count prev beams)))

(define (tachyon-count last '()) 1)

(define (tachyon-count)
  (with
   (remove
    empty?
    (map (as-list (list-&gt;indices (complement (is? #\.)))) (read-lines)))
   (tachyon-count (caar it) (cdr it))))

(memoize! tachyon-count)
</code></pre>

<p>Before I thought to clean up the input it was hard to keep track of
everything (I had prev, current, next, blank lines, passing through
etc). And I had something that worked on the example input but was
too slow for the real input. So I started over and that’s the
version you see above. It introduced a bug (I forgot to pass
through beams at first) which required some creative logging to
find with ever-increasing indentation prefixes etc etc until the
new version finally worked on the example input. But it was <em>still</em>
too slow for the real input. And memoization fixed that and here we
are. All in all an extra hour or two.</p>

<p>The hardest problem yet after the “breathers” of 5th and 6th, but I
remember last time (2023) I was completely stumped on some problems
even after spending a day with a paper notebook just thinking and
thinking and so far we haven’t seen that. I remember back then
having to postpone some of the stars like “Okay I’ll get back to
this one later” and doing it in the evening or the next day or
something and this year I’ve just done both of them in the morning
except for the first day that did take all day.<small> (And what a
privilege to be able to work all day on a recreational
puzzle!)</small> Maybe it says more about how incredibly burnt out
I was after the apartment move back then than about the
difficulties of the puzzles.</p>

<p>Also this one felt more like a “knowledge test” than the preious
entries. I knew about the basics of recursion vs iteration,
idempotence vs shadowing, and the life-changing magic of
memoization. I know about those things from books like <abbr title="Structure and Interpretation of Computer Programs">SICP</abbr> and
<abbr title="Paradigms in AI Programming">PAIP</abbr>. It’s less
about me figuring out something clever and more about me having
book learning. That doesn’t feel super fair.</p>

<p>Maybe I should take this opportunity to share some of that book
learning: My part one solution went through every row once. That’s
why it’s fast. It’s an iterative solution. The part two solution
needs to go through every row for every beam splitter above it. It
branches over three trillion times. That is too slow for even my
super duper computer to figure out. But memoization, which in this
case means having a hash-table that stores the results it has seen
before, means that it doesn’t have to re-calculate subtrees it has
seen before. It becomes fast again.</p>

<p>memoize! the brev-separate version does work even through
match-generics (but it needs to be called after all the
definitions) and zshbrev (since the entire file is compiled).</p>

<h2 id="date_2025-12-08_19:50">Dec 8th</h2>

<p>I usually hope for a hard one but today I overslept or rather I had
a hard time falling asleep and it’s already like two hours past my
normal wake-up time and I have laundry day so I hope it’s an easy
one today.</p>

<pre><code>(define (read-csv) (map (o (strse* "[,\"]" " " ) list) (read-lines)))
</code></pre>

<p>I haven’t gotten to read the problem yet but I’m opening the wrong
windows, pasting the wrong files etc. This is gonna be a hard day
no matter how easy the problem is just from my own tiredness.</p>

<pre><code>(define (pyth a b) (sqrt (+ (* a a) (* b b))))

(define (distance (x1 y1 z1) (x2 y2 z2))
  (pyth (abs (- z1 z2))
        (pyth (abs (- x1 x2)) (abs (- y1 y2)))))
</code></pre>

<p>Note to self: It was way slower when or both of these was memoized
because they’re usually not called that often on the same inputs so
the lookup costs more than it saves. Also note to self: I could
save a little by removing the outermost sqrt. Orders of squares are
the same as the order of roots.</p>

<p>Aaand it’s a super hard problem. We’re in the back half now folks! The deep end!</p>

<p>Back after breakfast break. This is three problems (distance in 3D
space, keeping track of groups of circuits, and recursive pairwise
comparison) that each on their own would’ve been enough the past
week. I for one did not know how to check distances in 3D space so
I had to figure it out. (I did know how to check them in 2D space.)</p>

<pre><code>(define circuits (call-table))

(define (connect a b)
  (unless (memq a (circuits b))
    (with (append! (circuits a) (circuits b))
      (for-each (fn (circuits x it)) it))))
</code></pre>

<p>This one, <code>take-up-to</code> is a goody that I should get around to
putting in brev-separate. I use it all the time for RSS stuff. I’m
sure it’ll come in use for more than one day this challenge:</p>

<pre><code>(define ((take-up-to lim) lis)
  (take lis (min lim (length lis))))

(define (mutate-cons! val (and lis (hd . tl)))
  (set-cdr! lis (cons hd tl))
  (set-car! lis val))

(define (insert-sorted! val lis)
  (cond ((&lt;= (car val) (caar lis))
         (mutate-cons! val lis))
        ((null? (cdr lis))
          (set-cdr! lis (list val)))
        (else
         (insert-sorted! val (cdr lis)))))

(define spans (call-table))
</code></pre>

<p>This divides up the half a million distances into spans.</p>

<p>After getting my two stars I wanted to keep optimizing and I timed
it out that five or six was the fastest quotient. I started out
with a hundred but that’s way slower. Even seven is slower and so
is four. With five, we have 25909 spans (hash-table entries) with
an average list length of just under twenty each. That’s an
indictment of my fancy mutating cdr-setting <code>insert-sorted!</code>. But a
testament to the glory of hash-tables.♥︎</p>

<pre><code>(define (stash! contender)
  (with (quotient (floor (car contender)) 5)
    (this (spans it)
      (if that
          (insert-sorted! contender that)
          (spans it (list contender))))))

(define (connect-and-count limit)
  (define-parameter quitter limit)
  (define boxes (read-csv))
  ((over (circuits x (list x))) boxes)
  (pair-for-each
   (fn (with (car x)
         ((over
           (stash! (list (distance it x) it x)))
          (cdr x)))) boxes)
  (let/cc break
   (for-each
    (fn (for-each (fn (when (zero? (quitter)) (break 'ok))
                      (quitter (sub1 (quitter)))
                      (connect (second x) (third x))) (spans x)))
    (sort (hash-table-keys (spans)))))
  (with (sort (map length (delete-duplicates (hash-table-values (circuits)))) &gt;)
    (apply * (take (sort it &gt;) 3))))
</code></pre>

<p>Getting a solution that even works on the example of part one took
several hours<small> (the actually figuring out the three parts of
the problem took a long time and then I also had a bad bug in my
connect routine where it worked until I was connecting existing
networks)</small>. That solution was too slow for the real
input—and that was still on the first star! The first problem was
that I was running sort on all the distances and then took the
limit on that sorted list. Running sort post-hoc on half a million
entries was something I thought it would’ve been able to handle but
apparently not.</p>

<p>Then I made an insert-sorted that, functionally (pure shadowing)
inserted the new distances as I went instead of sorting them
post-hoc. That finally gave me an answer for part one’s full data
but it took several minutes to find the answer. So after reading
part two, I made the mutating insert-sorted! and also added the
spans. Initially I had spans by hundreds which gave me the answer
in about twelve seconds or so. Really strange to me that sorting
was the bottleneck but that’s what it was.</p>

<p>Now for part two:</p>

<pre><code>(define (connect a b)
  (unless (memq a (circuits b))
    (with (append! (circuits a) (circuits b))
      (for-each (fn (circuits x it)) it)))
  (length (circuits a)))

(define (connect-and-count)
  (define boxes (read-csv))
  (define boxl (length boxes))
  ((over (circuits x (list x))) boxes)
  (pair-for-each
   (fn (with (car x)
         ((over
           (stash! (list (distance it x) it x)))
          (cdr x)))) boxes)
  (let/cc break
   (for-each
    (fn (for-each (fn (when (= (connect (second x) (third x)) boxl)
                        (break (* (caadr x) (caaddr x))))) (spans x)))
    (sort (hash-table-keys (spans))))))
</code></pre>

<p>I had it down to “just” twelve seconds to sort all the half a
million distances, but then the connecting them all into one big
network was still too slow. I tried several other algoritms for
connect until I landed on the one I used above and I updated part
one to match also. Since this was largely an optimization puzzle, I
kept working on part one to make it faster making sure I’d still
get the right answer and then applying it to part two. So today I
didn’t submit any wrong answers either. It just took all day, is all.</p>

<p>I had a bunch of versions of connect which added all the boxes to
all the boxes one by one. One of those versions was bugged (before
I even had part one done). I felt like galaxy brain when I came up
with the <code>append!</code> solution since it was so different than anything I
had and such a Gordian shortcut. If some of y’all had that approach
figured out right away I salute you.♥︎ For me it took some time
getting there.</p>

<p>Wow!! I loved this puzzle! I had it ticking along slowly until I
figured out a new way to connect and now the connection part is
instant. I’m really proud of my solution. I ended up with getting
the distance sorting down to under two seconds and the connection
part to be instant. That’s a good optimization down from an
instance sorting that timed out, and then I got it down to twelve
seconds, so that then the connection part was what timed out. And
now the whole thing is done in two secs. I loved this puzzle. It
took all day but it was a day well spent and I learned a lot just
by experimenting, without looking things up<small> (beyond just
reading the docs for SRFI-1, SRFI-69 and the other libraries I was
using. Especially my own. I don’t consider it cheating to read my
docs I’ve written and posted to this website♥.)</small>.</p>

<p>Maybe this is backwards and <abbr title="results-oriented thinking">rotty</abbr> but I’m way more proud of spending a whole
day on the problem like today than when I solve it quickly.
Although as per ushe with me there’s a <a href="https://idiomdrottning.org/zone-of-suck" title="The Zone of Suck">zone of suck</a> where
spending a couple of hours is what I’m least proud of compared to a
fast solution or sticking to it all day.</p>

<h2 id="date_2025-12-10_00:09">Dec 9th</h2>

<p>I’m hoping for easy ones from here on out and at first glance it
seems like today delivered on that since I can use a similar
pairwise comparison as yesterday. Unlike yesterday where I couldn’t
figure out any easier way to count up the distances than to
actually pyth them out, here an idea immediately comes to mind
where I can discard candidates based on one or both axes and weed
things out considerably, but I’ll try the more brute force wasteful
approach first, maybe it’ll be enough.</p>

<pre><code>(define (carpet (ax ay) (bx by)) (abs (* (- ax -1 bx) (- ay -1 by))))

(define (all-squares)
  (with (read-csv)
    (biggest (list-ec (:list a it) (:list b it) (carpet a b)))))
</code></pre>

<p>Sloppy! For the first time in a while I entered a wrong input into
the website; I didn’t notice that my code gave the wrong answer on
the example input, I was just happy that it was fast enough on both
the example input and the actual input. It was, so I’m not gonna
have to do anything fancy at least for part one, but, uh, being
right is more important than being fast.♥︎ The bug was that I had
forgotten the <code>-1</code> above, counting the tile distances exclusive
rather than inclusive.</p>

<p>Okay so unlike yesterday where I thought part one was pretty
difficult on its own, here there’ a huge jump in difficulty by part
two, or rather, what I did for part two is not super relevant for
part two. But that’s okay. That’s why I like to get to part two
quickly so I can know what I’m really supposed to do.<small> (And
the fact that it’s often hard to predict is part of the fun of
Advent of Code.)</small></p>

<p>It’s just after midnight and I haven’t figured out the second part yet.</p>

<h2 id="date_2025-12-10_11:41">Dec 9th, continued</h2>

<p>Okay it’s the next morning! Back to yesterday’s problem before even
looking at the new problem. Most of the following was written
yesterday. I write and delete and write and delete, that’s my
workflow.</p>

<p>The red tiles are all inside the area 1837,1574 to 98308,98188 so
initializing a vector of vectors to fit that dies with OOM so
we’re gonna have to get fancy and procedural.</p>

<pre><code>(define (horizontal? line) #f)
(define (horizontal? ((ax y) (bx y))) #t)

(define (data-&gt;lines data)
  (partition!
   horizontal?
   (map (compose sort list) data (cons (last data) data))))

(define nodes (read-csv))

(define h-lines #f)
(define v-lines #f)

(define red? (call-table))
((over (red? x #t)) nodes)

(define ((connected-v-line? point)
         (start end))
  (or (eq? start point)
      (eq? end point)))

(define (bendy? (start end))
  (with
      (map second
           (list
            start
            (find (complement (is? start))
                  (find (connected-v-line? start) v-lines))
            (find (complement (is? end))
                  (find (connected-v-line? end) v-lines))))
    (or (= (second start) (biggest it))
        (= (second start) (smallest it)))))

(receive (hl vl)
    (data-&gt;lines nodes)
  (set! h-lines (map sort hl))
  (set! v-lines (map sort vl))
  (set! steppy-lines (remove bendy? h-lines)))

(define ((cross? (= sort ((mxl my) (mxh my))))
         ((gx gyl) (gx gyh)))
  (and (&lt; gyl my gyh)
       (&lt; mxl gx mxh)))

(define ((cross? (= sort ((mx myl) (mx myh))))
         ((gxl gy) (gxh gy)))
  (and (&lt; myl gy myh)
       (&lt; gxl mx gxh)))

(define ((cross? ((mx my) (mx my))) any) #f)

(define ((overlap? line) anything) #f)

(define ((overlap? ((mx myl) (mx myh)))
         ((gx gyl) (gx gyh)))
  (and
   (eq? mx gx)
   (&lt; myl gyl gyh myh)))

(define ((overlap? ((mxl my) (mxh my))) ((gxl gy) (gxh gy)))
  (and
   (eq? my gy)
   (&lt; mxl gxl gxh mxh)))

(define (inside? point)
  (or (red? point)
      (with (list (list 0 (second point)) point)
        (odd?
         (+ (count (cross? it) v-lines)
            (count (overlap? it) steppy-lines))))))

(define-parameters best 2 heck '())

(define ((small ungreen?) ax ay bx by)
  (ungreen?
   (min (add1 ax) bx)
   (min (add1 ay) by)
   (max (sub1 bx) ax)
   (max (sub1 by) ay)))

(define ((normalize ungreen?) ax ay bx by)
  (ungreen? (min ax bx) (min ay by)
          (max ax bx) (max ay by)))

(define (ungreen? ax ay bx by)
  (or
   (any (cross? `((,ax ,ay) (,ax ,by))) h-lines)
   (any (cross? `((,bx ,ay) (,bx ,by))) h-lines)
   (any (cross? `((,ax ,ay) (,bx ,ay))) v-lines)
   (any (cross? `((,ax ,by) (,bx ,by))) v-lines)
   (not
    (every inside? `((,ax ,ay) (,bx ,ay) (,ax ,by) (,bx ,by))))))

(define (square-size ax ay bx by)
  (* (add1 (- bx ax)) (add1 (- by ay))))

(define (carpet (ax ay) (bx by))
  (with ((normalize square-size) ax ay bx by)
    (unless
        (or
         (&lt; it (best))
         ((normalize ungreen?) ax ay bx by)
         ((normalize (small ungreen?)) ax ay bx by))
      (best it)
      (heck `((,ax ,ay) (,bx ,by))))))

(define (path-format ((ax ay) (bx by)))
  (conc "M " (/ ax 1000.0) " " (/ ay 1000.0)
        "H " (/ bx 1000.0) " V " (/ by 1000.0)
        "H " (/ ax 1000.0) "Z"))

(define (all-squares)
  (do-ec (:list a nodes) (:list b nodes) (carpet a b))
  (print "The answer " (best))
  (print "which looks like this " (path-format (heck))))
</code></pre>

<p>Oh no I give up on this for now. So heartbroken.</p>

<p>I painted the red and green tiles both green in this image and
superimposed my program’s best solution as a black rectangle on
top.</p>

<p><img src="/theater-carpet.svg" alt="Their floor" /></p>

<p>But it’s not accepted as the right answer. I can’t see a better
answer with my own eyes either. So I’m leaving this as a one star
and I’m just that much closer to giving up on the entire Advent of
Code. I obsess over it, I spend all day on it in this horrible
hyperfocused state. Other activities like playing games with
friends or eating food become stress isntead of joy. And to boot
I’m still not smart enough to actually solve the problems!</p>

<p>I’ll go and take a look at Dec 10th’s problem.</p>


        </div>
      </div>
    </content>
    <updated>2025-12-01T15:06:05+01:00</updated>
    <link href="https://idiomdrottning.org/aoc25"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/emacs-font-fun"/>
    <id>https://idiomdrottning.org/emacs-font-fun</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/emacs-font-fun">Emacs Font Fun</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>I actually love fonts!<small> (Which might surprise those who know that I leave my webpage set to the browser default font, but it’s <em>because</em> I love good fonts so much that I think fonts should be a reader decision, not a server decision.)</small> When my desktop went headless after having to move to a way smaller apartment and my Emacs had to live in SSH only, I was sad because I had set up all kinds of weird fonts in Emacs and functions to switch the entire Emacs over along with “only switch this particular buffer over” variants. E.g.</p>

<pre><code>(defun to-june ()
  (interactive)
  (set-frame-font "Junicode-32"))

(defun to-june-b ()
   (interactive)
   (setq buffer-face-mode-face '(:family "Junicode" :height 320))
   (buffer-face-mode))
</code></pre>

<p>Along with this to make a changed buffer go back to the frame-wide font:</p>

<pre><code>(defun unbufface ()
  (interactive)
  (buffer-face-mode -1))
</code></pre>

<p>And now I have all those fun fonts working again on the Android version of Emacs! ♥︎♥︎♥︎</p>

<p>Hence the huge sizes. I was on 12 for most non-Junicode fonts with a -16 “big” option, while Junicode with its lower x-heights I had at 14 pts. But on “Moria”, I use 28 as the “small” size, 34 as the big size and Junicode gets to be 32.</p>

<p>My default font for coding and general emacsing around was (CW non-free) Futura while I wrote most prose texts with Junicode. Coding in proportional?! A geometric sans with a super ambiguous character set for I1O0? Well, it works great for Lisp for the most part and I had it set up so I could super easily toggle out from it back into Deja Vu Mono, or Fira Code these days.</p>

<pre><code>(add-to-list 'default-frame-alist '(font . "Futura LT-28"))
</code></pre>

<p>I set this all of this up pretty soon before having to switch away from it so I only got to enjoy it for a few months so I’m grateful that I have it back albeit not on my big 21″ 4:3 screen. It’s on a more cozy and puny lantern-lit 7″ screen. Last piece of the puzzle is that I’m gonna go find a retro-ish typewritery font. Old Timey Code maybe. I was on “Bohemian Typewriter” but I couldn’t get that to work on since the <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Android-Fonts.html" title="Android Fonts (GNU Emacs Manual)">Android version of Emacs don’t support OTF</a>. And good riddance since the latin-1 coverage was so bad, but also not good riddance since the alternatives I’ve found so far are more consistenly x-spaced instead of nostalgically jittering around like an X-File on uppers.</p>

<p>I really like switching fonts with the same text open. It helps me get fresh eyes on the same text and see different problems or things I can write in a more beautiful or clear way.</p>


        </div>
      </div>
    </content>
    <updated>2025-11-15T16:59:50+01:00</updated>
    <link href="https://idiomdrottning.org/emacs-font-fun"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/dsssl-with-varargs"/>
    <id>https://idiomdrottning.org/dsssl-with-varargs</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/dsssl-with-varargs">DSSSL with varargs</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>This is so jank but I was frustrated with how <a href="https://wiki.call-cc.org/man/4/Extensions%20to%20the%20standard#extended-dsssl-style-lambda-lists" title="Extended DSSSL style lambda lists - The CHICKEN Scheme wiki">the rest parameter redundantly has all the keywords left in</a> (unlike <code>stragglers</code> in <a href="/define-options" title="define-options">define-options</a>) so I did a grobian solution at least for now:</p>

<pre><code>(define (strip-keys keys) (fn (strip-keys x keys)))

(define (strip-keys lis keys) (cons (car lis) (strip-keys (cdr lis) keys)))

(define (strip-keys (kw val . lis) keys)
  (strip-keys (require (member kw keys) lis) keys))

(define (strip-keys () keys) '())
</code></pre>

<p>And then here’s an example of it in use:</p>

<pre><code>(define (lsrss #!rest files #!key (append #f))
  (set! files (strip-keys files '(#:append)))
  (...))
</code></pre>


        </div>
      </div>
    </content>
    <updated>2025-10-27T09:40:48+01:00</updated>
    <link href="https://idiomdrottning.org/dsssl-with-varargs"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/somle"/>
    <id>https://idiomdrottning.org/somle</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/somle">Chatbot the ultimate social media feed</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<blockquote>
  <p><strong>coffeetree:</strong> the chatbot paradigm is literally an infinite feed<br />
<strong>baconlad:</strong> hard disagree on this.  Its not feeding you content.<br />
<strong>coffeetree:</strong> That’s like saying “I only use this one tiny feature of Facebook, and no other features, and no other platforms, and I handle that paradigm just fine.”</p>
</blockquote>

<p>And some people do say that. I think both sides are right here in that yes it is a useful tool and you get to avoid a lot of the bad parts of the web, but also, it is addictive and like how many silo social media sites it adapts to you to keep you hooked, and also, the environmental and economic impact is huge. The <a href="/ml" title="Machine Learning—good and bad arguments against">increased wealth gaps and increased concentration of ownership of means of production</a> is an undeniable ginormous downside of the current slate of LLM and ANN tech. Our economy was already <a href="/externalities" title="Externalities">a broken buggy system</a> even before the gigaton wrench thrown into it by ML. 😭</p>

<p>They <a href="/the-answer" title="The Answer">in some very real semiotic senses</a> are different, yes. But the similarites are that they are these huge data center machines that take all the input from what people post online and chew it up and push it back to you in a way that they control and can profit from. AI is in someways even worse, an even more purely distilled version of the idea of The Algorithm.</p>

<p>Now, that’s social media in the sense of algorithmic silo socials like TikTok, X, Tumblr, Facebook, Instagram, YouTube and similar. Not all social media is algorithmic (Discord isn’t, although it is a silo) nor is all social media even silos; email and fedi are examples of non-silo socials.</p>

<p>And by “algorithm” we mean specifically recommendation/discovery algorithm; obviously all computer stuff uses running code i.e. “algorithms” in a much broader and gentler sense. Even a 1980s BBS “had algorithm” since it used computer programs to work but in a more specific sense of “algorithm”, we mean the reco engine.
The hideous world-wrecking reco engine. And AI chatbots keep that part, the bad part. They just remove the part of it that were your friends.</p>

<p>The comparison of “chatbot as the ultimate social media feed” (in a negative sense) is stretched but insightful and I appreciated it.</p>

<p>That said, one very real difference is that while there is some amount of <a href="/vendor-lock-in" title="Vendor Lock-in">vendor lock-in</a>, it doesn’t hold your friendships hostage the way silo socials do.</p>


        </div>
      </div>
    </content>
    <updated>2025-10-21T15:09:27+02:00</updated>
    <link href="https://idiomdrottning.org/somle"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/om-delta-chat"/>
    <id>https://idiomdrottning.org/om-delta-chat</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/om-delta-chat">Lite irrtankar om Delta Chat</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Jag använder Delta Chat med min egen mejlserver och kopplat till min vanliga mejladdress så jag kan också “chatta” med vilken mejladdress som helst på hela jorden utan att behöva försöka övertala dom att installera samma chatapp som mig; dom använder bara sin vanliga mejlapp.
Mejl från myndigheter, föräldrar, randos online; allt dyker upp som chatmess och jag kan svara direkt på ett ursmidigt sätt och få överblick över tidigare meddelanden i samma tråd bara genom att scrolla upp.
Och behöver jag nåt mer fancy får jag kavla upp ärmarna och starta den vanliga mejlappen jag också har installerta och kopplat till samma konto.</p>

<p>Det går inte om man har en chatmailserver för att dom har stängt av alla vanliga mejl ut, i nån sorts försöka att minska spam (men det går ju fortfarande att spamma andra deltachatanvändare så jag är inte helt nöjd med hur dom tänkte där).</p>

<p>Jag hoppas Delta Chat fortsätter värna om kompabiliten med vanlig email och inte lägger alla ägg i sin chatmailkampanj. Idén med att koppla ihop sig via chatmailservrar har inga egentliga fördelar gentemot ex vis XMPP/OMEMO bara att det finns ännu färre servrar och ännu färre användare och man ramlar i det klassiska nätverkseffektsproblemet att för dom flesta av oss går det inte att   Oövertala släkt &amp; vänner att joina ytterligare en app och vice versa, att bli övertalad till det, är ännu tristare.</p>

<p>Det som är fint med att det är email är ju att det går att chatta med alla så länge dom har en mejladdress och valfri mejlapp. Ja, jo, en del personer har en aversion mot mejl och och kommer vägra svara en och säga att mejl är nåt dom bara har för brev från mormor, skatteverket, och återställa lösneordet (och dom har jag ju försökt tipsa om Delta Chat men en del vill inte det, se nätverkseffektsproblemet ovan) men bortsett från dom. Det gör det dessutom bra om båda sitter på ex vis gmail och deltachattar med varandra och nån vill stoppa den konverslationen så vad ska dom göra? Dom behöver stänga ner hela Google. Det går alltså att “snylta” på den vanliga epostinfrastrukturen och ha det till chattande.</p>

<p>Delta Chat stöder läsning av alla sorters PGP men för att skicka ut med e2ee-kryptering går det bara om den du pratar med använder Autocrypt och du fått minst ett Autocrypt-mejl från dom innan. Det gör också att chatmails “vi raderar automatiskt alla icke-e2ee–mejl” policy blir lite awkward för innan så var det ju bara att börja med lite <a href="https://nohello.net" title="no hello">hej hej</a> och sen blev det krypterat från och med det.</p>

<p>Ex vis hade K9 stöd för Autocrypt, inte från början men det fanns en inställning (misstänker att Mozilla kommer att ta bort det för dom är fiender till Autocrypt och dom tog bort det ur Thunderbird). Posteo har också det, dom kan lägga till Autocrypt-headers automatiskt på alla utgående mejl när du väl lagt in din nyckel. Men för chatmailanvändare betyder det i båda fallen att den andra personen måste skicka första mejlet så du får deras nyckel. Och när det är chatmail till chatmail som ska börja prata måste dom alltså numera ha nån sorts mellanlager för att starta kontakten. 🤦🏻‍♀️</p>

<p>Därför hoppas jag Delta Chat fortsätter att låta chatmail vara frivilligt så vi som använder det som en vanlig mejlapp kan fortsätta med det. <a href="https://idiomdrottning.org/pgp" title="Why it's OK that PGP sucks">För dom som vägrar PGP blir det ändå s2se</a> vilket är betydligt bättre än ex vis SMS. (Det är så frustrerande när folk på ex vis arbetsförmedlingen säger “vi får inte skicka tider över mejl för det är så osäkert, det är bäst att vi SMS:ar det istället”, det är galet, SMS är en miljon miljoner gånger osäkrare än normal jäkla TLS-epost vilket all epost numera är.</p>

<p>Delta Chats fokus på enbart Autocrypt är ofta lite frustrerande. Om jag får ett vanligt okrypterat mejl är det bara att svara (för mig som inte är fast på chatmail alltså utan fortfarande har det kopplat till en normal mejl) och om jag får ett mejl med Autocrypt är det också bara att svara, men om jag får ett mejl med PGP utan Autocrypt, då måste jag bita ihop och rulla upp den andra mer robusta men också mkt mkt osmidigare mejlappen.</p>

<p>Det gäller då dels nördar som handpillar med PGP som om det vore nittiotal, vilket är ett fåtal men jag får ändå några såna mejl i veckan, och dels dom som använder den stora konkurrenten till Autocrypt, nämligen WKD. Och det betyder Proton! Eftersom min hemmasnickrade mejlserver har slagit på <em>både</em> Autocript <em>och</em> WKD (vilket Posteo också numera har fast varje användare måste slå på det manuellt), betyder det att jag kommer få krypterade mejl automatiskt från varenda Protonanvändare. Och jag kan läsa dom i Delta Chat men måste som sagt komma ihåg att svara från min mer robusta meljapp. (Som heter <a href="https://idiomdrottning.org/emacs-basics" title="Emacs Basics">Emacs</a>.)</p>

<p>WKD är mycket bättre än Autocrypt för det funkar från första mejlet eftersom det är helt out of band och det är också mindre sårbart för MITM. Autocrypt har dock en stor stor fördel gentemot WKD och det är att det funkar med alla mejlservrar. Du kan sätta Autocrypt på dina gmail-mejl men det kommer aldrig gå att sätta WKD på dom om inte Google plötsligt själva bestämmer sig för att stödja det eller om du köper den där grejen där du får din egen domän på gmailen.</p>

<p>Jag har ändå tidigare föreslagit att Delta Chat ska stödja <em>både</em> WKD <em>och</em> Autocrypt så att så fort jag får ett mejl från nån som har WKD så ska jag kunna chatta krypterat med dom änven om dom inte har Autocrypt. Och nu med chatmail-kampanjen när man <em>ändå</em> behöver speciella servrar så gäller det ju dubbelt för <strong>varför har då inte chatmail-servrarna automatiskt WKD också!?</strong> Då skulle det funka sömlöst med Proton! (Tuta kan dra åt helvete men det vet förhoppningsvis alla vid det här laget.)</p>

<p>Anledningen till att dom drog igång chatmailkampanjen var gissnigsvis två anledningar. Dels att Hotmail blev inkompatiblet med Delta Chat (alltså jag kan fortfarande chatta s2se med hotmailanvändare men dom kan på sin sida inte köra Delta Chat) så min polare som använde Delta Chat med sin hotmailaddress fick byta till att göra det med gmail och det kanske inte kommer funka så länge till. Den andra tror jag är att om man blandar Delta Chat med mejl men vill bara ha Delta Chat till chattar och bara vanlig mejl till vanliga mejl så kommer chattarna poppa upp tillfälligt i den vanliga meljappen och sen försvinna vilket kan uppfattas som störigt. Medan jag som får och vill ha alla mejlen i båda apparna (med olika blocklistor, ex vis jag har blockat alla nyhetsbrev och sånt från Delta Chat <a href="https://idiomdrottning.org/nmatom" title="Notmuch Search to Atom Feed">för jag läser dom genom den vanliga meljappen istället</a>), jag hade inte det problemet. Men visst finns det buggar med min blandsetup. Delta Chat har nyligen infört en misfeature att det inte längre går att svara på mejlinglistemejl. Förut var det väldigt smidigt när nån på GitHub kommenterade på en PR så kunde jag svara direkt i Delta Chat men det har dom numera alltså slängt av vilket är otroligt ogint och onödigt. Read Only grejer i Delta Chat är det sämsta av båda världar eftersom det är en mycket dålig läsare + jag vill inte bli pingad av read-only saker utan vill läsa dom nån gång i veckan i lugn och ro + hela uspen med Delta Chat är ju hur smidigt det går att svara, som på en chatt för det <em>är</em> en chat!</p>

<p>Men sen ja deras implementation av PGP är väldigt robust. Min kompis som bytte sin Delta Chat från hotmail till gmail? Han är den enda som jag inte kan mejla från min vanliga mejlapp eftersom den inte stöder PGP-nycklar där epostadressen inte matchar vilket hans inte längre gör. Nu när min älskade tablet är på lagning använder jag den vanliga mejlappen över SSH från en laggig och seg e-ink–skrivmaskin och alla andra Delta Chat–polare kan jag skriva till men inte honom.</p>

<p>Och ja det går att ha stickers♥︎</p>

<p>Det går till och med att ha speciell appar som man skickar till varandra som typ zipfiler eller vad det är. Det heter webxdc. Jag bortsåg glatt från detta först för jag vill inte spela liksom spel och sånt i min mejl men sen hittade jag en jag gillar väldigt mycket och gärna använder ihop med en av mina Delta Chat-kompisar, <a href="https://codeberg.org/webxdc/calendar" title="webxdc/calendar: A mobile-friendly calendar for WebXDC - Codeberg.org">en kalender</a> som jag tycker är bra. Faast det var inte helt smart nu när jag för tillfället är hänvisad till att åter igen använda min vanliga mejl för att ha kontankt med honom och alla andra tills min tablet blir lagad.</p>


        </div>
      </div>
    </content>
    <updated>2025-10-21T10:27:51+02:00</updated>
    <link href="https://idiomdrottning.org/om-delta-chat"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/cursed-android"/>
    <id>https://idiomdrottning.org/cursed-android</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/cursed-android">Still cursed after all these years</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>It often strikes me as so wild, the fact that I’m typing this in a curses UI in 2025. I see this less as a vindication of the durability of the textual software stack (although it is) and more as a failure of the stack as the whole. [I hate laptops][Ihl] but Linux on the laptop and desktop was in a great place. I had my own fork of dwm<small> (hopefully I can still rescue that dwm code at some point in the distant future)</small> and I was running Emacs with proportional fonts and everything was peachy and even keen.</p>

<p>But iOS and Android, in conjunction with serverside apps like Facebook and Google Docs, wreaked havoc on that paradise and I can’t believe that Android now almost 20 years in is still in a Google-strangled hellscape of planned obsolesence e-waste devices. I’m typing this in curses Emacs because Android is so awful that I find myself preferring to SSH into a normal computer. Same with iPad. I might’ve been slightly more productive on iPad than on Android but only because iPad’s RDP apps and mosh apps were better than what I’ve found on F-Droid so far, i.e. it was better at letting me connect to a real normal computer running normal Linux like GNU intended.</p>

<p>The efforts to make tablets and phones that run some sort of abominable “mobile” Linux have my full blessing. That’s gonna have to be the only way out of this unless there’s a serious successful fork of Android soon to wrest it out of Google’s tentacles.</p>

<p>On a positively-accentuated note, the textual stack <em>is</em> pretty dang awesome. I started writing this as a post on Fediverse but halfway through decided to make it a blogpost instead and I could easily do that because I type those two kinds of posts the same way in the <a href="https://idiomdrottning.org/emacs-basics" title="Emacs Basics">same app</a>.</p>


        </div>
      </div>
    </content>
    <updated>2025-10-12T10:57:46+02:00</updated>
    <link href="https://idiomdrottning.org/cursed-android"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/ditt-jobb-vs-ai"/>
    <id>https://idiomdrottning.org/ditt-jobb-vs-ai</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/ditt-jobb-vs-ai">Ditt jobb kommer ersättas av AI</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Vi ser då och då listor i media med vilka jobb som AI kommer kunna ersätta.</p>

<p>Vet inte hur dom resonerar men så här tänker jag själv, att det kanske gäller
iframtiden om AI blir mycket bättre än hur det är nu, för <a href="/ml" title="Machine Learning—good and bad arguments against">nu är det
kasst</a>.</p>

<p>Men den framtiden kan komma väldigt snabbt och plötsligt.
Jag tycker att det är bra att sprida medvetenhet om hur idén med
“arbetsmarknad” är ganska konstig i en värld där vi bygger
arbetsbesparande maskiner.</p>

<p>Jag skulle gärna se att vi (gärna gradvis men snarast) går mot ett
radikalt annorlunda sätt att fördela resurser och arbetsuppgifter än
marknadstänket för det känns lite floppigt att när en arbetsmarknad
styr oss så kan vi som bäst uppfinna bättre redskap, mer effektiva
“spadar” och “hammare” och “krattor” som låter oss gräva djupare,
hamra hårdare, och kratta fortare, men inte skära in på den där
helvetiska 40-timmars–veckan, hur mycket verktyg vi än uppfinner.</p>

<p>Och eftersom vi lever i ett samhälle som styrs av kapitalägande och
-utvecklande så kommer dom där nya verktygen att komma och inte på ett
sätt som är bra för dom som jobbar. Det är det största problemet med
AI, att det på ett drastiskt sätt ökar ägandekoncentrationen av
produktionsmedlen. (Det andra stora problemet är såklart att det
precis som all <a href="https://idiomdrottning.org/template-production" title="Templates and copies">template production</a> och all automatisering
undergräver medvetenheten mellan klimatpåverkan och produktion
eftersom den andra stora marknadsbuggen är att saker som inte kan
räknas in i transaktioner, som ex vis miljökvadd eller nätberoende,
belönas mer ju värre det är.)</p>

<p>Så när det kommer listor med “dom här jobben hotas av AI: typ alla” så är det bara bra, för det kan förhoppningsvis få oss att äntligen komma igång med att knepa ihop några nya sätt att leva som inte är lika marknadsberoende för isåfall kan kornukopian äntligen komma inom räckhåll. Rätten till lättja, rätten till att drömma och skapa och dela och ge fritt.</p>

<p>Så länge vi är satta i lönearbete för att få mat på bordet och tak
över huvet är en grävskopa inte bättre än en spade. Dagarna är ändå
likadana: “Gå upp, gå till jobbet, jobba, jobba, äta lunch. Samma sak
händer imorgon. Jobba, åka trick hem och sätta sig och glo.” Att
verktyget gör att din chefs vägbygge kommer längre eller din chefs
gruva blir djupare eller din chefs dataprogram blir obegripligare
hjälper inte oss det minsta i en sån värld.</p>

<p>Och inte nog med att verktygen inte gör vår dag bättre. Dom kan leda
till massiv fattigdom och nöd i form av arbetslöshet eftersom jobb,
hur knäppt och hemskt det än är att jobba, har gjorts till en
förutsättning för att få äta och sova tryggt i det här skeva
världsbygget.</p>

<p>När spinning jenny, trådmaskinen från sjuttonhundratalet, kom, då
skälvde världen på ett sätt som fortfarande är på vippen att döda den
eftersom industralismens miljökatastrofer fortfarande inte är lösta
utan fortfarande är skenande. Nu när vi är på vippen att skapa en
maskin som skapar maskiner då vete tusan vad vi ska göra. Att bara
låta bli att uppfinna den och bara köra på som vi gör nu kommer inte
hända för det finns ett fåtal superrika supertaskiga personer som
tjänar grovt på att den kommer alltså kommer den.</p>

<p>Men det behöver inte vara fel om vi bara kan få bukt med <strong>den sjukdom som kallas produktionsmedelsägande</strong> och istället alla få en del av la
dolce vita att ta det lugnt medan datorn gör den tråkigaste delen av
jobbet. Bara det inte blir tvärt om, att <a href="https://idiomdrottning.org/knowledge-navigator" title="The Dystopian Hellscape of Apple's 1987 Knowledge Navigator Concept Video">den gör den roliga delen och tvingar oss göra det trista</a>.</p>


        </div>
      </div>
    </content>
    <updated>2025-08-21T08:33:17+02:00</updated>
    <link href="https://idiomdrottning.org/ditt-jobb-vs-ai"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/endless-scroll"/>
    <id>https://idiomdrottning.org/endless-scroll</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/endless-scroll">Endless scroll</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>The “endless scroll” debate was after it replaced pages where you’d scroll scroll scroll click, scroll scroll scroll click, scroll scroll scroll click. That was annoying while still not actually stemming addiction<small> (at least for me)</small>. I’d still read through those megathreads on RPG.net, UI annoyances or no. The endless scroll it just took the clicks out of that process which was an improvement. But what I want is instead taking scrolls out of the process! So it’s tap, tap, tap, tap—like an ebook!</p>

<p>Probably going to be just as addictive but I won’t get anxiety from all the scrolling.</p>

<p>Scrolling and panning is fiddly and I never get exactly the right amount of page scrolled it’s like threding a needle repeatedly and most psge down algos are no good either since they’re paging in a text format that’s not designed for pages so you have to read the same couple of lines twice, last on this page and first on the next. So in the future maybe we’ll render HTML as actual pages (after all, epub readers can [sorta] do it). Even <code>less</code> and <code>more</code> on Unix can do it; they show all of one page, then all of the next page separately and so on. The weaksauce nature of page down in GUI apps like Netscape was one of the biggest letdowns <a href="/internet_timeline" title="When was the Internet invented?">when I first started using them in the nineties</a>.</p>

<p>However, the addiction dark pattern has another component; the endless and often junky <em>content</em> which really makes the scroll endless. That part can not stay.</p>

<p>That’s a secondary reason for why I don’t like discover algorithms on Mastodon, the primary reason being how <a href="https://idiomdrottning.org/masto-explore" title="Masto's “explore” tab">it’s artificial virality</a>.</p>


        </div>
      </div>
    </content>
    <updated>2025-05-27T22:11:52+02:00</updated>
    <link href="https://idiomdrottning.org/endless-scroll"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/i-miss-foss"/>
    <id>https://idiomdrottning.org/i-miss-foss</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/i-miss-foss">I stopped sticking to FOSS but immediately regretted that decision</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>I more or less only used FOSS from late nineties until autumn 2021,
where I got an iPad. This decision was partially prompted by
frustration with Android’s open washing (I was stuck with an
un-updatable Android tablet that wasn’t that old. By comparison, this
iPad has received updates for way more years already) and partially in
a rash angry moment after interacting with a complete jerk online who
made me in one instant give up on the entire FOSS community.</p>

<p>There was also the fact that I realized that I had already been
leaking away my own FOSS-y values, that there had been one growing,
frogboiling exception; video game consoles. Now, for a while when I
was at my most freshly converted GNU-wly brainwashed, I didn’t use
any. Then I rationalized that it’s not that bad to use a Game Boy or
NES. The roms are runnable on widely available emulators (not to
discount the incredible feats of engineering creating those emulators
entailed) and often decompilable or simple enough to understand in
machine code. I found them similar to Z-machine or Scumm VM which I
already thought was OK. After all, the requirement was free
<em>software</em>, not all free media. I could still watch normal movies, for
example.</p>

<p>But that can hardly be said about modern consoles like Switch or Wii
or 3DS. They’re like entire operating systems spamming tons of
telemetry and junk.</p>

<p>So “I am already using proprietary apps so why not do it more?” was my thinking.</p>

<aside>Super minor side note only for true Idiomdrottning factoid hunters: Besides Nintendo, there’s another example. I used a Nokia N9 for a while in between my first and second Android phones, before I went dumbphone in 2017. The N9 ran Linux but had some proprietary layers. I did not know that when I bought the phone. I didnt’t know that until months later actually. That was the best phone I ever had, too bad they shut down the 3g network or I would still have it.</aside>

<p>After getting the iPad I immediately regretted that. It was <a href="/ios" title="The worst things about iPad OS">so much worse</a> than I could’ve ever imagined.</p>

<p>And now I’m stuck with it for as long as I have this device. There’s no chance of a FOSS firmware being made anytime soon. That m1, m2 Linux distro that existed briefly until the kernel guys bullied them away, they were prohibited to make it work on tablets, they could only work on computers.</p>

<p>I’m not gonna get the Switch 2 nor a new iPad. I’m going to try to get
better at this in the future. It’s not exactly easy because this world
has made it way harder to be FOSS only than it was in the nineties.
I’m just gonna do my best from now on and not beat myself up.</p>

<h2 id="the-one-rule-i-stuck-to-and-im-glad-i-did">The one rule I stuck to and I’m glad I did</h2>

<p>I want to steer clear of “<a href="/network-externalities" title="Network externalities">network effect</a>” apps like Messenger or
WhatsApp or X. Those are much much harder to <a href="/vendor-lock-in" title="Vendor Lock-in">get out of</a> once you
start using them. Do not lightly sign up for them. So on the iPad I
never ever used iMessage for example.</p>


        </div>
      </div>
    </content>
    <updated>2025-04-17T09:47:03+02:00</updated>
    <link href="https://idiomdrottning.org/i-miss-foss"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/pagination"/>
    <id>https://idiomdrottning.org/pagination</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/pagination">Scrolling is the enemy</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Apps that can stay still, that can show me one page at a time and then
calmly go to the next or previous page, that’s relaxing. Discrete
steps and cleary defined modes. That calms me down.</p>

<p>Apps that are all gradiated and fiddly and zoomy and jittery, that
freaks me the heck out.</p>

<p>Some of us broken-hearted suckers in the 21th century need to juggle a
lot of data. A fast screen can help with that. A fast screen can also
hinder that, if it asks us to do the juggling on a rolling table on a
moving train on a tilting pinball table.</p>

<p>This is why epub readers are better than browsers even on the most
glaring LCD of all time: it show you one page at a time instead of
scrolling a few sentences here and there. I don’t get why browsers
even in our modern day still can’t do that, while epub readers all do
it easily.</p>

<p>That was one of the big web disappointments for me when I first
modemed up in 1995: that the “page down” experience was so
unsatisfying, with having to reread last page’s last sentence as the
next page’s first sentence instead of clearly defined pages.</p>

<p>I don’t wanna scroll scroll scroll, I want to turn between separate
pages with no overlapping text; a UI design decision the printed book
mastered centuries ago, thankfully or we’d still read our potboilers
and smutty romance novels on scroll tubes.</p>

<p>Panning good when zoomed in, but zooming and panning should be a
deliberate choice, not the whim of a misplaced palm.</p>

        </div>
      </div>
    </content>
    <updated>2025-01-22T18:58:19+01:00</updated>
    <link href="https://idiomdrottning.org/pagination"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/jellyfin-vlc"/>
    <id>https://idiomdrottning.org/jellyfin-vlc</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/jellyfin-vlc">Watching Jellyfin videos with VLC</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Okay so if you wanna watch a Jellyfin video with VLC, go to your
Jellyfin instance’s web view where you can copy a stream URL and it’ll look like this:</p>

<pre><code>https://jellyfinserver.tld/Items/TheIdForTheVideoGoesHere/Download?api_key=YourKeyIdGoesHere
</code></pre>

<p>And you can paste that URL into your VLC.</p>

<p>If you want to grab a whole season’s worth of video IDs that you can mangle
into a .pls, you can with Jellyfin’s API! Here’s an example using httpie and
jq.</p>

<pre><code>https jellyfinserver.tld/Shows/ShowIdThatYouAlsoCanFindFromTheWebView/Episodes X-Emby-Token:YourKeyIdGoesHere season==1 |jq -r '.Items[].Id'
</code></pre>

<p>With VLC, watching stream URLs from .pls files is way better than
watching individually pasted streams since it can remember how far
into the video you were.</p>

<p>Pretty sure this works with other stream players too, like mpd.</p>

        </div>
      </div>
    </content>
    <updated>2024-10-28T19:14:34+01:00</updated>
    <link href="https://idiomdrottning.org/jellyfin-vlc"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/mvd"/>
    <id>https://idiomdrottning.org/mvd</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/mvd">md, mvd and cpd</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>I have gotten so much mileage out of these three zsh functions for
making directories for over a decade, I should’ve posted them a long
time ago but I just didn’t think to.</p>

<pre><code>md () {
	mkdir -p $1
	cd $1
}

mvd () {
	mkdir -p "${@[$#]}"
	mv "$@"
	cd "${@[$#]}"
}

cpd () {
	mkdir -p "${@[$#]}"
	cp -r "$@"
	cd "${@[$#]}"
}
</code></pre>


        </div>
      </div>
    </content>
    <updated>2024-10-16T08:34:18+02:00</updated>
    <link href="https://idiomdrottning.org/mvd"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/chunk-by"/>
    <id>https://idiomdrottning.org/chunk-by</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/chunk-by">chunk-by</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Takes a list and splits it in order into smaller lists such that each
list contains one <code>pred</code>. If there are no pred, that’s fine, DTRT
silently i.e. make a one-element list containing the whole list.</p>

<pre><code>(define (vowel? l) ((as-list (c member l)) "aeiou"))

(define (chunk-by pred lis) (list lis))

(define (chunk-by pred lis)
  (receive (hd tl)
      (split-at lis (add1 (require number? (list-index pred lis))))
    (cons hd (chunk-by pred (require (c any pred) tl)))))

(chunk-by vowel? (string-&gt;list "somejunk"))

⇒ ((#\s #\o) (#\m #\e) (#\j #\u #\n #\k))
</code></pre>

<ul>
  <li><a href="/brev" title="brev">brev</a></li>
</ul>

        </div>
      </div>
    </content>
    <updated>2024-10-02T01:11:01+02:00</updated>
    <link href="https://idiomdrottning.org/chunk-by"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/shared-solutions"/>
    <id>https://idiomdrottning.org/shared-solutions</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/shared-solutions">Shared solutions</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>I understand why people are like “But I figured out how to populate my
<code>autoexec.bat</code> so why can’t everyone else do that same work too?” but
one reason why I’m always harping on usability is that the great thing
about FOSS is that it enables sharing solutions.</p>

<p>I hung out adjacent to a hacker group in high school and the mindset
there was always like “Don’t ask questions! Don’t answer questions!
Just tell people to RTFM! I did, and I learned things better and more
thoroughly that way” whereas I was more like this:</p>

<p>“If it takes me 4 hours to learn something on my own and you 4 hours
to learn something else on your own, and then we can have two ten
minute conversations where we help each other, we’ve both saved 3h40m
each compared to if I had to spend 4 hours to learn my thing and then
4 hours more to learn your thing.”</p>

<p>And this goes quadruple for the world of software where we can share
not only knowledge but actual running code.</p>

<p>We can share ready-made, polished, usable tools. We can learn so that
not everyone has to learn.</p>

<p>Like in the real world I can just buy a pair of scissors and use that
to cut things without knowing how to make scissors or even work metal.
Not everyone needs to do every thing from scratch literally all the
time. Doing some of the things from scratch some of the time is really
good because it’s correctly observed that that <em>is</em> a good way to
learn. But 8 billion people all doing the same work is <a href="/automation-vs-scarcity" title="Automation vs Scarcity">just a
waste</a>. The typing monkeys will never <a href="https://en.wikipedia.org/wiki/Infinite_monkey_theorem" title="Infinite monkey theorem - Wikipedia">get to Shakespeare</a>
if they’re all stuck typing the same quick brown foxes.</p>

<p>Now, we need dialectics about that so that in every generation there’s
at least someone who can fix the machine that fixes the machine that
fixes the machine. We don’t wanna stack the <a href="/composition-considered-harmful" title="Composition Considered Harmful">house of cards</a> so high
that it’s built on nothing.</p>

<p>There are two different reasons why people get into hacking.</p>

<p>One is that they love picking things apart and making them from
scratch and seeing how they work and what really makes that clock
tick.</p>

<p>The other, and maybe this is more my jam, is that they get frustrated
with redundant tasks, with doing things over and over again where
doing it once would’ve been enough.</p>


        </div>
      </div>
    </content>
    <updated>2024-08-13T12:34:36+02:00</updated>
    <link href="https://idiomdrottning.org/shared-solutions"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/clever-gui"/>
    <id>https://idiomdrottning.org/clever-gui</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/clever-gui">Put that in your CLI and smoke it</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Snover, <a href="https://mtlynch.io/notes/guis-are-antisocial/">as transcribed by Michael Lynch</a>, says:</p>

<blockquote>
  <p>I realized that — you know, that the mouse is antisocial. The GUI is antisocial. So what’s that mean? You have a problem to solve, and you solve it with the GUI. What do you have? A problem solved.</p>

  <p>But when you solve it with a command line interface in a scripting environment, you have an artifact. And all of a sudden, that artifact can be shared with someone.</p>
</blockquote>

<p>It’s certainly true that historically, CLIs have lead themselves much much more readily to reproducing and automating tedious tasks, and that those automations can be shared. That’s the main point, and I’m right there <a href="/ed-and-make" title="ed config.h &amp;&amp; make install">joining the choir</a>.</p>

<p>It’s his secondary point that’s a li’l off:</p>

<blockquote>
  <p>By the way, the way you did it can show cleverness. I’ve never seen anybody use a GUI in a clever way. Ever. There’s no cleverness to it. No, like, “Oh my god, you should see the way Adam clicked that mouse. Oh my god. Guys, guys, guys, guys, come on! Check it out: Adam’s going to click the button! Oh my god! That’s amazing!” It just doesn’t happen.</p>

  <p>Snover contrasts this with the reaction to seeing an impressive command-line script:</p>

  <p>It’s like, “Oh my god! Did you see what Proust did? That’s phenomenal! This guy’s a frickin’ genius.” And then, “Hey, give that to me. I’m going to steal that technique and apply it to my code.”</p>
</blockquote>

<p>Now, this on the other hand is the most sollipsistic thing I’ve heard all week (and it was RNC week!).</p>

<p>You’ve never seen anyone use a GUI in a clever way? I’ve been ten
thousand miles in the mouth of a graveyard. The last makings of the future
upon green banks of unseen battlefields. I traveled far and wide to
prisons of the cross. What did you see there? I’ve seen hackers juggle mouse chords in ACME, musicians sequencing sequences of MIDI sequences in seq24, speed painters catching the light of day itself. I’ve seen <a href="https://www.youtube.com/watch?v=ubgvomRTW80">Bay Raitt box modelling in Mirai</a>.</p>

<p>It’s not a selling point of any UI that you’ve got to be clever to use it well, I don’t even agree that this is a good thing about CLIs, but GUIs share the same trait.</p>

<p>Ten years ago, the first post on this entire blog <a href="/the-terminal-and-the-shell" title="The Terminal and the Shell">was about CLIs</a>. I love CLIs. I just feel like this particular selling point is based on an ignorance on all the wonderfully productive work people do in spreadsheet, MIDI sequencers, box modellers, and painting apps. We use UIs to live, we don’t live to use UIs.</p>

<p>Michael replied<small> (and I’ll paste some of this in as reply to his comment on his web page next time I have www access)</small>:</p>

<blockquote>
  <p>Thanks for responding! I’m still thinking about whether it is possible to use GUIs in a “clever” way. You shared the video of box modeling in Mirai, and others have talked about ableton speedruns, but even that feels like not quite using a GUI in a clever way. Like if you watched F. Scott Fitzgerald or Alice Walker sit at a typewriter and write an amazing page of a novel, we probably wouldn’t say they’re using the typewriter in a clever way. When I see the Mirai and Ableton videos, it feels like the person is using the GUI in an efficient or competent way, but it still feels somehow different from scripting something in a clever way.</p>
</blockquote>

<p>A typewriter is a straightforward UI and while the end product of a
story like <cite>Bernice Bobs Her Hair</cite> is indeed awesome, I
agree that it’s not UI dazzling<small> (a court stenographer might
impress me with theor chorded typing of text and even words, but
Fitzgerald or Walker banging on sholes, that’s not a “clever UI” in
that same way)</small>. I also had many people writing in with game
examples, yeah, I had some game examples in my own first draft but
ended up deleting them since the point of a UI is to be easy to use
while the point of a game is to be challenging to use. Not the same
thing.</p>

<p>But the Ableton or Mirai examples or even some video or audio editors,
that’s where I’m gonna take my stand. If we disagree that those usages
are clever then there’s got to be some sort of semantics mismatch of
what “clever” really means: I’ve seen radio station journalist use
audio editing apps the public have never seen in ways that are
insanely ideosyncratic, keyboard centric, efficienct, and clever.</p>

<p>My reaction to this framing is that we devs make apps (and libes) for
making apps for making apps for making apps. Someone who’s learned to
juggle Mirai or Ableton, they’re not a part of that same dev
community, that same hacker culture, but I respect them maybe even
more for how they do what they do. I understand some of the scripting
clevernessess like duff’s device or the eval/apply loop or even code
golfing. For me, the awe I feel is the same awe. If you have a more
nuanced take on different kinds of awe and cleverness, that’s
something I don’t wanna take away from you, but it’s hard to argue for
that in a way that doesn’t disparages these non-coders that are
masters of their tool in their own right.</p>

<p>Of course, when I can combine the two, that’s where I’m in really my
element. I love the scripting interfaces of MyPaint, GIMP
(#ChangeTheName), Blender, and Inkscape. Blender is as much of a
Python machine as Emacs is a Lisp machine. We don’t have to choose.</p>

<h2 id="artifacts">Artifacts</h2>

<p>Snover went on:</p>

<blockquote>
  <p>Or then I have this artifact, and I publish it, and people are using it. There’s a debt of gratitude. Like, they owe me a beer.</p>
</blockquote>

<p>Always a quid-pro-quo with these guys. I’m not on board with shackling our world of imagination and <a href="/mittens" title="Economics of Mittens &amp; Socks">sharing-is-caring</a> to the zero-sum world of beers. Since we can make copies there’s no limit to how much we can lift each other up, how many shoulders we can stand on. It’s amazing that we can reproduce and automate and share workflows, that’s great, and <em>that’s</em> one area CLIs are winning.</p>

<p>Not the only area: on the web, in search boxes and address fields and chats, those are all CLIs. CLIs are back with a bang and there are so many jobs that a CLI does better.</p>


        </div>
      </div>
    </content>
    <updated>2024-07-21T10:17:16+02:00</updated>
    <link href="https://idiomdrottning.org/clever-gui"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/finding-keys"/>
    <id>https://idiomdrottning.org/finding-keys</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/finding-keys">Finding PGP keys</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<aside>Calling the public key a “key” in public key encryption was a bad and confusing idea. Calling it an envelope or a padlock or a chain woulda been way better, reserving the name “key” for the private key. This goes for both SSL certs and for PGP email.</aside>

<p>Here’s how I find PGP public keys. I have a zsh function that runs
this, where “$1” means the email address I want to send to:</p>

<pre><code>gpg --auto-key-locate local,wkd,keyserver --locate-keys "$1" ||
    curl -XGET https://api.protonmail.ch/pks/lookup\?op\=get\&amp;search\=$(uenc "$1")|gpg --import
</code></pre>

<p>That second clause doesn’t get invoked very often; Proton users who
don’t have their own domain, their keys are available over <a href="/gpg-wkd" title="GPG WKD">WKD</a>,
and some users who <em>do</em> have their own domain still have WKD set up,
and some (all?) who don’t are still in Proton’s HPK keyserver. Maybe
that covers all of them and there’s nothing left. I put it in the
script before I knew about their keyserver, and got good mileage out
of it early on. I’m never ever gonna get Proton myself and it’s so
nice to just be able to normally email people who’re on there and step
one to doing that is getting their keys.</p>

<p>This doesn’t find Autocrypt keys; that’s something I might wanna fix
somehow, maybe introducing a notmuch query into the mix? I’d have to
reindex with that header.</p>

<p>Now, local is checked first and that’s bad, don’t try that at home,
kids. If I already have some old, stale key to them, that’s what’s
gonna pop up first and end the search. But I’m such a ditz that I kept
re-importing keys that I’ve already got until I introduced local as
the first step.</p>

<p>The keyservers I currently check are these:</p>

<pre><code>keyserver hkps://keys.openpgp.org
keyserver hkps://mail-api.proton.me
keyserver hkps://keys.mailvelope.com
keyserver hkps://keyserver.ubuntu.com
keyserver hkps://pgp.mit.edu
</code></pre>

<p>I’m not sure my own keys are in any of them, I might’ve submitted them
at one time or another. I primarily rely on WKD or Autocrypt. The
keyserver idea was pretty flawed compared to WKD, and then Autocrypt
is a good workaround for email providers that don’t allow WKD. Which
are pretty few, but <a href="https://posteo.de/en/help/publishing-public-pgp-key-for-posteo-email-address" title="How do I publish the public PGP key for my Posteo email address in the Posteo key directory?">Posteo does</a> and anything that allows you to use
your own domain.</p>


        </div>
      </div>
    </content>
    <updated>2024-06-12T09:35:18+02:00</updated>
    <link href="https://idiomdrottning.org/finding-keys"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
  <entry>
    <link rel="self" href="https://idiomdrottning.org/code-for-yourself"/>
    <id>https://idiomdrottning.org/code-for-yourself</id>
    <title type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="https://idiomdrottning.org/code-for-yourself">Coding for yourself is OK</a></div></title>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
	      <div>
        

<p>Re: <a href="https://alexschroeder.ch/view/2024-06-06-programming">Learning to be a programmer</a></p>

<p>I’ve made tons of driveby commits since pandemic started. 100
contributions in the last year just on GitHub and probably just as
many on other repos.</p>

<p>So “even they can’t read programs written by others without 1000x
effort” isn’t literally true.</p>

<p>I also… I think “programming for the household” is actually awesome.
Automating our <em>own</em> lives, autonomously, not squeezing our lives into
someone else’s automation.</p>

<p>When what we do can be extracted into reusable tools or libraries,
that’s great. I’ve started 136 repos on idiomdrottning.org also since
pandemic started. Programming for your own needs doesn’t have to end
there.</p>

<p>There’s no shame in programming for yourself.</p>

<p>The <a href="/egoism" title="When they tried to say that egoism was good">other day I was pondering if egoism can be used for
altruism</a> and in hindsight, the answer’s closer than I
would’ve guessed: <a href="/mittens" title="Economics of Mittens &amp; Socks">in FOSS licensing and paying it forward</a>.</p>


        </div>
      </div>
    </content>
    <updated>2024-06-07T00:56:58+02:00</updated>
    <link href="https://idiomdrottning.org/code-for-yourself"/>
    <author>
      <name>Idiomdrottning</name>
      <email>sandra.snan@idiomdrottning.org</email>
    </author>
    </entry>
</feed>

