<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Andrey Listopadov</title>
    <link>https://andreyor.st/categories/rant/</link>
    <image>
      <title>Andrey Listopadov</title>
      <link>https://andreyor.st/categories/rant/</link>
      <url>https://andreyor.st/categories/rant/favicon-64.png</url>
    </image>
    <description>Posts from the 'rant' category by Andrey Listopadov</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <copyright>Andrey Listopadov 2020-2026 - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</copyright>
    <lastBuildDate>Sun, 23 Nov 2025 02:57:00 +0300</lastBuildDate>
    <atom:link href="https://andreyor.st/categories/rant/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>GNOME is better macOS than macOS</title>
      <link>https://andreyor.st/posts/2025-11-23-gnome-is-better-macos-than-macos/</link>
      <guid>https://andreyor.st/posts/2025-11-23-gnome-is-better-macos-than-macos/</guid>
      <description>&lt;p&gt;Recently, I bought my first-ever MacBook.
I&amp;rsquo;ve spent some time with it, and I gotta say - despite all that hot garbage that is thrown at GNOME for being an OSX clone, GNOME does the job better than I&amp;rsquo;ve expected, and certainly better than Apple.
In some areas, that is.&lt;/p&gt;
&lt;h2 id=&#34;good-old-days-of-linux&#34;&gt;Good old days of Linux&lt;/h2&gt;
&lt;p&gt;A bit of a backstory.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using GNUplusSlashLinux for more than fifteen years.
Most of the time, I used GNOME, starting from GNOME2, moving to Unity maybe for two years, then GNOME Shell, then KDE Plasma 5 for another two years, and switched back to GNOME Shell again.
I&amp;rsquo;m not mentioning some of my at most month-long endeavors to other &lt;abbr title=&#34;Desktop Environment&#34;&gt;DE&lt;/abbr&gt;s, like XFCE, or tiling WMs, because they never stuck with me.
So I&amp;rsquo;ve been there for most releases of GNOME Shell, followed them closely, even used to run Ubuntu GNOME when GNOME Shell became a thing, until it became the default in Ubuntu once again.
Though by that time, I had already moved from Ubuntu to a different distribution for a variety of reasons.&lt;/p&gt;
&lt;p&gt;I wasn&amp;rsquo;t always satisfied by GNOME, and was a fair bit vocal about it in the past - even got myself banned from r/gnome subreddit, for shitting on it too much.
That&amp;rsquo;s why I experimented with other desktop environments, particularly Unity and KDE.&lt;/p&gt;
&lt;p&gt;Unity felt like a breath of fresh air after GNOME2, mostly because Canonical took years of people trying to make GNOME2 more OSX-like, and made decent steps in that direction.
A global menu, HUD, blur, buttons on the left - you name it, it was there.
Granted, it wasn&amp;rsquo;t an OSX clone - rather, it was its own thing, and I still remember Unity days fondly.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/unity7.png&#34;
         alt=&#34;Figure 1: Honestly, this was the best desktop&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 1: &lt;/span&gt;Honestly, this was the best desktop&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I switched to GNOME Shell almost instantly as it was released as Ubuntu GNOME spin, and while it was a bit janky, Unity started to lose steam, and GNOME looked like a hot new thing.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/gnome3.6.png&#34;
         alt=&#34;Figure 2: Anyone else remember that wallpaper? GNOME sure has come a long way&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 2: &lt;/span&gt;Anyone else remember that wallpaper? GNOME sure has come a long way&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I did, however, run Unity on my older PCs, as it was far less taxing on resources than early versions of GNOME3, but then it was discontinued, and long-awaited Unity 8 with Mir never became a thing.
So, when I was fed up with GNOME being a resource hog, often crashing, and moving towards Wayland, which didn&amp;rsquo;t work as good as it was advertised, I decided to try KDE somewhere around 2018.&lt;/p&gt;
&lt;p&gt;And boy, how wrong was I, thinking that KDE is a buggy mess that would drain my resources even worse than GNOME.
KDE Plasma 5 was nothing like I imagined - it was fast, lightweight, and slick.
As a fan of old Unity design and OSX looks, I&amp;rsquo;ve configured my plasma, but it wasn&amp;rsquo;t a full clone, as other people often did.
Instead, I tried to keep Plasma look like Plasma, but feel like a mix between Unity and OSX:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/plasma5.png&#34;
         alt=&#34;Figure 3: To this day, I still think this was beautiful. I&amp;amp;rsquo;ve spent a lot of time fiddling with icon spacings in the top panel, combining widgets, writing my own in QML, just to throw it all away, a few years later&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 3: &lt;/span&gt;To this day, I still think this was beautiful. I&amp;rsquo;ve spent a lot of time fiddling with icon spacings in the top panel, combining widgets, writing my own in QML, just to throw it all away, a few years later&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;ve run Plasma 5 for about two years, and while I was enjoying it, there still were a lot of jank, bugs, occasional crashes (although Plasma recovered from them beautifully), but GNOME also became a lot better.
So I switched back to GNOME, and I&amp;rsquo;m still running it on my old laptop, where I&amp;rsquo;m writing this part of the text right now:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/gnome42.png&#34;
         alt=&#34;Figure 4: GNOME 42 (Note, I have a 2K display and use GNOME without scaling with enlarged fonts. Helps a lot with too big GNOME UI elements taking up space)&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 4: &lt;/span&gt;GNOME 42 (Note, I have a 2K display and use GNOME without scaling with enlarged fonts. Helps a lot with too big GNOME UI elements taking up space)&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As you can see, there are no fancy extensions - everything is the same as in a stock Fedora Workstation, because it is a stock Fedora Workstation.
And I love it.&lt;/p&gt;
&lt;p&gt;Over the years, I had a love-hate relationship with GNOME, but after a while, I came to accept it as it is, and now it feels like a perfect desktop environment for me personally.
That, of course, is thanks to GNOME developers actually trying to make GNOME better, and me buying into their vision of what an OS should look and feel like.
There is still some jankiness to it, but I&amp;rsquo;m willing to overlook it at this point, because after years of searching, I couldn&amp;rsquo;t find anything that suits my workflow better.
And I&amp;rsquo;m fine with that.&lt;/p&gt;
&lt;h2 id=&#34;a-disclaimer&#34;&gt;A disclaimer&lt;/h2&gt;
&lt;p&gt;Before I go crazy on bashing macOS, I need to make a disclosure - &lt;strong&gt;I AM BIASED&lt;/strong&gt;.
I&amp;rsquo;ve been using GNOME for a lot of time, and a lot of things that feel logical to me may not feel logical at all to others.
Especially for macOS users.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m fully aware of that.&lt;/p&gt;
&lt;p&gt;Now, with that outta the way, let&amp;rsquo;s begin.&lt;/p&gt;
&lt;h2 id=&#34;a-new-laptop&#34;&gt;A new laptop&lt;/h2&gt;
&lt;p&gt;As I mentioned, I&amp;rsquo;ve just got myself a MacBook.
I did it because today&amp;rsquo;s laptop market is a bit odd.
It&amp;rsquo;s hard to find a decent non-gaming laptop with good specs.
I mean, there are Lenovo ThinkPads, ThinkBooks, and even IdeaPads that are decent, but I already owned an IdeaPad, and while it served me for six years and I liked it, I don&amp;rsquo;t know if I want another one.
Other brands may offer similar hardware, but I didn&amp;rsquo;t investigate much.&lt;/p&gt;
&lt;p&gt;One of the reasons is OS choices.
You see, I have run Linux as my main OS since around 2008.
But I still have to use Windows from time to time.
During university years, most of the non-programming software was Windows-only.
After university, I had to use Windows to do media stuff, like music recording and video production, or design.&lt;/p&gt;
&lt;p&gt;Design is probably the only task Linux can handle without much of a hassle, given that there are decent programs for that.
For instance, I don&amp;rsquo;t mind GIMP, Krita, or Inkscape for most of my image-related tasks.
I can even do video editing in Blender (and I do), but for some reason, it just works better under Windows.
Audio production - not a chance.
You&amp;rsquo;ll find me dead in the ground before I figure out how to set up a realtime kernel and configure Jack.
Not to mention, VSTs just don&amp;rsquo;t work because many are relying on Windows APIs, and patching them through Wine is not a path I would like to walk.&lt;/p&gt;
&lt;p&gt;So I had to use Windows, constantly rebooting from Linux to it every time I wanted to record myself, or work on some video for my channel.
And I also had to use the one that came with my laptop - 6 years ago, it was Windows 10.&lt;/p&gt;
&lt;p&gt;My parents recently got a new laptop with Windows 11 on it, and my god, it is horrible.
After using Linux for more than fifteen years, I can&amp;rsquo;t even imagine how Microsoft is still able to get away with it.
Ads are everywhere - on the lockscreen, in the start menu, in notifications, etc.
You can&amp;rsquo;t even activate it without connecting to the network and signing into a Microsoft account.&lt;/p&gt;
&lt;p&gt;Oh, and updates.
Fucking updates are being forced, while I&amp;rsquo;m waiting for the next 5 hours until my video finishes rendering, because my laptop is too old to render in 4K.&lt;/p&gt;
&lt;p&gt;Yes, it&amp;rsquo;s possible to &amp;ldquo;debloat&amp;rdquo; Windows, remove most of the jank they added, disable automatic updates, and have a decent, clean experience.
I just don&amp;rsquo;t want to do that.
The whole point of an operating system, to me, is to stay out of the way while managing resources, processes, and making sure my shit is done without major hiccups.&lt;/p&gt;
&lt;p&gt;Now, when Windows 10 support is ending, all we&amp;rsquo;re left with is Windows 11, and god knows what bullshit awaits us in Windows 12.&lt;/p&gt;
&lt;p&gt;So I thought to myself: &amp;ldquo;Ok, I like Linux, but I want to be able to do my media-related stuff, and I don&amp;rsquo;t want to invest in making Linux be able to do so. What options do I have other than that?&amp;rdquo;
I just applied to a new job, and almost everyone there uses a Mac, and many of them are musicians, either hobbyists like myself or even professionals.
After asking around, it appeared to me that switching to macOS could be the solution I&amp;rsquo;m looking for.
It&amp;rsquo;s a Unix, so I guess my Linux habits won&amp;rsquo;t have to go through WSL hoops, and it has world-class support for media tasks.
I think macOS can even be considered media-first when it comes to production, unlike Linux or Windows.&lt;/p&gt;
&lt;p&gt;So I saved some money and got myself a new MacBook Pro, with M4, and decent specs.
While the laptop itself is nice - I like the aluminium body, keyboard is nice (although I had an opportunity of using their butterfly one, and liked it better), the screen is gorgeous, and even the laptop speakers are great, but&amp;hellip;&lt;/p&gt;
&lt;p&gt;&amp;hellip;but the OS&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;macos&#34;&gt;macOS&lt;/h2&gt;
&lt;p&gt;This is the most counter-intuitive, user-unfriendly, confusing piece of software that I&amp;rsquo;ve used in my life.
And I worked as a consultant in a cellphone store and used software for cashiers, yet it still was not as horrid.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure nothing I will write here will be new, and I&amp;rsquo;m committing an &amp;ldquo;internet crime&amp;rdquo; of &amp;ldquo;beating a dead horse&amp;rdquo; here, while it&amp;rsquo;s probably already reduced to vapors, but I still want to get it out of my system.&lt;/p&gt;
&lt;h3 id=&#34;the-desktop&#34;&gt;The Desktop&lt;/h3&gt;
&lt;p&gt;My laptop came with macOS 15 Sequoia preinstalled, so I haven&amp;rsquo;t used previous versions much, although I had used a Mac a bunch of times in the past when I was at the university ten plus years ago.
However, I decided to immediately upgrade to the 26.1 Tahoe, so I wouldn&amp;rsquo;t get too used to the good-looking interface, and instead learn to get comfortable with this liquid glass crap.
So I didn&amp;rsquo;t touch macOS for some years, and some things have changed, but the majority of annoyances I remember are still here.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on different desktops/WMs for macOS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve started receiving some comments on this post, and I&amp;rsquo;d like to clarify that I&amp;rsquo;m aware of other window manager alternatives for macOS, like &lt;a href=&#34;https://github.com/nikitabobko/AeroSpace&#34; target=&#34;_blank&#34;&gt;AeroSpace&lt;/a&gt;, &lt;a href=&#34;https://github.com/koekeishiya/yabai&#34; target=&#34;_blank&#34;&gt;Yabai&lt;/a&gt; and the like.&lt;/p&gt;
&lt;p&gt;First, I&amp;rsquo;m not a fan of tiling window managers.
Personally, I almost always have one window per desktop.
Tiling never stuck with me. I&amp;rsquo;ve tried it several times during these fifteen plus years, and I don&amp;rsquo;t really feel the need for a keyboard-driven environment besides my Emacs.
I mostly use touchpad gestures and the overview/mission control feature when I need to switch windows.&lt;/p&gt;
&lt;p&gt;Secondly, I don&amp;rsquo;t really have &lt;em&gt;that many&lt;/em&gt; issues with the current macOS WM, and I don&amp;rsquo;t really want to replace it.
I was a GNOME user, remember?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;virtual-desktops&#34;&gt;Virtual desktops&lt;/h4&gt;
&lt;p&gt;As an ex-GNOME user, I like my virtual desktops.
MacOS has had virtual desktops for a long time, but I think GNOME handled those better.&lt;/p&gt;
&lt;p&gt;First of all, switching with the touchpad gesture feels a lot slower.
In GNOME, it wasn&amp;rsquo;t an animation - the speed was tied directly to how fast you move your fingers, with easing after you release them.
I got used to switching between desktops with a lot of speed, and on macOS, it&amp;rsquo;s not like that.&lt;/p&gt;
&lt;p&gt;It seems it is also tied to the speed of the gesture, but it feels like there&amp;rsquo;s a cap on the maximum possible speed:&lt;/p&gt;
&lt;p&gt;&lt;video autoplay loop muted&gt;&lt;source src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/desktop-switching.mp4&#34; type=&#34;video/mp4&#34;&gt;
Your browser does not support the video tag.&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;An additional feature of GNOME is that it will automatically create additional virtual desktops once you&amp;rsquo;ve filled all of them, and there&amp;rsquo;s no limit to how many you can have.
I usually have 4-5 desktops, but when I&amp;rsquo;m not working on something, it&amp;rsquo;s nice that I can only have one or two.&lt;/p&gt;
&lt;p&gt;In macOS, you can have as many desktops as you want, however, they&amp;rsquo;re not added automatically.
Or at least I couldn&amp;rsquo;t find a setting for that.
Because of that, even if I&amp;rsquo;m not using all of them, sometimes a stray window is sitting on the last desktop, and I have to go through all of them to get to it.&lt;/p&gt;
&lt;h4 id=&#34;fullscreen-and-maximize-button&#34;&gt;Fullscreen and maximize button&lt;/h4&gt;
&lt;p&gt;For some reason, the developers decided that the maximize button should instead act like a fullscreen button in macOS.
When you maximize a window, it is moved to its own, dedicated virtual desktop - and &lt;strong&gt;I like it&lt;/strong&gt;.
I have used this pattern in GNOME for a long time by installing an &lt;a href=&#34;https://extensions.gnome.org/extension/8226/maximize-to-empty-workspace-2025/&#34; target=&#34;_blank&#34;&gt;extension&lt;/a&gt; that brings this feature into GNOME.&lt;/p&gt;
&lt;p&gt;However, and it is a big &lt;strong&gt;however&lt;/strong&gt;, in GNOME, when I maximize a window, it creates a virtual desktop next to the one I&amp;rsquo;m on.
In macOS, it also creates a virtual desktop, but it moves it to the far right.&lt;/p&gt;
&lt;p&gt;So, imagine I&amp;rsquo;m browsing the web, seeing something interesting, and I want to open Emacs to take a note.
I open Emacs, maximize it, and on the desktop to the left of Emacs, I still have my browser, and I can switch between these back and forth.
In macOS, if I do this, the window is moved to a desktop at the far end of the list of desktops.
You can rearrange those manually, but it&amp;rsquo;s clumsy.
I would much prefer if the desktop were created to the right of the current one.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/emacs-on-far-right.png&#34;
         alt=&#34;Figure 5: Emacs was opend on Desktop 1, but after maximizing it is now essentially at Desktop 5&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 5: &lt;/span&gt;Emacs was opend on Desktop 1, but after maximizing it is now essentially at Desktop 5&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You can prevent this, in some form, by turning on the &amp;ldquo;Automatically rearrange Spaces based on most recent use&amp;rdquo; in the settings.
Then, a new fullscreen desktop is created to the right of the one you&amp;rsquo;re currently on, which is nice, however, then the spaces get rearranged all the time.
Like, when you&amp;rsquo;re opening a link from the messenger, you switch to a desktop that has the browser open.
The spaces get rearranged, such that the desktop with the messenger is next to the one with the browser, so you have to find the other one that previously was inbetween.
It&amp;rsquo;s so bizarre that we can&amp;rsquo;t have fullscreen spaces created next to the current one without automatic rearrangement.&lt;/p&gt;
&lt;p&gt;And, because it is a fullscreen mode, it also makes things go a bit haywire for some applications.
Emacs doesn&amp;rsquo;t like this mode in particular.&lt;/p&gt;
&lt;p&gt;Right now, I&amp;rsquo;m using four virtual desktops and manually organizing those, but maybe I need to adjust my usage pattern.
Speaking of virtual desktops, let&amp;rsquo;s look at the Mission Control thing.&lt;/p&gt;
&lt;h4 id=&#34;mission-control&#34;&gt;Mission Control&lt;/h4&gt;
&lt;p&gt;That&amp;rsquo;s another pain point for me.
You see, GNOME also has something akin to Mission Control - you swipe up with three fingers, and you see all your open windows:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/overview-gnome.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;You can then move your windows between desktops or switch between them.&lt;/p&gt;
&lt;p&gt;MacOS also has this, with the exact same gesture:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/mission-control.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;However, it is far less useful.&lt;/p&gt;
&lt;p&gt;In GNOME, I can close windows in the Overview:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/overview-action1.png&#34;
         alt=&#34;Figure 6: This close button is part of the Overview, not the window manager&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 6: &lt;/span&gt;This close button is part of the Overview, not the window manager&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I can tweak the dock:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/overview-action2.png&#34;
         alt=&#34;Figure 7: Actually, the dock is only visible in Overview&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 7: &lt;/span&gt;Actually, the dock is only visible in Overview&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I can start typing, and the search appears:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/overview-action3.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;I can even swipe up again and bring up the applications menu:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/overview-action4.png&#34;
         alt=&#34;Figure 8: Not that I use it that much though&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 8: &lt;/span&gt;Not that I use it that much though&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;There&amp;rsquo;s a lot of control!
Wanna know how much you can control in the Mission Control?&lt;/p&gt;
&lt;p&gt;You can move windows and desktops around.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;What mission?
What control?
You can&amp;rsquo;t do anything.&lt;/p&gt;
&lt;p&gt;Sure, one advantage macOS&amp;rsquo; mission control has is that you can move entire desktops around - in GNOME, you can&amp;rsquo;t rearrange desktops themselves, only windows.
And it plays nicely, with all this full-screen nonsense, but I still much prefer the GNOME approach here.&lt;/p&gt;
&lt;h3 id=&#34;the-dock&#34;&gt;The Dock&lt;/h3&gt;
&lt;p&gt;Dock is a weird concept.
It&amp;rsquo;s cool to have it, and I remember in my early Linux days I wanted a macOS-like dock so badly that I tried to make it out of existing panels and custom widgets.
I installed various dock plugins in my desktop environments just to have it zoom in on icons when I hover over them.
These days are long past.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/cairo-dock.png&#34;
         alt=&#34;Figure 9: remember Cairo dock?&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 9: &lt;/span&gt;remember Cairo dock?&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/plank.jpg&#34;
         alt=&#34;Figure 10: remember Plank?&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 10: &lt;/span&gt;remember Plank?&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;GNOME made a smart decision to only show the dock when you&amp;rsquo;re in Overview - their &amp;ldquo;Mission Control&amp;rdquo; variant.
You don&amp;rsquo;t need the dock constantly taking vertical space from your windows, and you don&amp;rsquo;t have to deal with it showing up accidentally when you simply hover the mouse pointer at the bottom of the screen.
Or at the side of the screen if it is your thing.&lt;/p&gt;
&lt;p&gt;When it is only inside the Overview/Mission control, it makes sense as a favorites bar and a way of switching between applications.
I haven&amp;rsquo;t used &lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;Tab&lt;/kbd&gt; (or &lt;kbd&gt;⌘ command&lt;/kbd&gt;+&lt;kbd&gt;⭾ Tab&lt;/kbd&gt;) in years because of that.
I would much prefer for the dock to be only visible in Mission Control, but sadly, there&amp;rsquo;s no way of doing it in macOS, as far as I can see.
Moreover, Dock becomes useless in Mission Control specifically, so it won&amp;rsquo;t work that well in macOS.&lt;/p&gt;
&lt;p&gt;What baffles me is that when you close an app, it stays in the dock, taking up space.
Again, there seems to be no way of disabling it.
Disabling the &amp;ldquo;Show suggested and recent apps in Dock&amp;rdquo; in system settings doesn&amp;rsquo;t affect this behavior.&lt;/p&gt;
&lt;p&gt;You have to either use &lt;kbd&gt;⌘ Q&lt;/kbd&gt;, or right-click on the dock item and choose &amp;ldquo;close&amp;rdquo;.
What&amp;rsquo;s the red button in the window for then?
Usually, the app simply continues working in the background, as if minimized.&lt;/p&gt;
&lt;p&gt;Speaking of which, so far only the minimize button seems to work in a sane manner.&lt;/p&gt;
&lt;p&gt;Sure, there&amp;rsquo;s little point in actually closing applications, given that this laptop has a lot of RAM and CPU power, but it always felt weird to me that apps want to stay open all the time.
I&amp;rsquo;m done with you - why do you think that you&amp;rsquo;re so important that you shouldn&amp;rsquo;t be closed?
Do what you&amp;rsquo;ve been told.&lt;/p&gt;
&lt;h3 id=&#34;the-finder&#34;&gt;The Finder&lt;/h3&gt;
&lt;p&gt;Linux has always had weird file managers.
To me, nothing beats Microsoft&amp;rsquo;s Windows 7 file explorer - it was the least BS one in my opinion.
GNOME&amp;rsquo;s Nautilus, or Files, as it is called nowadays, had a few long-standing problems, some of which date back ten plus years.
KDE&amp;rsquo;s Dolphin was nice, but too had some weird quirks.
However, it&amp;rsquo;s nothing in comparison to macOS Finder.&lt;/p&gt;
&lt;p&gt;See the screenshot?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/finder.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;A normal file view, right?
Now, what should happen if I shrink the window?&lt;/p&gt;
&lt;p&gt;&lt;video autoplay loop muted&gt;&lt;source src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/finder-resize.mp4&#34; type=&#34;video/mp4&#34;&gt;
Your browser does not support the video tag.&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The what?
Who thought that this was a good idea?
For those who didn&amp;rsquo;t get it, the grid of items remains the same despite the size of the window!
Like, seriously?&lt;/p&gt;
&lt;p&gt;The oddities don&amp;rsquo;t end there.
Where&amp;rsquo;s the current path?
For some reason, you can only see it when holding the &lt;kbd&gt;⌥ option&lt;/kbd&gt;, it appears at the bottom of the window.
You can, in fact, toggle it in the settings so it is always shown, and while you can click on it to navigate, you can&amp;rsquo;t edit it, as it seems.&lt;/p&gt;
&lt;p&gt;What really baffles me though is that it seems impossible to shift-select multiple files in grid view - only in list view.
Even though, if you do shift-select files in list view, and switch back to the grid view, files stay selected.
Seems to be related to how the &lt;a href=&#34;#the-media-viewer&#34;&gt;media viewer works&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another oddity is that when moving directories around, there&amp;rsquo;s no option to merge directories with the same name by default.
I mean, when I move a directory to a different place, where another directory exists with the same name, I expect them to be merged, but the pop-up only says this:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/no-merge.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;When the &lt;kbd&gt;⌥ option&lt;/kbd&gt; key is pressed preemptively and held during the drag, the option to merge directories appears:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/merge.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Like, you need to know, before the hand, that the target location you&amp;rsquo;ve chosen contains a folder with the same name you&amp;rsquo;re moving right now.
And if you didn&amp;rsquo;t, you need to repeat the drag.
Why not show it by default?&lt;/p&gt;
&lt;p&gt;For the record, when the same thing is done in the GNOME file manager, it asks you if you want to merge directories, but you can&amp;rsquo;t replace them, so you can&amp;rsquo;t ever lose data.
And if it contains files with the same names, it asks for each file if you want to replace it, or keep both.
Of course, you can choose to replace all files, so it wouldn&amp;rsquo;t drag:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/gnome-merge.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Cutting files is also super weird.
You first copy the file as normal, then proceed to the directory you want to move the file, and press &lt;kbd&gt;⌘ command&lt;/kbd&gt;+&lt;kbd&gt;⌥ option&lt;/kbd&gt;+&lt;kbd&gt;v&lt;/kbd&gt;.
Not only does the shortcut itself make your hand curl up as if you were a lobster, but also the semantics of this operation feel extremely weird - you copied the file, after all, why would it move afterwards?
I guess, it&amp;rsquo; handy in cases when you thought you wanted to copy the file, but you actually decided to move it, so you don&amp;rsquo;t need to input a whole new shortcut again, but it&amp;rsquo;s a minor win in my book.&lt;/p&gt;
&lt;p&gt;Thankfully, I use graphical file managers rarely, so I can continue living in my comfort zone of Emacs&amp;rsquo; DIRED.&lt;/p&gt;
&lt;h4 id=&#34;files-and-folders&#34;&gt;Files and folders&lt;/h4&gt;
&lt;p&gt;But, while we&amp;rsquo;re at it, let&amp;rsquo;s talk about files in general.&lt;/p&gt;
&lt;p&gt;I have some local music stored in the &lt;code&gt;Music&lt;/code&gt; folder.
Some videos and films are stored in the &lt;code&gt;Videos&lt;/code&gt; folder.
I also have pictures stored in the &lt;code&gt;Pictures&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;Crazy, right?
Imagine using the file system to store files systematically?&lt;/p&gt;
&lt;p&gt;I also try to keep things organized, so I manage all these with some directories, like &lt;code&gt;band-name/album-name/track-name&lt;/code&gt; in case of music files, or &lt;code&gt;year/place-name/photo&lt;/code&gt; in case of photos.
I like it because if I want to listen to something, I can just drop the folder into the player, or open the image folder and relive those moments.
Organizing these with folders is also handy for backing these things up - just drop the folder to the external drive and you&amp;rsquo;re good to go.&lt;/p&gt;
&lt;p&gt;In macOS, as it seems, tags are the way the system wants to work with files.
It&amp;rsquo;s a different approach, and I&amp;rsquo;m not entirely opposed to that, since filesystems are an illusion, after all.&lt;/p&gt;
&lt;p&gt;But every once in a while, I open the &lt;code&gt;Music&lt;/code&gt; folder, and I see a second &lt;code&gt;Music&lt;/code&gt; folder inside it.
Apparently, it is created when opening the Music app.
I can&amp;rsquo;t use the Music app because it can&amp;rsquo;t play FLAC, which is what the majority of my local library is encoded in.&lt;/p&gt;
&lt;p&gt;When I open the &lt;code&gt;Videos&lt;/code&gt; folder, I often find a &lt;code&gt;TV&lt;/code&gt; folder inside.
I&amp;rsquo;m not sure what app creates it, but it wasn&amp;rsquo;t done by me, as I never launched the TV app.
And it appears there far more regularly than the &lt;code&gt;Music/Music&lt;/code&gt; one.&lt;/p&gt;
&lt;p&gt;Other apps often create their own folders anywhere they want.
The OrbStack app creates an &lt;code&gt;OrbStack&lt;/code&gt; folder in &lt;code&gt;$HOME&lt;/code&gt;.
Some other app I installed created a folder in the &lt;code&gt;Music&lt;/code&gt; folder for some reason, even though it didn&amp;rsquo;t store &lt;em&gt;music&lt;/em&gt; in it, just some audio files.&lt;/p&gt;
&lt;p&gt;It feels like that this is not my computer, but one I share with everyone else, and watch they do as they please with my filesystem.
Never once was this a problem in Linux.&lt;/p&gt;
&lt;p&gt;Why can&amp;rsquo;t apps keep their files to themselves?
I&amp;rsquo;m tempted to create things like &lt;code&gt;Music/My Music&lt;/code&gt;, or &lt;code&gt;Pictures/My Pictures&lt;/code&gt;, and use them specifically, forgetting about the default &lt;code&gt;Music&lt;/code&gt;, &lt;code&gt;Pictures&lt;/code&gt;, &lt;code&gt;Videos&lt;/code&gt; thing, but it is such a cumbersome solution.
It feels like, when you invite friends to a party, and they go to your bookshelf, and decide that they&amp;rsquo;ll put some of their own stuff on it for the time being.
And when they leave, they forget to take their stuff, but you don&amp;rsquo;t want to touch it, as it is not exactly yours.
And you can&amp;rsquo;t throw it out, as your friends will be mad.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know.&lt;/p&gt;
&lt;h3 id=&#34;the-media-viewer&#34;&gt;The media viewer&lt;/h3&gt;
&lt;p&gt;Oh, the image viewer.&lt;/p&gt;
&lt;p&gt;Let me ask you a question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You have a directory with some photos.
You want to go through these photos in the order taken and view them.
What do you do?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In Linux, or Windows for that matter, the answer is simple: you double-click on the image you want to start with, the image viewer is opened, and you can use the arrow keys to go back and forth.
Simple and effective!&lt;/p&gt;
&lt;p&gt;What happens when you do it in macOS Finder?
Well, the image viewer still opens, but the arrow keys do nothing!
No, in order to go through images, you need to select the items you want to view, hover over the File menu in the top bar, press the &lt;kbd&gt;⌥ option&lt;/kbd&gt; key, and select &amp;ldquo;Start slideshow &lt;code&gt;N&lt;/code&gt; items&amp;rdquo;:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/slideshow.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;HUH?
Slideshow is cumbersome, as it is a slideshow - it&amp;rsquo;s not meant for going through images manually.&lt;/p&gt;
&lt;p&gt;Alternatively, you can use a different viewer, called &amp;ldquo;Quick Look&amp;rdquo;, which supports arrow keys.
Horray!
But what is it?
If you&amp;rsquo;re in grid view, why are the arrow keys following the grid too?&lt;/p&gt;
&lt;p&gt;&lt;video autoplay loop muted&gt;&lt;source src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/quick-view.mp4&#34; type=&#34;video/mp4&#34;&gt;
Your browser does not support the video tag.&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HUUUUUH&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;Who thought that this was a good idea?
Hopefully, I won&amp;rsquo;t repeat this question all too many times.&lt;/p&gt;
&lt;p&gt;The only reasonable way of using it is by switching to the list view and using the up and down arrow keys.
Which is still not great, because Quick View is meant to be viewed temporarily.
Any accidental click outside of it will close it, and you&amp;rsquo;ll have to find where you were and start from there.&lt;/p&gt;
&lt;p&gt;You can put it into fullscreen mode, but then the arrow keys stop working.
This seems like such a basic feature for an image viewer, but somehow they&amp;rsquo;ve managed to make it unusable.
Not only that, but if this folder has any non-image files, like text files or PDFs, they&amp;rsquo;ll be included in the quick view too.&lt;/p&gt;
&lt;p&gt;I understand that the purpose of Quick View is to view files quickly, and often it is a great thing to have, but at the same time, it&amp;rsquo;s super weird in behavior.&lt;/p&gt;
&lt;h3 id=&#34;hardware-accessibility&#34;&gt;Hardware accessibility&lt;/h3&gt;
&lt;p&gt;OK, I can go on and on about the software part of the os, but to be honest, all of this is covered by other people rather well.
And, in truth, I couldn&amp;rsquo;t be bothered to try all other preinstalled software, because it&amp;rsquo;ll take a lot of time to put it into this already long article, but also because I don&amp;rsquo;t want to.
For instance, the Music app is also weird, and I already have an &lt;a href=&#34;https://andreyor.st/posts/2023-11-19-linux-music-players/&#34;&gt;extensive post about how Linux music players are weird&lt;/a&gt;, so it goes without saying that I&amp;rsquo;ll need to replace it with something else.
Like, the Quick View can play FLAC without any issues, but the Music app (formerly iTunes, I believe) can&amp;rsquo;t.
Even GNOME&amp;rsquo;s player could do that, and it kinda copies Apple&amp;rsquo;s Music app in some aspects.
Reencoding all my library into ALAC - Apple&amp;rsquo;s equivalent of FLAC for lossless audio is not an option for me.
I did it once before when I was a &lt;em&gt;happy owner&lt;/em&gt; of an old 4th-gen iPod touch, but never again - it is pointless, there are better music players out there.&lt;/p&gt;
&lt;p&gt;So to me, the silver lining is that GNOME&amp;rsquo;s inbuilt software is in a lot of ways better than one in macOS.
And KDE has even better software than GNOME in a lot of cases.
So instead, let&amp;rsquo;s talk about hardware oddities and their operating system counterparts.&lt;/p&gt;
&lt;h4 id=&#34;the-microphone&#34;&gt;The Microphone&lt;/h4&gt;
&lt;p&gt;So, while I do care about my privacy, I can&amp;rsquo;t call myself a privacy maniac.
However, one thing I like to have in my devices is the ability to mute the microphone.
It&amp;rsquo;s best when there&amp;rsquo;s a hardware switch for that, but it&amp;rsquo;s so rare that I&amp;rsquo;ve only seen it once in my life on some Lenovo laptop.&lt;/p&gt;
&lt;p&gt;I mean, it&amp;rsquo;s simply more convenient to do that than switch to a specific application that currently uses the microphone and toggle it there.&lt;/p&gt;
&lt;p&gt;So, on Linux, I used the microphone key found on my laptop&amp;rsquo;s keyboard.
It muted the microphone in the system settings.&lt;/p&gt;
&lt;p&gt;When I got my Mac, I examined the keyboard, and found almost all usual shortcuts - you can change screen brightness on &lt;kbd&gt;F1&lt;/kbd&gt; and &lt;kbd&gt;F2&lt;/kbd&gt;, you can change volume on &lt;kbd&gt;F11&lt;/kbd&gt; and &lt;kbd&gt;F12&lt;/kbd&gt;, and there&amp;rsquo;s the microphone icon on &lt;kbd&gt;F5&lt;/kbd&gt;.
So I naturally assumed that this was a button to mute the microphone, after all, my old laptop had it too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I assumed wrong.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s &lt;strong&gt;no way&lt;/strong&gt; to disable the microphone in macOS via inbuilt means.
The only thing you can do is to manually set the volume input gain level to 0, effectively making the microphone deaf.&lt;/p&gt;
&lt;p&gt;Wanna know what the microphone button does then?
I&amp;rsquo;ll show you:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/dictation.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Yup, it asks you if you want to use the speech-to-text feature.
&lt;em&gt;Cool&lt;/em&gt;, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And you can&amp;rsquo;t change what this button does.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can set a different shortcut for dictation in the settings, but look what happens when you press the microphone key:&lt;/p&gt;
&lt;p&gt;&lt;video autoplay loop muted&gt;&lt;source src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/dictationship.mp4&#34; type=&#34;video/mp4&#34;&gt;
Your browser does not support the video tag.&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;It changes itself back to this button if you click &amp;ldquo;don&amp;rsquo;t ask again&amp;rdquo;!
And then it asks you again once you press it!
So what was the point of providing &amp;ldquo;don&amp;rsquo;t ask again&amp;rdquo; then?&lt;/p&gt;
&lt;p&gt;Though I&amp;rsquo;ve managed to defeat this beast of a problem by writing some AppleScript.
This script can be executed in the Shortcuts app, and by analyzing its output, we can rename the shortcut accordingly:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/shortcuts.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;After adding this shortcut to the &amp;ldquo;Controls&amp;rdquo; menu, it acts as a mute button:&lt;/p&gt;
&lt;p&gt;&lt;video autoplay loop muted&gt;&lt;source src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/shortcut.mp4&#34; type=&#34;video/mp4&#34;&gt;
Your browser does not support the video tag.&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Works well so far, and even remembers the current input level value, so I can still adjust it if needed.
Even more, it works better than it did on Linux, because it constantly forgot what level the mic was set to and chose a default value of 20%, which was too low.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a text version of the script, if anyone needs it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-applescript&#34; data-lang=&#34;applescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;on&lt;/span&gt; &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;run&lt;/span&gt; {&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;input&lt;/span&gt;, &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;parameters&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;font-weight:bold&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;currentVolume&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;to&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;input&lt;/span&gt; volume &lt;span style=&#34;font-weight:bold&#34;&gt;of&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;get volume settings&lt;/span&gt;) &lt;span style=&#34;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;currentVolume&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;is&lt;/span&gt; 0 &lt;span style=&#34;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;font-weight:bold&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;storedVolume&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;to&lt;/span&gt; &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;do shell script&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;defaults read com.andreyorst.mictoggle volume&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;on&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;error&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;font-weight:bold&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;storedVolume&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;to&lt;/span&gt; 50
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;do shell script&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;defaults write com.andreyorst.mictoggle volume -int &amp;#34;&lt;/span&gt; &amp;amp; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;storedVolume&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;set volume&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;input&lt;/span&gt; volume &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;storedVolume&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Unmute&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;font-weight:bold&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;do shell script&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;defaults write com.andreyorst.mictoggle volume -int &amp;#34;&lt;/span&gt; &amp;amp; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;currentVolume&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;set volume&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;input&lt;/span&gt; volume 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Mute&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Error&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;run&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I can&amp;rsquo;t share the whole script because no way I&amp;rsquo;m making an iCloud account for that.&lt;/p&gt;
&lt;h4 id=&#34;the-keyboard-layout&#34;&gt;The keyboard layout&lt;/h4&gt;
&lt;p&gt;Not strictly about the hardware part, since the keyboard itself is pretty good.
It&amp;rsquo;s a bit of a shame that Apple stopped using their Butterfly switches, as I did like them more than the current scissor ones, but the keyboard is still better than the one on my old laptop.&lt;/p&gt;
&lt;p&gt;One thing that bothers me, and which I got very accustomed to, is the fact that GNOME can remember keyboard layout per window:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/remember-layout.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;MacOS can&amp;rsquo;t do that - and I don&amp;rsquo;t know what is the reason for that.
The settings page for layouts has a setting called &amp;ldquo;Automatically switch to a document&amp;rsquo;s input source&amp;rdquo;:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/layout-settings.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;However, I&amp;rsquo;m not sure what it does.
It seems to work sometimes, but other times it does the opposite of what I want to.
I only have two layouts.
Is it that hard to keep track of that amount of data per window?&lt;/p&gt;
&lt;h4 id=&#34;the-keyboard-layout--physical&#34;&gt;The keyboard layout (physical)&lt;/h4&gt;
&lt;p&gt;Though let&amp;rsquo;s touch the keyboard one more time.
I know that Apple has at least two keyboard layouts for different markets - one for the US and one for Europe.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/us-layout.png&#34;
         alt=&#34;Figure 11: US layout&#34; width=&#34;100%&#34; height=&#34;auto&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 11: &lt;/span&gt;US layout&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-11-23-gnome-is-better-macos-than-macos/europe-layout.png&#34;
         alt=&#34;Figure 12: European layout&#34; width=&#34;100%&#34; height=&#34;auto&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 12: &lt;/span&gt;European layout&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I specifically wanted the US layout, because all other laptops and standalone keyboards I had in my entire life had this kind of layout.
Never once in my life have I seen the European layout on any device.&lt;/p&gt;
&lt;p&gt;But, for some unknown reason, Apple sells its laptops with a European layout in my area, and when my wife had a Mac, it had that.
Every time I used it, I was fighting my muscle memory.&lt;/p&gt;
&lt;p&gt;Sure, it has more keys, and these contain some characters otherwise inaccessible without switching the layout, so it can look like Apple does a good thing.
However, literally no one except Apple users had seen this layout, and it makes no sense to them.
In my particular case, some already existing keys in the European layout are placed differently, like &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, since I didn&amp;rsquo;t want to deal with that, I got myself a version with a US layout.
Thankfully, there&amp;rsquo;s a &amp;ldquo;PC&amp;rdquo; layout in the system settings, so I don&amp;rsquo;t have to relearn anything.
Using the &amp;ldquo;PC&amp;rdquo; layout on a European keyboard, however, is even more cumbersome.&lt;/p&gt;
&lt;h4 id=&#34;no-middle-click-on-the-touchpad&#34;&gt;No middle click on the touchpad&lt;/h4&gt;
&lt;p&gt;For some reason, there&amp;rsquo;s no such thing as a middle-click on a touchpad.
In Linux, it is usually mapped to clicking with three fingers at the same time.
I&amp;rsquo;ve been using it in Firefox a lot, and to paste stuff into the terminal once in a while.&lt;/p&gt;
&lt;p&gt;However, in macOS, my muscle memory is now against me.
I click with three fingers in Firefox, and usually it brings up the right-click menu, which is fine at least.
But sometimes it is registered as a left-click instead, and it is super annoying.
Re-learning to use @@html:&amp;lt;kbd&amp;gt;⌘ command&amp;lt;/kbd&amp;gt; click will take some time, I guess&lt;/p&gt;
&lt;h4 id=&#34;the-camera-cutout&#34;&gt;The camera cutout&lt;/h4&gt;
&lt;p&gt;Same as with the microphone, I&amp;rsquo;d like to have a physical way of blocking the camera, but because Apple moved towards a custom-shaped display matrix, it can&amp;rsquo;t be done as easily as before.
It has a lot of sensors there, and blocking them with a huge plastic cover will make it impossible to close the laptop lid.&lt;/p&gt;
&lt;p&gt;Again, not strictly about the hardware side of things, but the display in general feels weird because of this notch - it&amp;rsquo;s right in the middle of the screen, and I&amp;rsquo;m used to having the clock there, after years of using GNOME.
Some say it&amp;rsquo;s more logical to have them at the far right side, as it is commonly done on phones, but phones have a much smaller and narrower screen.
The same goes for notifications - having them in the middle feels more natural to me, because most of the content I&amp;rsquo;m working with is somewhere in the middle.
But your opinion may vary (as if anything else I said here can be universally agreed on).&lt;/p&gt;
&lt;h3 id=&#34;the-good-parts&#34;&gt;The good parts&lt;/h3&gt;
&lt;p&gt;OK, enough bitching around, let&amp;rsquo;s discuss some good parts.&lt;/p&gt;
&lt;p&gt;The screen itself is gorgeous, colors are vivid yet natural, and the 14-inch model is actually both a bit bigger and a bit taller than my old 13-inch laptop.
I like the screen a lot.
I didn&amp;rsquo;t go for the nano-texture display, as I don&amp;rsquo;t work in environments where reflections are problematic, so I can&amp;rsquo;t say how it affects image quality.
Maybe if I did, it&amp;rsquo;d be in the bad section.&lt;/p&gt;
&lt;p&gt;The speakers are pretty good.
And for a laptop, they&amp;rsquo;re actually amazing.
They have depth, the upmp, and are loud enough to watch films with comfort - can&amp;rsquo;t say this about any other laptop I&amp;rsquo;ve had before.&lt;/p&gt;
&lt;p&gt;Touchpad - oh, the touchpad.
Simply put, the fact that it is a lot bigger (in fact, they had even bigger ones before), and I can press on it absolutely everywhere, is amazing.
I was also surprised to learn that they aren&amp;rsquo;t actually pressed in physically.
Instead, it is a glass surface, which has haptic feedback that simulates the click.
Purely amazing.
I&amp;rsquo;ve spent a long time trying to find anything remotely close to these touchpads on other laptops, but I never could find one.&lt;/p&gt;
&lt;p&gt;M4 Pro CPU is a beast.
Recently, I forgot a passphrase to one of my GPG keys and tried to brute-force it.
My old laptop had AMD Ryzen™ 7 3750H, which isn&amp;rsquo;t fast by today&amp;rsquo;s standards, but it wasn&amp;rsquo;t a slow CPU either - it handled all of my tasks without major hiccups.
I tried it first on the old laptop, and it could try about ~45 passphrases per second.
The M4 could try ~750 passphrases per second.
Everything is &lt;strong&gt;so fast&lt;/strong&gt; I can&amp;rsquo;t believe it.&lt;/p&gt;
&lt;p&gt;The macOS itself isn&amp;rsquo;t all that bad.
As a matter of fact, writing this post helped me find how to fix a lot of problems I initially had - this post was a lot longer before that, but I had to cut stuff from it, as it was no longer applicable.
There are still some oddities - it constantly bothers me with asking permission to run apps for the first time, because I install them through &lt;code&gt;brew&lt;/code&gt;, or downloaded from the official website (again, I&amp;rsquo;m not making an Apple account, no, no).
But I finally have a machine on which I can do both my programming tasks and media tasks without needing to keep two different operating systems to handle each task specifically.
Finally, I can record my music with my right hand while writing code with the left - a dream.&lt;/p&gt;
&lt;p&gt;Even the liquid glass thing is not as bad as I thought it would be.
Thankfully, Apple added a tinted style to it, so it is less transparent, which helps readability.&lt;/p&gt;
&lt;p&gt;And overall, I really like the device - it feels solid, and that it will last me another five years at minimum.
Hopefully I didn&amp;rsquo;t jinx myself here, though.&lt;/p&gt;
&lt;p&gt;Anyhow, thanks for reading, and please do share your experience with me, or if you know ways of solving the problems I&amp;rsquo;ve listed, I would be glad to know about them!&lt;/p&gt;&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: GNOME is better macOS than macOS&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Sun, 23 Nov 2025 02:57:00 +0300</pubDate>
    </item><item>
      <title>Am I becoming obsolete or just older?</title>
      <link>https://andreyor.st/posts/2025-04-22-am-i-becoming-obsolete-or-just-older/</link>
      <guid>https://andreyor.st/posts/2025-04-22-am-i-becoming-obsolete-or-just-older/</guid>
      <description>&lt;p&gt;Let&amp;rsquo;s set the tone for this post right away, I just want to get this out of my system: I despise the rise of AI in tech.
Coding assistants, chat-bots, vibe-coding, agents.
Oh, the vibe coding&amp;hellip; WTF?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-04-22-am-i-becoming-obsolete-or-just-older/old_man_yells_at_openai.png&#34;
         alt=&#34;Figure 1: This will be an &amp;amp;ldquo;old man yells at cloud&amp;amp;rdquo; type of post.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 1: &lt;/span&gt;This will be an &amp;ldquo;old man yells at cloud&amp;rdquo; type of post.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I don&amp;rsquo;t like AI in general, although I know that neural networks can be used for good.
Before you accuse me that I&amp;rsquo;m completely anti-AI, let me explain where I see this technology applicable.&lt;/p&gt;
&lt;p&gt;One such example is modeling.
I play guitar, and it&amp;rsquo;s a pretty expensive hobby.
A good guitar itself can cost hundreds, or thousands of dollars, but then there&amp;rsquo;s guitar gear.
Tube amplifiers and pedals are &lt;strong&gt;expensive&lt;/strong&gt;.
Not everyone has money to have several amplifiers for achieving versatile sound, and a bunch of effect pedals to work with these amplifiers.&lt;/p&gt;
&lt;p&gt;The industry noticed that, and a lot of effort was put into modeling these circuits in software.
Guitar pedals and amplifiers are basic electronics, for the most part - cascading transistors, condensers, operational amplifiers, etc.
It&amp;rsquo;s possible to model them accurately in software, and I did so myself, implementing Maxon&amp;rsquo;s OD808 overdrive pedal in C++ as my diploma project for graduation.
The diploma itself was about precise software modeling of analog systems, and the guitar pedal was a practical example I could show.
You should have seen the faces of the diploma committee when I pulled out a guitar for a project demonstration.
Better, you should have seen MY face, when the head of department took my guitar and started blasting Iron Maiden riffs.&lt;/p&gt;
&lt;p&gt;However, modeling these schematics accurately and working in real-time is hard.
Especially, when we&amp;rsquo;re talking about emulating tubes or tape machines.
People still did, but the results were not as close to the real thing, as one might like, and sound engineers are picky for the details.
But then there was another way of modeling - feed the raw signal through your equipment of choice, be it a tube amp, or some weird pedal schematic, capture the result, and train a neural network so it could reproduce it flawlessly.
This is the technology behind &lt;a href=&#34;https://www.neuralampmodeler.com/&#34; target=&#34;_blank&#34;&gt;Neural Amp Modeler&lt;/a&gt; and other such projects.&lt;/p&gt;
&lt;p&gt;This is where neural network technology shines in my opinion - you get accurate results, even though accurately emulating entire circuits with tubes in real time is still a hard task for programmers.
It sure can be done manually, and there are successful examples, where individual components of the circuit are programmed as separate modules, and then the whole circuit is constructed from them.
That&amp;rsquo;s the approach I took with my diploma project as well, but it still takes much more time than just training a model with a pair of &lt;code&gt;.WAV&lt;/code&gt; files.
And the neural network method comes with another real benefit allowing you to capture your sound exactly as is for later use.
For example, if you&amp;rsquo;re recording at the studio, and you want to patch things later or re-record certain parts, you can capture your studio sound, and then reproduce it later.&lt;/p&gt;
&lt;p&gt;There are other fields, where neural networks can be helpful, I won&amp;rsquo;t go listing all such cases, I&amp;rsquo;m just going into this tangent so that you understand that I&amp;rsquo;m not against neural networks in general.
However.&lt;/p&gt;
&lt;p&gt;When ChatGPT3 was first announced, people started going crazy.
We had no real breakthroughs for quite a few years, GPT2 wasn&amp;rsquo;t really good.
Image and video generation models weren&amp;rsquo;t good either.
Then suddenly, GPT3 talks close to being confused with a real person, and images generated by other models start looking correct, and not like a mush of pixels.
People notice that GPT3 can generate code in most programming languages, and make attempts at problem-solving and such.&lt;/p&gt;
&lt;p&gt;Fast forward a few months, and we have first coding assistants and IDEs with a primary focus on AI for development and refactoring.
The internet is becoming worse by the day - AI-generated articles pollute search results, and you don&amp;rsquo;t know if the thing is real or not when looking at &amp;ldquo;drawings&amp;rdquo;.
AI-generated music pollutes streaming services, often sounding awfully lot like some other artist, without acknowledging that it was trained on their music.
Some streaming services even announced that now you can remix the music with AI if you have a paid plan.
People accuse one another of the use of AI to pretend that they know how to do what they do.
The worst.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-04-22-am-i-becoming-obsolete-or-just-older/youtube_comment.png&#34;
         alt=&#34;Figure 2: A comment to the Generative AI is a Parasitic Cancer video by Freya Holmér&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 2: &lt;/span&gt;A comment to the &lt;a href=&#34;https://www.youtube.com/watch?v=-opBifFfsMY&#34; target=&#34;_blank&#34;&gt;Generative AI is a Parasitic Cancer&lt;/a&gt; video by &lt;a href=&#34;https://www.youtube.com/@acegikmo&#34; target=&#34;_blank&#34;&gt;Freya Holmér&lt;/a&gt;&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then, there&amp;rsquo;s the new hot thing - vibe coding.
I&amp;rsquo;m living under a rock, I know that, because I don&amp;rsquo;t follow news, tech hype reaches me mostly via gossip in real life.
So when I first heard the term vibe coding, I naturally assumed that it&amp;rsquo;s another one of these &amp;ldquo;Lofi girl&amp;rdquo;-type work setups where you have a comfy environment for coding with good vibes.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-04-22-am-i-becoming-obsolete-or-just-older/lofi_girl.png&#34;
         alt=&#34;Figure 3: this kind of stuff, just imagine she&amp;amp;rsquo;s programming. I guess, I could generate an image where she&amp;amp;rsquo;s programming for real, but then this post would be hypocrisy.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 3: &lt;/span&gt;this kind of stuff, just imagine she&amp;rsquo;s programming. I guess, I could generate an image where she&amp;rsquo;s programming for real, but then this post would be hypocrisy.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This is pretty much what I do when I have free time to work on my personal projects.
I prepare myself a nice cup of coffee with steamed milk and peanut butter&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, sit down at my desk, put on headphones, and enjoy my coding time in the late evening.
Vibes, right?
Nope!
Apparently, vibe coding is when you explain to your AI-based editor what the heck you wanted to do, except you&amp;rsquo;re much more into debugging code written for you.
WTF it is calling &lt;em&gt;vibe coding&lt;/em&gt;?
I wouldn&amp;rsquo;t mind it being called AI coding, but &lt;em&gt;vibe&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;I get it, AI is a hot new thing, and people want to use new shiny things because it triggers a dopamine hit.
But toying with something for fun, or being passionate about things should still remain reasonable.
It&amp;rsquo;s fine when you do this for fun, or sparingly, but when all you do is now done by AI, it starts getting a bit too far in my book.&lt;/p&gt;
&lt;p&gt;I see AI-generated images in presentations and I cringe.&lt;/p&gt;
&lt;p&gt;I see how people brag about how they used AI to do &amp;ldquo;this and that&amp;rdquo;, even though the task at hand was easy and I cringe.&lt;/p&gt;
&lt;p&gt;I see how people misuse AI for harder tasks that can still be done without AI perfectly fine, with a much lower error rate, and I cringe again.&lt;/p&gt;
&lt;p&gt;I see posts about how vibe coding brings back joy to programming, and I only have one question - what joy?
If you did enjoy programming, but felt down recently, I think AI is going to make you feel better in the short term, but burndown will hit you again soon enough when the novelty wears off.
And if you didn&amp;rsquo;t enjoy programming in the first place, how AI is going to help you enjoy it?
Because you don&amp;rsquo;t have to do it manually?&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t enjoy the process, AI won&amp;rsquo;t make it more enjoyable.
You will simply offload the unenjoyable part to AI, and then gaslight yourself.&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;Phew.&lt;/p&gt;
&lt;p&gt;These are the kinds of thoughts I had in my head lately.
And when I&amp;rsquo;m annoyed at something for no apparent reason I usually start reflecting on it in hopes to understand the source of the problem for me.
Worrying over nothing is the worst thing you can do for your mental health.
Like, If I don&amp;rsquo;t use AI, I can choose not to care about others who use AI, and continue to live my life like I did before.
That&amp;rsquo;s what I did before, like when blockchain and NFT were the hot new topics in town.&lt;/p&gt;
&lt;p&gt;However, AI-assisted coding practically hurts me even without me participating in it.
First, I chose to ignore AI assistants, heck I don&amp;rsquo;t even use non-AI assistants such as &lt;abbr title=&#34;Language Server Protocol&#34;&gt;LSP&lt;/abbr&gt; that much.
So when I talk to people, who use AI, I get weird looks, like: &amp;ldquo;Man, you&amp;rsquo;re gonna be obsolete in 5 years if you don&amp;rsquo;t learn how to use AIs, it makes you much more efficient&amp;rdquo;.
Am I?&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think efficiency is in speed only - it doesn&amp;rsquo;t really matter how fast you can produce code.
Programming is mostly thinking a lot, and then writing a little.
It sure does matter to a business, because today&amp;rsquo;s main tactic is &lt;abbr title=&#34;Time To Market&#34;&gt;TTM&lt;/abbr&gt; optimization, but that&amp;rsquo;s another topic.&lt;/p&gt;
&lt;p&gt;I think relying on AIs for coding is going to hurt us in the long run.
Count me old-school, but I believe that today a lot of people in the programming world don&amp;rsquo;t know how to code.
You&amp;rsquo;d be surprised how little people know these days.
Ask them the difference between an array and a vector, and they won&amp;rsquo;t be able to tell the difference.
Don&amp;rsquo;t get me started on advanced topics like asynchronous/parallel programming, or even more basic features like closures.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve seen a lot of people who think that they know what they&amp;rsquo;re doing, but when asked about certain internal characteristics of the language/framework/library they use, that did matter to the problem at hand, they stumbled and couldn&amp;rsquo;t answer.
And I&amp;rsquo;m no better, I was there too - I&amp;rsquo;ve been writing code for bare-metal in C during the first two years at my first-ever job and didn&amp;rsquo;t understand anything.
Only after I delved deeply into the language and SOC stuff, I started understanding what and why.
It improved my results at work and tremendously helped me beyond just C later.
So I believe, that people can learn, but would they if everything is done by AI?&lt;/p&gt;
&lt;p&gt;I can confidently say, that I know Lua deeply enough to understand how its bytecode VM works.
I also can confidently say, that I know how most of Clojure works internally.
But I still can&amp;rsquo;t confidently say that I&amp;rsquo;m a great programmer.
A decent one, maybe, but I still don&amp;rsquo;t know a lot of stuff behind the scenes.
This is why I constantly delve deeper into the technology I&amp;rsquo;m using, trying to understand the inner workings of it.&lt;/p&gt;
&lt;p&gt;And now we have AI assistants, practically writing programs for these people, and they can&amp;rsquo;t reason about the code written by hand, let alone generated code.
I&amp;rsquo;ve seen people applying for a job, and failing miserably on the simplest questions on the coding interview.
They can output the solution for tricky Leetcode tasks, but can&amp;rsquo;t explain them.
Some are even proud that they can make AI assistants solve the problem for them without them even trying to understand the problem.
I, myself, can&amp;rsquo;t solve these Leetcode problems, but I still can write robust code for the real-world task at hand.&lt;/p&gt;
&lt;p&gt;Recently, I encountered a message in a group chat, that pretty much triggered me to write this post, with the following contents:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are orders of magnitude fewer code examples for ClojureDart, which makes it very difficult to train LLMs.
Examples from regular Clojure will probably be pulled up, but the libraries won&amp;rsquo;t work - you&amp;rsquo;ll need to port them first.&lt;/p&gt;
&lt;p&gt;For a &lt;strong&gt;beginner&lt;/strong&gt;, this is &lt;strong&gt;a very large and painful entry threshold&lt;/strong&gt;, especially &lt;strong&gt;if there is no desire to write the code yourself and delve deeply into the language.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh, how did people even learn to code before LLMs existed?
I imagine it was so hard and painful!
Imagine - learning the language by writing code and understanding what you&amp;rsquo;re doing, am I right?
This is so wrong on so many levels.&lt;/p&gt;
&lt;p&gt;First of all, if there is no desire to write the code yourself, why the heck do you want to be a programmer?
This code is going to run on a machine, so it should be well understood, otherwise it&amp;rsquo;s a time bomb waiting to explode.
And with LLMs and vibe coding, the whole point is that you don&amp;rsquo;t look into the code that much.
Or at all.&lt;/p&gt;
&lt;p&gt;And, I&amp;rsquo;m not talking about using LLMs - even without them hallucinating a solution today we can&amp;rsquo;t write reliable software that works properly all of the time.
I get frequent crashes when doing mundane tasks - if I were to take a screenshot every time a program hangs or crashes, making me lose my work, my screenshot folder would fill up my hard drive.
Most programs today can&amp;rsquo;t achieve even &lt;a href=&#34;https://en.wikipedia.org/wiki/High_availability#%22Nines%22&#34; target=&#34;_blank&#34;&gt;&amp;ldquo;four nines&amp;rdquo;&lt;/a&gt;, let alone five or six nines of uptime.
Your Windows laptop will suddenly reboot to install updates, so even if your application can theoretically achieve &amp;ldquo;five nines&amp;rdquo;, the platform it is running on can&amp;rsquo;t.
I lost work due to Windows updating the .NET Framework, why the heck does it require a reboot?&lt;/p&gt;
&lt;p&gt;Today&amp;rsquo;s software is in a miserable state.
We have a lot of over-engineered stuff at the base of our software.
Legacy code creeps in, making writing new code harder.
Bugs are today&amp;rsquo;s norm - I&amp;rsquo;m still amazed that everything is holding up, instead of falling apart.&lt;/p&gt;
&lt;p&gt;If engineers were as bad as programmers today, we would all be &lt;strong&gt;dead&lt;/strong&gt;, lying flattened under collapsed buildings.
Oh, wait&amp;hellip;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;So am I becoming obsolete or just getting older and more grumpy?
I guess it&amp;rsquo;s both.&lt;/p&gt;
&lt;p&gt;I always wondered when would I become one of those old people who don&amp;rsquo;t understand modern technology.
I was born in the 90s, and saw firsthand how technology became more and more advanced - I had access to a computer since the age of three.
My folks don&amp;rsquo;t understand modern social networks that well, let alone all this AI stuff.
Now I&amp;rsquo;ve become a part of the club, I guess - I also don&amp;rsquo;t understand modern social networks and all this AI stuff.
Heck, my dad is probably more advanced in tech than me, as he&amp;rsquo;s been doing this smart-home thing for a while now, and I&amp;rsquo;m not understanding any of it.&lt;/p&gt;
&lt;p&gt;Programming is both a job and a hobby of mine, I like writing code to solve problems I often impose it on myself for the fun of it.
I like to think about solutions, and if I can&amp;rsquo;t do it myself, I search online like in the good old days, learning in the process.&lt;/p&gt;
&lt;p&gt;You can argue, that an AI chatbot is a glorified search interface, except it&amp;rsquo;s not exactly that in my opinion.
Today&amp;rsquo;s AI chatbots don&amp;rsquo;t just present you with existing information, instead, they reconstruct it.
LLMs are more or less a decompression algorithm, except not precise in most cases.
They can hallucinate a solution, and still sound as confident as if it was true.
Keep in mind, that an AI chatbot doesn&amp;rsquo;t &lt;em&gt;understand&lt;/em&gt; what it&amp;rsquo;s outputting, for now at least.
People do that too, unfortunately, but at least they don&amp;rsquo;t sound as confident when they clearly don&amp;rsquo;t know what they talking about (yeah, sure).&lt;/p&gt;
&lt;p&gt;Sometimes, beginner programmers who only know how to code with LLMs face the harsh reality, that LLMs can&amp;rsquo;t solve all problems.
For now.
Maybe in the upcoming years, large language models will become so sophisticated that it would be possible.
I don&amp;rsquo;t know.
And If such a beginner realizes, that they still need to learn things, the internet is already full of articles generated with AIs by people who don&amp;rsquo;t understand the topic, making it much harder for them to learn properly.
I&amp;rsquo;m facing this problem too, each time I want to learn something new, figuring out halfway through the article that it is an AI slop.&lt;/p&gt;
&lt;p&gt;Maybe one day I will have to use AI because my job will demand it, and I will change my opinion on this whole thing.
Or maybe I&amp;rsquo;ll change the job instead, depending on the circumstances.
Blacksmithing looks interesting.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2025-04-22-am-i-becoming-obsolete-or-just-older/blacksmithing.jpg&#34;
         alt=&#34;Figure 4: A poker I made&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 4: &lt;/span&gt;A poker I made&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Or maybe woodworking?
Or pottery?
Unless AI would do these things too by that time.
Of course not.&lt;/p&gt;
&lt;p&gt;Of course not.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;BTW, amazing recipe: 12 grams of coffee beans, grounded for double shot espresso, about 35-40 grams of salty peanut butter, and steamed milk. Nice texture and taste.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;AI is apparently used in engineering too now.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Am I becoming obsolete or just older?&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Tue, 22 Apr 2025 00:36:00 +0300</pubDate>
    </item><item>
      <title>GNOME Software</title>
      <link>https://andreyor.st/posts/2023-12-06-gnome-software/</link>
      <guid>https://andreyor.st/posts/2023-12-06-gnome-software/</guid>
      <description>&lt;p&gt;Have you tried using the GNOME Software?
This thing:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-12-06-gnome-software/gnome-software.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Do you use it?
Are you even using GNOME?&lt;/p&gt;
&lt;p&gt;Oh, sorry, I think should point this out, it&amp;rsquo;s kinda important - I&amp;rsquo;m asking the developers of GNOME Software.&lt;/p&gt;
&lt;p&gt;Because, apparently, they don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;OK, let&amp;rsquo;s start this post over.&lt;/p&gt;
&lt;div class=&#34;title-group&#34;&gt;
  &lt;h1 class=&#34;title&#34;&gt;GNOME Software&lt;/h1&gt;
  &lt;div class=&#34;sub-title&#34;&gt;
    &lt;div class=&#34;date&#34;&gt;
      &lt;time datetime=&#34;2023-12-06T19:12:00+0300&#34; class=&#34;post-date&#34; title=&#34;Last modified at Wed, Dec 6, 20:50, 2023&#34;&gt;
        Wed, Dec 6, 2023
      &lt;/time&gt;
    &lt;/div&gt;
    &lt;div class=&#34;tags&#34;&gt;
      &lt;span class=&#34;horizontal-links links&#34;&gt;
        &lt;a href=&#34;https://andreyor.st/categories/rant/&#34;&gt;@rant&lt;/a&gt;
        &lt;a href=&#34;https://andreyor.st/tags/gnome/&#34;&gt;GNOME&lt;/a&gt;
        &lt;a href=&#34;https://andreyor.st/tags/tools/&#34;&gt;tools&lt;/a&gt;
        &lt;span title=&#34;~1876 words&#34;&gt;~8 minutes read&lt;/span&gt;
      &lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;GNOME Software, or, just, Software, is an application that &amp;ldquo;allows you to find and install new apps and system extensions and remove existing installed apps&amp;rdquo;.
I mean, that&amp;rsquo;s what description is given to it on the &lt;a href=&#34;https://apps.gnome.org/Software/&#34; target=&#34;_blank&#34;&gt;official project page&lt;/a&gt;.
It is preinstalled on most distributions that use the GNOME desktop environment, except for, maybe Ubuntu, which has its own store for applications.
But I&amp;rsquo;m not going to touch Ubuntu here, I don&amp;rsquo;t really want to touch Ubuntu at all, to be honest, although I have to use it five days a week.&lt;/p&gt;
&lt;p&gt;At home, I use the Fedora Silverblue.
I like Fedora, in my opinion, it is one of the finest out-of-the-box-oriented distributions today because of how pure it is.
No fancy patches, or extensions, just pure GNOME as it is.
In addition to that, the Silverblue variant is immutable, so in addition to providing pure GNOME, it barely provides anything else.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve picked Silverblue because I like to keep my system clean, and immutability helps tremendously.
Everything is either a Flatpak or lives inside the Podman container.
To be frank, even before I used Silverblue I had the same kind of workflow on the regular Fedora distribution, and the same applies to Ubuntu that I use at work.
If your system got messed up enough for you to care you can just delete the container, and recreate a new one, installing only the stuff you do actually require.
But I&amp;rsquo;m getting off track here.&lt;/p&gt;
&lt;p&gt;I feel that the fact that I&amp;rsquo;m using Fedora Silverblue is important for this post because this means that I&amp;rsquo;m using a distribution that I can&amp;rsquo;t make changes to.
Well, I can, of course, but I don&amp;rsquo;t do that.
Again, all my apps are either in Flatpaks or containers, so the system itself is barely different from the fresh installation.
I had installed updates, but these come from Fedora, so I&amp;rsquo;m assuming they know what they&amp;rsquo;re doing.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s get back to the topic - I install apps as Flatpaks quite often.
I do so using GNOME Software!&lt;/p&gt;
&lt;p&gt;And it is &lt;strong&gt;pain&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;I have used GNOME since 2008 or 2009, can&amp;rsquo;t remember that exactly, so I&amp;rsquo;m a seasoned Linux user.
I have used a lot of different distributions, and for the past five years, Fedora has been my daily driver because it is simple, reliable, and has everything I need.
And what it doesn&amp;rsquo;t have, I can build from sources in the container with my own hands, so I don&amp;rsquo;t give a shit about stuff like AUR, Nix, etc.
For simple stuff, like a &lt;a href=&#34;https://andreyor.st/posts/2023-11-19-linux-music-players/&#34;&gt;music player&lt;/a&gt;, Flatpak is more than enough, and for stuff like Emacs, I&amp;rsquo;m running a container with all my development tools.
And here comes GNOME Software.&lt;/p&gt;
&lt;p&gt;For the past decade, ever since GNOME Shell was released in 2011 I have been using it.
I had spent two years using KDE just to see if the grass was &lt;em&gt;kreener&lt;/em&gt; on the other side but returned to GNOME eventually.
And, for the past decade, I&amp;rsquo;ve been using GNOME Software.&lt;/p&gt;
&lt;p&gt;I mean, what, do you expect me to &lt;a href=&#34;https://andreyor.st/posts/2023-10-27-you-dont-need-a-terminal-emulator/&#34;&gt;open a terminal&lt;/a&gt; every time I need to install or update a package?
Sure, back in the day I did that when I used Arch, and Ubuntu before it, but honestly, I don&amp;rsquo;t care if I need to input &lt;code&gt;pm update&lt;/code&gt; in the shell or press an &amp;ldquo;Update&amp;rdquo; button in the GUI.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;terminal is faster and more convenient for managing packages&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yeah, right.
And how am I supposed to go and look for the screenshots of the app I&amp;rsquo;m about to install on the terminal?
You could say it&amp;rsquo;s a silly reason, but during the post on Linux music players, I have done that a lot.
Of course, when I know exactly what app I need, I can go to the shell, and type out the command in a fraction of the time that it takes for GNOME Software to fully load and become responsive.
And that&amp;rsquo;s what brings me to the beginning of the post.&lt;/p&gt;
&lt;p&gt;Dear GNOME Software developers, tell me, do you use your application or do you install everything from the terminal anyway?&lt;/p&gt;
&lt;p&gt;I think that the developer should be, if not the main, but an active user of the project they&amp;rsquo;re developing.
That applies both to open source and what you do at work - because if not, you don&amp;rsquo;t know your own software.&lt;/p&gt;
&lt;p&gt;A theory is nothing without practice, and the same goes for software development.
The code is a theory.
You compile the code and verify that it is correct like any theory should be.
The application of that code is &lt;strong&gt;practice&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And I feel that the GNOME Software developers don&amp;rsquo;t use the program they create.
Here&amp;rsquo;s why.&lt;/p&gt;
&lt;p&gt;As I said before, I use Fedora Silverblue, with everything installed as a Flatpak or inside a container, so the system is pretty much as if I just installed it.
I rarely shut down my laptop, instead, I put it to sleep (not even into hibernation) most of the time.
Now, imagine I want to go and install the Audacious music player.
I open Software and start typing &lt;code&gt;Audacious&lt;/code&gt;, and that&amp;rsquo;s what I see:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-12-06-gnome-software/loading.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;I stare at this for a few seconds, check that my internet connection is working, and realize that it happened again - GNOME Software just doesn&amp;rsquo;t do anything.
I tried waiting for minutes, and the spinner didn&amp;rsquo;t go away.
So I closed the app.
I opened it again.
And I still see the damn thing spinning because you know what?
When you press the close button, the Software doesn&amp;rsquo;t actually close, it always runs in the background, for reasons.&lt;/p&gt;
&lt;p&gt;So I have to go into the System Monitor and &lt;code&gt;kill&lt;/code&gt; the damn thing.
I reopen it again, type &lt;code&gt;Audactions&lt;/code&gt;, after it finishes loading everything, and now I see the player:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-12-06-gnome-software/audacious.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;And this happens to me &lt;strong&gt;every single time&lt;/strong&gt;.
On a system, that is, according to its promise is immutable, and hence this should probably happen even at the stock installation.
And, you know what - it does!
I have recently reinstalled Fedora on one of the laptops I have and left it working for a while, checking periodically if GNOME Software will behave like that.
I didn&amp;rsquo;t have to wait for long, it happened the next day!&lt;/p&gt;
&lt;p&gt;So this bug can be reproduced, with a bit of patience, on a clear installation!
Yet, it haunts me for the last 5-6 years or so!
Aarrgh!
ARE YOU EVEN TESTING YOUR SOFTWARE AT ALL?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;*​breathes​*&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sorry, I&amp;rsquo;ve got too &lt;del&gt;angry&lt;/del&gt; dramatic even for my style of writing.
I&amp;rsquo;m not the only one who experiences this particular bug, among other bugs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A Reddit &lt;a href=&#34;https://old.reddit.com/r/Fedora/comments/tt04l1/hows_software_still_having_issues_like_these/&#34; target=&#34;_blank&#34;&gt;post&lt;/a&gt; describes this exact problem, and many others, specifically mentioning that they&amp;rsquo;re not new&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A &lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-software/-/issues/2007&#34; target=&#34;_blank&#34;&gt;bug report&lt;/a&gt; was submitted to the official issue tracker, outlining the same problem.
I like the comment there:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Without a log I’m afraid we can’t diagnose or fix the problem, so I’ll close this issue report for now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Can&amp;rsquo;t diagnose?
Seriously?
Have you even tried though?&lt;/p&gt;
&lt;p&gt;Seriously, as a software developer, I don&amp;rsquo;t understand this.
I mean, sure, sometimes I don&amp;rsquo;t want to deal with bugs I personally don&amp;rsquo;t have, and it is open source, so I don&amp;rsquo;t really have to do that, someone else can and is welcome to.
But to close the issue because there are no logs and it can&amp;rsquo;t be diagnosed, despite the fact that the issue exists?
Especially, when it hits multiple people.
I don&amp;rsquo;t get it.
Don&amp;rsquo;t try to explain me, I won&amp;rsquo;t get it, because it&amp;rsquo;s hard for me to get it, so I&amp;rsquo;ll close the discussion for now.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Many &lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1748&#34; target=&#34;_blank&#34;&gt;search&lt;/a&gt; &lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1243&#34; target=&#34;_blank&#34;&gt;related&lt;/a&gt; &lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1185&#34; target=&#34;_blank&#34;&gt;requests&lt;/a&gt; are closed, yet the problem is still there.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s like they find a possible reason why this might happen, fix it, and close the issue without testing.
I mean, unless you can reproduce the bug before the fix, how would you know that it is fixed?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don&amp;rsquo;t know how the development process is organized in GNOME, especially for GNOME Software.
However, I think that they don&amp;rsquo;t actually use their own thing, because it has so many obvious bugs that I can hit several during a single session.
For instance, the Reddit post mentions this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While installing, the window &amp;ldquo;reloads&amp;rdquo; a couple of times, as if it was refreshing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yup, happens to me &lt;strong&gt;every time&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes it feels like updates have been fetched, and the ones being displayed are the most recent, but updating only reveals those updates were not the newest ones and even updates need to be downloaded&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Happens to me as well.
To be more specific it happens like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You open the Software (that has been running in the background for long);&lt;/li&gt;
&lt;li&gt;You see that the &amp;ldquo;Updates&amp;rdquo; tab has a notification;&lt;/li&gt;
&lt;li&gt;You open the &amp;ldquo;Updates&amp;rdquo; tab and it shows a spinner;&lt;/li&gt;
&lt;li&gt;Then the updates appear.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I would assume, seeing the spinner, that it had fetched everything, but by pressing the &amp;ldquo;refresh&amp;rdquo; button, the spinner appears and goes for much longer, revealing that there are in fact more updates than there initially were.
Literally happened to me while I was writing the previous line, as I&amp;rsquo;m writing and testing at the same time.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s another &lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1511&#34; target=&#34;_blank&#34;&gt;issue&lt;/a&gt;, that is closed on the tracker, but happens to me all the time, and is extremely easy to reproduce: while you install anything, the search stops working.
The funny thing, it is closed as a duplicate of the issue with the name &amp;ldquo;Rework threading model&amp;rdquo;, which supposedly should fix all of that.
However, how it is a duplicate?
At best it is blocked by that ticket, but it doesn&amp;rsquo;t duplicate it in any way because it describes the actual issue.
It should be closed only when the blocking issue is done, and the bug is tested to be gone.
What a mess.&lt;/p&gt;
&lt;p&gt;So, yeah, GNOME Software is full of bugs, as most of the software is today.
If you&amp;rsquo;ve seen the &amp;ldquo;Preventing the Collapse of Civilization&amp;rdquo; talk by Jonathan Blow, you may remember there was a moment where he listed all of the bugs that happened to him in the past few days.
Have a &lt;a href=&#34;https://youtu.be/ZSRHeXYDLko?t=1337&#34; target=&#34;_blank&#34;&gt;look&lt;/a&gt;, I&amp;rsquo;ve linked it with a timecode.&lt;/p&gt;
&lt;p&gt;So, why am I complaining in the blog and not actually submitting an issue?
Well, I don&amp;rsquo;t want to.
GNOME has a long history of ignoring problems, saying that people use their software in the wrong ways, and so on.
I just don&amp;rsquo;t want to go to their territory and be ignored too.
Instead, I can write a quick rant in my personal space, where I&amp;rsquo;m always right, and no one can prove me wrong, ha ha ha!
Obviously, I&amp;rsquo;m being sarcastic here, if you couldn&amp;rsquo;t tell.&lt;/p&gt;
&lt;p&gt;I guess, someday these bugs will be fixed, it&amp;rsquo;s just a shame that they have existed for so many years, and it seems like the developers don&amp;rsquo;t care.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s enough ranting for this year.
Use what you&amp;rsquo;re making, and test your fixes, or at least ask for feedback once you think you fixed the bug.
See ya!&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: GNOME Software&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Wed, 06 Dec 2023 20:59:00 +0300</pubDate>
    </item><item>
      <title>Linux Music Players</title>
      <link>https://andreyor.st/posts/2023-11-19-linux-music-players/</link>
      <guid>https://andreyor.st/posts/2023-11-19-linux-music-players/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m feeling ranty this week for some reason.
Today will be no different and I&amp;rsquo;ll post another rant on the software world but not about programming.
Instead, I want to tip into the consumer application world, and shit on the current state of music players on GNU/Linux specifically, although the situation is as bad as on other platforms IMO.&lt;/p&gt;
&lt;p&gt;Not long ago I bought a new set of headphones and decided to upgrade my listening experience a bit.
For the last few years, I usually listened to music on my phone via some streaming service - I&amp;rsquo;ve tried plenty and wasn&amp;rsquo;t really satisfied with any of these, although there are some good things each does.
For example, thanks to Spotify and Google Play Music before it, I&amp;rsquo;ve discovered a lot of artists that I wouldn&amp;rsquo;t likely hit if not for their algorithm.
Another thing - listening to music via a subscription service is dirt cheap.&lt;/p&gt;
&lt;p&gt;My listening routine can be described as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick an album;&lt;/li&gt;
&lt;li&gt;Hit play;&lt;/li&gt;
&lt;li&gt;Enjoy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Yes, I&amp;rsquo;m not into playlists, especially not into various methods for organizing music based on mood, algorithmically, by genre, etc.
In fact, I openly despise playlists.
I hate when players shuffle my music (looking at you, Spotify), so all I really need from a music player is to act mainly like a CD player/Vinyl turntable.
I load a specific album, listen to it, and I&amp;rsquo;m done with it.
An album is a piece of art - shuffling songs is like shuffling episodes of a show or chapters in a book.&lt;/p&gt;
&lt;p&gt;What I did like in Spotify, and Google Play Music before it, was discovering new music.
The subscription model means that I can listen to all new releases whenever they come out without having to pay for each of them, purchasing only those I absolutely want to keep, but on some other service that allows me to download the actual files, and listen to them without connecting to internet on any of my devices and in any music player.
For those wondering, you can save your music on Spotify for later listening, but you can&amp;rsquo;t use Spotify without an internet connection even for listening to your downloaded music, because Spotify needs to check if your subscription is still active.
And you can&amp;rsquo;t extract music from Spotify, legally, at least, so if Spotify disappears, all your purchased music goes down with it.
I often find myself in this situation when I&amp;rsquo;m on a train, kilometers away from any cellular network, and Spotify refuses to load my offline music because there&amp;rsquo;s &amp;ldquo;no internet&amp;rdquo;.
So I prefer to keep music I really enjoy out of streaming services.&lt;/p&gt;
&lt;p&gt;Discovering new music on a snap is great, and the subscription model works for that especially well, but one of the problems with most subscription services is audio quality.
When I&amp;rsquo;m listening on my phone I usually don&amp;rsquo;t care - it doesn&amp;rsquo;t feature a great DAC, I&amp;rsquo;m listening through wireless IEMs, although of decent quality, this still limits the quality of sound to what Bluetooth protocol is available for my phone.
And another important thing with music on the phone - I&amp;rsquo;m listening to it on the go, so there&amp;rsquo;s little time to concentrate on details, like when I do at home when I have time to just listen to music without any distractions.
So before you suggest using something like Tidal instead of Spotify just to get the quality audio - I don&amp;rsquo;t really need this kind of audio fidelity on the go, and at home, I have my FLACs.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s go back to the main topic - you got your FLACs, how to listen to them on your PC?
Well, you use an audio player, of course, but which one?&lt;/p&gt;
&lt;p&gt;My operating system of choice is Fedora and there are plenty of audio players for Linux that I can try.
For the past few years, I&amp;rsquo;ve been using MVP as my music player - it can play everything I need, it stays out of the way, and it is fast and resource-friendly.
But it is extremely cumbersome to use.
While not a problem for me, as I rarely listened to music on my PC previously, now that I have a good pair of headphones, I tend to do it more often.
And user experience with MPV started to show.
My main problems with MPV are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s a video player primarily so its UI is oriented towards the video playback.&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t click on the song in the album and play it, the song list only appears when you switch tracks as a visual aid.&lt;/li&gt;
&lt;li&gt;It plays everything in the given directory, even cover images.
&lt;ul&gt;
&lt;li&gt;If you have a &lt;code&gt;.cue&lt;/code&gt; file along with the album file, MPV will first play the &lt;code&gt;.cue&lt;/code&gt; file, then it will play the album file, doubling the total length.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s no notion of a music library besides your filesystem - you just drag and drop files to the player.&lt;/li&gt;
&lt;li&gt;It doesn&amp;rsquo;t remember where I was if you close it.&lt;/li&gt;
&lt;li&gt;Wayland support is not great, for a quite long time the drag-and-drop feature did not work on GNOME at all for the flatpak package.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But these are the problems with stock MPV, there are plenty of other frontends for it, that may work better.
I haven&amp;rsquo;t tried these, and maybe I will in the future.
&lt;a href=&#34;http://ovoplayer.altervista.org/&#34; target=&#34;_blank&#34;&gt;Ovoplayer&lt;/a&gt; looks promising, but sadly doesn&amp;rsquo;t have a package for Fedora, and for some reason doesn&amp;rsquo;t start from an Ubuntu container.&lt;/p&gt;
&lt;p&gt;But this won&amp;rsquo;t be a rant if I&amp;rsquo;m going to list good players, so let&amp;rsquo;s start with &amp;ldquo;first-party&amp;rdquo; players, provided with the desktop environment.
After that, let&amp;rsquo;s look at some &lt;em&gt;modern&lt;/em&gt; players.
Then, I&amp;rsquo;ll list some other commonly suggested players.&lt;/p&gt;
&lt;h3 id=&#34;gnome-music&#34;&gt;&lt;a href=&#34;https://apps.gnome.org/Music/&#34; target=&#34;_blank&#34;&gt;GNOME Music&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As I&amp;rsquo;m using GNOME let&amp;rsquo;s start with the GNOME Music.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/gnome-music.png&#34;
         alt=&#34;Figure 1: GNOME Music screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 1: &lt;/span&gt;GNOME Music screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;First things first, I used this player years ago and I remember it being OK.
However, trying it today I can&amp;rsquo;t say that I like it.
Well, can&amp;rsquo;t say I dislike it either, it just doesn&amp;rsquo;t work for me.&lt;/p&gt;
&lt;p&gt;It has a nice UI, though I&amp;rsquo;d prefer a slimmer one.
Importantly, it has dedicated tabs for albums, artists, individual song lists, and a separate tab for playlists.
Unfortunately, GNOME Music doesn&amp;rsquo;t support &lt;code&gt;.cue&lt;/code&gt; sheets.
I have some releases that are using &lt;code&gt;.cue&lt;/code&gt; sheets and I, personally, don&amp;rsquo;t want to go through the hassle of extracting individual tracks.
As a result, these releases are missing from the Albums tab.
There&amp;rsquo;s an open &lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-music/-/issues/57&#34; target=&#34;_blank&#34;&gt;bug report&lt;/a&gt; asking for the &lt;code&gt;.cue&lt;/code&gt; support that dates back to 2016.&lt;/p&gt;
&lt;p&gt;These albums show up in the &amp;ldquo;Songs&amp;rdquo; section though, so you can listen to them, as a single stream.
That&amp;rsquo;s not bad, considering that I&amp;rsquo;m usually listening to albums from start to finish, but what&amp;rsquo;s the point of having an organized albums section if it doesn&amp;rsquo;t show all of the albums, and you have to remember that you still have them in the tracks section.
That can contain thousands of items because of the size of the music library.&lt;/p&gt;
&lt;p&gt;Oh, and you can&amp;rsquo;t change your music folder, it only works with &lt;code&gt;~/Music&lt;/code&gt; for some reason.
Well, I, personally, don&amp;rsquo;t have a problem with that - that&amp;rsquo;s where I store music on my PC, but it may not work for other people.&lt;/p&gt;
&lt;h3 id=&#34;amberol&#34;&gt;&lt;a href=&#34;https://apps.gnome.org/en/Amberol/&#34; target=&#34;_blank&#34;&gt;Amberol&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GNOME had a neat idea - create a list of applications designed specifically for GNOME with its guidelines in mind.
It is called &lt;a href=&#34;https://apps.gnome.org/&#34; target=&#34;_blank&#34;&gt;GNOME Circle&lt;/a&gt;, and it is listed among GNOME&amp;rsquo;s core apps.
The Amberol, at the moment of writing, is one of the first apps on the list, and it is a music player.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/amberol-full.png&#34;
         alt=&#34;Figure 2: Amberol screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 2: &lt;/span&gt;Amberol screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Similarly to GNOME Music, Amberol doesn&amp;rsquo;t support &lt;code&gt;.cue&lt;/code&gt; sheets.
Again, there&amp;rsquo;s a &lt;a href=&#34;https://gitlab.gnome.org/World/amberol/-/issues/177&#34; target=&#34;_blank&#34;&gt;bug report&lt;/a&gt; for that, but the reason it doesn&amp;rsquo;t support &lt;code&gt;.cue&lt;/code&gt; is due to the method they access files.
Sandboxing, which is used by flatpaks causes problems, as the portal interface isn&amp;rsquo;t designed for this particular use-case.&lt;/p&gt;
&lt;p&gt;Unlike GNOME Music, however, Amberol doesn&amp;rsquo;t show these albums in the playlist at all.
Don&amp;rsquo;t ask me why, I don&amp;rsquo;t know.
And unlike GNOME Music, there is no dedicated tab for albums, artists, or anything really - only a single playlist, consisting of all tracks in your music library.
I know that Amberol tries to be a simple music player, but I wouldn&amp;rsquo;t say that GNOME Music was complicated.
Going simpler than GNOME Music is hurting usability.&lt;/p&gt;
&lt;h3 id=&#34;lollypop&#34;&gt;&lt;a href=&#34;https://wiki.gnome.org/Apps/Lollypop&#34; target=&#34;_blank&#34;&gt;Lollypop&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another music player that is made using GNOME guidelines.
It has more features than Amberol, and maybe even more features than GNOME Music:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/lollypop.png&#34;
         alt=&#34;Figure 3: Lollypop screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 3: &lt;/span&gt;Lollypop screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;But once again - missing albums due to missing &lt;code&gt;.cue&lt;/code&gt; support.
Are you for real?
Another &lt;a href=&#34;https://gitlab.gnome.org/World/lollypop/-/issues/257&#34; target=&#34;_blank&#34;&gt;8-year-old bug report&lt;/a&gt; asking for &lt;code&gt;.cue&lt;/code&gt; support with no resolution.
Well, at least there is some discussion going on, so the ticket isn&amp;rsquo;t entirely stale.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s even weirder, it seems that Lollypop doesn&amp;rsquo;t have a tracklist, unlike the previous two players.
It&amp;rsquo;s weird because while I don&amp;rsquo;t use this feature, it&amp;rsquo;s so fundamental to all music players we&amp;rsquo;re going to discuss further, not having it is just strange.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m starting to think that the GNOME project simply can&amp;rsquo;t have a decent music player.
Let&amp;rsquo;s look at how KDE does this.&lt;/p&gt;
&lt;h3 id=&#34;elisa&#34;&gt;&lt;a href=&#34;https://apps.kde.org/elisa/&#34; target=&#34;_blank&#34;&gt;Elisa&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A few years ago I was using KDE for a brief period, but I can&amp;rsquo;t remember if KDE did have their dedicated music player back then.
Or maybe I wasn&amp;rsquo;t bothered to check, as I already used MPV.
As far as I can see today the KDE Project has the Elisa music player:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/elisa.png&#34;
         alt=&#34;Figure 4: Elisa screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 4: &lt;/span&gt;Elisa screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Well, I tried it, and&amp;hellip; missing albums, &lt;strong&gt;again&lt;/strong&gt;!
And again due to the lack of &lt;code&gt;.cue&lt;/code&gt; support!
&lt;a href=&#34;https://bugs.kde.org/show_bug.cgi?id=398694&#34; target=&#34;_blank&#34;&gt;There&amp;rsquo;s a bug report for that again&lt;/a&gt;, and there are even some duplicates, probably because their bug tracker web interface is abysmal.&lt;/p&gt;
&lt;p&gt;Seriously, are &lt;code&gt;.cue&lt;/code&gt; sheets that rare?
Maybe they are hard to implement properly?
Up to this moment, only MPV could deal with them with no problem, and it&amp;rsquo;s a shame.&lt;/p&gt;
&lt;p&gt;Well, at least these &lt;code&gt;.cue&lt;/code&gt; albums show up in the Tracks section, as a single track.
Hitting play on one of them, however, plays a random album instead.
Welp.
It&amp;rsquo;s a shame though, the UI is clean, everything is mostly logically displayed, and there&amp;rsquo;s little wasted space, unlike in many GNOME apps.&lt;/p&gt;
&lt;h3 id=&#34;amarok&#34;&gt;&lt;a href=&#34;https://apps.kde.org/amarok/&#34; target=&#34;_blank&#34;&gt;Amarok&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another project is often seen on KDE systems.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/amarok.png&#34;
         alt=&#34;Figure 5: Amarok screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 5: &lt;/span&gt;Amarok screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;No Flathub entry, that&amp;rsquo;s a good start.
You see, I mostly install software through flatpaks because I use Fedora Silverblue - an immutable OS.
This can cause problems, and I know that, but most of the time everything works fine.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/clementine.png&#34;
         alt=&#34;Figure 6: Don&amp;amp;rsquo;t worry, Clementine, I&amp;amp;rsquo;ll get to you soon&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 6: &lt;/span&gt;Don&amp;rsquo;t worry, Clementine, I&amp;rsquo;ll get to you soon&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Once again, missing &lt;code&gt;.cue&lt;/code&gt; support.
Which is weird, as there are reports on the internet that Amarok does support &lt;code&gt;.cue&lt;/code&gt; since version 1.2.4.
Maybe it was lost along the way, I don&amp;rsquo;t know.
Well, at least it shows the albums in the list of all media, albeit as a single file.
Thank you for not omitting these, at least.
And it actually plays them, unlike Elisa.&lt;/p&gt;
&lt;p&gt;The user interface, on the other hand, is weird.
I couldn&amp;rsquo;t figure out if I&amp;rsquo;d pressed the pause button, as it doesn&amp;rsquo;t change shape, and the visualizer doesn&amp;rsquo;t stop immediately either.
This is because there&amp;rsquo;s by default a two-second fade-out that has to play out before the track is put on pause, and the button doesn&amp;rsquo;t change until the track is fully stopped.
I dislike fadeouts, crossfades, and other things like that, sorry.&lt;/p&gt;
&lt;p&gt;Well, it&amp;rsquo;s an old project, and its UI is a product of its time, I guess.&lt;/p&gt;
&lt;h3 id=&#34;rhythmbox&#34;&gt;&lt;a href=&#34;https://wiki.gnome.org/Apps/Rhythmbox&#34; target=&#34;_blank&#34;&gt;Rhythmbox&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/rhythmbox.png&#34;
         alt=&#34;Figure 7: Rhythmbox screenshot from Flathub&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 7: &lt;/span&gt;Rhythmbox screenshot from Flathub&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Another fairly old music player.
Yet, the user interface is clean, uncluttered, and concise.
Nothing but praise from me on the UI.&lt;/p&gt;
&lt;p&gt;Again, &lt;strong&gt;no &lt;code&gt;.cue&lt;/code&gt; support&lt;/strong&gt;!
&lt;strong&gt;WHY?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well, at least, there&amp;rsquo;s a plugin.
The flatpak version couldn&amp;rsquo;t see external plugins, so I had to install it as an rpm package inside a container (Silberblue works like that).
And while it works now, for some reason, the &lt;code&gt;.cue&lt;/code&gt; albums aren&amp;rsquo;t shown in the albums section.
Instead, they&amp;rsquo;re shown under the Unknown section, and clicking on such an album opens a separate window with track listing obtained from the &lt;code&gt;.cue&lt;/code&gt; file.
So the experience isn&amp;rsquo;t great, unfortunately.
But we&amp;rsquo;re close.&lt;/p&gt;
&lt;h3 id=&#34;strawberry&#34;&gt;&lt;del&gt;Clementine&lt;/del&gt; &lt;a href=&#34;https://www.strawberrymusicplayer.org/&#34; target=&#34;_blank&#34;&gt;Strawberry&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I told you that we&amp;rsquo;re going to talk about Clementine.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/strawberry.png&#34;
         alt=&#34;Figure 8: Strawberry screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 8: &lt;/span&gt;Strawberry screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Strawberry is a fork of Clementine, which is a fork of Amarok.
Same as Amarok, the default UI is horrible, sorry.
But, &lt;strong&gt;finally&lt;/strong&gt;, a &lt;code&gt;.cue&lt;/code&gt; support!
Which makes it even stranger, as Amarok itself did not work with &lt;code&gt;.cue&lt;/code&gt; files.
Maybe I&amp;rsquo;m missing something?&lt;/p&gt;
&lt;p&gt;Importing my music library I can now see all of the albums, and play individual tracks, but it&amp;rsquo;s so &lt;em&gt;clumsy&lt;/em&gt;.
When I open the album, and double click on the track, this only track is appended to the playlist.
And it isn&amp;rsquo;t the one played either.&lt;/p&gt;
&lt;p&gt;I do hate playlists, but what I hate even more is play queues, and this action acts like adding to a queue.
I don&amp;rsquo;t want to deal with a queue at all - give me an album list, play the one I&amp;rsquo;m interested in, or start from a track I&amp;rsquo;m interested in going forward.
&lt;strong&gt;Simple as that.&lt;/strong&gt;
In Strawberry, I have to clear a playlist, replace the playlist with an album playlist, or create a new playlist, and it&amp;rsquo;s just tiresome when all I want is to listen to a specific album.&lt;/p&gt;
&lt;p&gt;Why is it so hard for most players today?
Don&amp;rsquo;t tell me I&amp;rsquo;m using players wrong.
I mean, I &lt;em&gt;can&lt;/em&gt; get this from Strawberry, just going through several hoops.
Well, we were going into older players, let&amp;rsquo;s look at something more recent then.
Maybe some modern players took inspiration from Spotify and others and made something similar.&lt;/p&gt;
&lt;h3 id=&#34;tambourine&#34;&gt;&lt;a href=&#34;https://github.com/MMarco94/tambourine-music-player/&#34; target=&#34;_blank&#34;&gt;Tambourine&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Well, I&amp;rsquo;ve kinda covered modern players already at the start of this post, but these were desktop environment-specific players.
Mostly.
This one is just an independent project.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/tambourine.png&#34;
         alt=&#34;Figure 9: Tambourine screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 9: &lt;/span&gt;Tambourine screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The interface is laggy and we&amp;rsquo;re back to no &lt;code&gt;.cue&lt;/code&gt; support.
It has potential, though.&lt;/p&gt;
&lt;p&gt;Also, good luck searching for a &amp;ldquo;Tambourine music player&amp;rdquo;.&lt;/p&gt;
&lt;h3 id=&#34;nuclear&#34;&gt;&lt;a href=&#34;https://nuclear.js.org/&#34; target=&#34;_blank&#34;&gt;Nuclear&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve seen this player being praised online several times but never took a look.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/nuclear.png&#34;
         alt=&#34;Figure 10: Nuclear screenshot from the project repository&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 10: &lt;/span&gt;Nuclear screenshot from the project repository&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The startup time of several seconds and crashed when I tried to import my music library.
It&amp;rsquo;s funny, though, this is what they say on their web page:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Say goodbye to proprietary music players filled with ads, tracking, and profiling.
Nuclear empowers you to listen to what you want, where you want, and how you want, for free&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They say it like there are no other non-proprietary music players without all of that.
Well, maybe, if we&amp;rsquo;re talking about service-based players, like Spotify, but I&amp;rsquo;m here for a local-first player.
Their user interface is filled with some online garbage, trending artists, and such, not sure what service it comes from.
The local library is waaay below in the list, and, as mentioned, it doesn&amp;rsquo;t work.
I&amp;rsquo;ve limited it to a portion of the library, and there is no &lt;code&gt;.cue&lt;/code&gt; support, once again.&lt;/p&gt;
&lt;p&gt;But again, it&amp;rsquo;s funny, when I pressed play on the album, the player immediately added something that wasn&amp;rsquo;t in my library to the queue right after it.
Kinda disagrees with the statement they make on the web page.
That&amp;rsquo;s not &lt;em&gt;how I want&lt;/em&gt; to listen to music.&lt;/p&gt;
&lt;h3 id=&#34;tauon-music-box&#34;&gt;&lt;a href=&#34;https://tauonmusicbox.rocks/&#34; target=&#34;_blank&#34;&gt;Tauon Music Box&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Is the closest to no-bullshit among all of the ones I&amp;rsquo;ve tried.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/tauon.png&#34;
         alt=&#34;Figure 11: Tauon Music Box screenshot from Flathub&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 11: &lt;/span&gt;Tauon Music Box screenshot from Flathub&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It does support &lt;code&gt;.cue&lt;/code&gt; sheets (wow), and managed to load my music library without many issues.
One of the problems occurred with a three-disk album that doesn&amp;rsquo;t have disk metadata, for some reason.
Every track appeared as a separate album in the list.
But, to be honest, this three-disk album is hard for all other players I&amp;rsquo;ve tried (that support &lt;code&gt;.cue&lt;/code&gt;), not sure why.&lt;/p&gt;
&lt;p&gt;I like this player, but its UI is a bit too flat and too simplistic for my taste.
Like, all of the buttons are just differently arranged rectangles.
I can get used to this, but a bit more clarity wouldn&amp;rsquo;t hurt.
Another small nitpick is that the album grid scrolls in fixed steps, and not fluidly - it just throws me off a bit.&lt;/p&gt;
&lt;p&gt;Other than that - good player, can recommend.&lt;/p&gt;
&lt;h3 id=&#34;qmmp&#34;&gt;&lt;a href=&#34;http://qmmp.ylsoftware.com/&#34; target=&#34;_blank&#34;&gt;Qmmp&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/qmmp.png&#34;
         alt=&#34;Figure 12: Qmmp screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 12: &lt;/span&gt;Qmmp screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Well, most modern players didn&amp;rsquo;t make the cut, let&amp;rsquo;s go back and look at older ones.&lt;/p&gt;
&lt;p&gt;If you want an old Winamp-like player, qmmp is probably what you&amp;rsquo;re looking for.
It supports Winamp skins and acts pretty much the same way.&lt;/p&gt;
&lt;p&gt;It plays everything, has the &lt;code&gt;.cue&lt;/code&gt; support, and the only issue for me is that I can&amp;rsquo;t drag the window with the mouse.
Probably some Wayland/GTK incompatibilities - it&amp;rsquo;s a Qt-based player after all.
I have tried it in the past as well, though I vaguely remember having issues with it here and there.
So I moved to Deadbeef.&lt;/p&gt;
&lt;h3 id=&#34;deadbeef&#34;&gt;&lt;a href=&#34;https://deadbeef.sourceforge.io/&#34; target=&#34;_blank&#34;&gt;Deadbeef&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve previously used Deadbeef, and I think it&amp;rsquo;s one of the best players available on Linux.
I have never used Foobar2000 but as far as I know, Deadbeef is similar to it.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/deadbeef.png&#34;
         alt=&#34;Figure 13: Deadbeef screenshot from the project web page (it doesn&amp;amp;rsquo;t look like this OOTB)&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 13: &lt;/span&gt;Deadbeef screenshot from the project web page (it doesn&amp;rsquo;t look like this OOTB)&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For some reason, it isn&amp;rsquo;t available in the form of a flatpak, so I had to install it inside of a container, similar to Rhythmbox.&lt;/p&gt;
&lt;p&gt;Again, it plays anything and has the &lt;code&gt;.cue&lt;/code&gt; support.
There are many plugins, and this player can be heavily configured.
UI is a bit ugly, but who cares?
And you can stylize it with plugins, replacing any element you don&amp;rsquo;t like.
On my older setup, I had the seek bar replaced with a waveform, kinda like on SoundCloud.
I lost my config years ago and don&amp;rsquo;t want to reconfigure it again, honestly, even if it looks like there are not many changes.&lt;/p&gt;
&lt;p&gt;Still, it has many small problems.
You can&amp;rsquo;t enable album repeat without going into the menu, the play and pause buttons are separate, and you do have to press play to play and pause to pause.
In most players play acts as a pause when the playback is active, and vice versa.&lt;/p&gt;
&lt;p&gt;I like how you can make a simple playlist &lt;em&gt;look&lt;/em&gt; like these are separate albums.
These lines between each album are just separators in a continuous stream of songs, yet, enabling album cover art makes it look even more like separate entries.
It&amp;rsquo;s just an illusion, though, a clever one.&lt;/p&gt;
&lt;h3 id=&#34;audacious&#34;&gt;&lt;a href=&#34;https://audacious-media-player.org/&#34; target=&#34;_blank&#34;&gt;Audacious&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-19-linux-music-players/audacious.png&#34;
         alt=&#34;Figure 14: Audacious screenshot from the project web page&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 14: &lt;/span&gt;Audacious screenshot from the project web page&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Audacious is another good music player.
After a few tweaks in the settings, it looks quite similar to Deadbeef.
Unlike Deadbeef, I couldn&amp;rsquo;t find any way to separate albums in the playlist.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t like playlists, but both Deadbeef and Audacious have an OK playlist experience.
Disabling the column headers helps Audacious a lot, as you basically prevent any shuffling of your playlist by doing so.
So if you&amp;rsquo;ve dragged a folder with songs into the player, and they&amp;rsquo;re in the correct order, you just hit play and you&amp;rsquo;re good.
In some albums, I had tracks imported in the wrong order, and while it is a bummer - it is an easy fix.&lt;/p&gt;
&lt;p&gt;And unlike most other players, Audacious managed to import this three-disk &lt;code&gt;.cue&lt;/code&gt; album and have all disks organized nicely.
That is until I accidentally hit &amp;ldquo;sort by the album&amp;rdquo;, and it grouped all three disks into one album again (after that I disabled the column headers).
What is also weird, is that it doesn&amp;rsquo;t seem to support the disk metadata.&lt;/p&gt;
&lt;h2 id=&#34;skill-issue&#34;&gt;Skill issue?&lt;/h2&gt;
&lt;p&gt;Yeah, yeah, I know, that&amp;rsquo;s a fair point.
But honestly, I don&amp;rsquo;t believe that using a music player should be hard or unintuitive.
The default players in GNOME and KDE do an OK job, to be honest, it&amp;rsquo;s just the fact that they can&amp;rsquo;t play everything I have.&lt;/p&gt;
&lt;p&gt;Remember how it was in the old days?
You pick your CD, tape cassette, or vinyl record, put it into a respective device and you&amp;rsquo;re good.
Yes, you had to swap disks if you wanted to listen to something else, and you couldn&amp;rsquo;t change the order of tracks.
On the other hand, you&amp;rsquo;ve listened to music how the artist intended.&lt;/p&gt;
&lt;p&gt;PC audio players felt liberating back then because you could reorder things, which usually was impossible on the physical medium.
And they had many more features, unavailable on most playback devices back then.
But today, I personally want to go back to a simpler time, but without having to deal with actual CDs.
CDs are good as backups, as I still have some music disks from the 2000s that still work, and I don&amp;rsquo;t have any hard drive from the 2000s that works.
Vinyl is also good, possibly even better to actually preserve data, but it is mastered differently, and I don&amp;rsquo;t have a turntable.&lt;/p&gt;
&lt;p&gt;There are probably many more players I haven&amp;rsquo;t tried, and it&amp;rsquo;s almost impossible to do because there are too many of them.
Again, Tauon Music Box was good, so it&amp;rsquo;s possible to create a modern player, nobody just really wants to.
It does support everything I need, but I didn&amp;rsquo;t really like the UI, to be honest, and it has problems with multi-disk albums.&lt;/p&gt;
&lt;p&gt;The struggle doesn&amp;rsquo;t stop there though.
On Windows, it is a bit better - it has Winamp, AIMP, and Foobar2000.
Mobile devices are, however, in a similar trouble as the Linux desktop - good luck finding a decent no-bullshit local-first music player for Android.
I know, it has AIMP, VLC, and MPV but out of these three only AIMP is a &lt;em&gt;music&lt;/em&gt; player.
There are paid music players too, but I don&amp;rsquo;t know if any of them actually good.
And I don&amp;rsquo;t have an Apple device, so I don&amp;rsquo;t know if it has good non-stock music players.&lt;/p&gt;
&lt;p&gt;So I landed up on Strawberry for now - it works better than everything else, but I&amp;rsquo;d like something a bit simpler.
Seriously, though - bring &lt;code&gt;.cue&lt;/code&gt; support to GNOME Music and it got pretty much everything I need.&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Linux Music Players&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Sun, 19 Nov 2023 22:57:00 +0300</pubDate>
    </item><item>
      <title>Overcolorization</title>
      <link>https://andreyor.st/posts/2023-11-17-overcolorization/</link>
      <guid>https://andreyor.st/posts/2023-11-17-overcolorization/</guid>
      <description>&lt;p&gt;Tree-sitter became more widespread and Emacs took notice and included a bunch of &lt;code&gt;&amp;lt;lang&amp;gt;-ts-mode&lt;/code&gt; as alternatives to &lt;code&gt;&amp;lt;lang&amp;gt;-mode&lt;/code&gt; into the core.
This is good news and a welcome change, but I have some concerns about the approach.&lt;/p&gt;
&lt;p&gt;When I first saw the Tree-sitter talk by Max Brunsfeld I was concerned that the language &lt;a href=&#34;https://youtu.be/Jes3bD6P0To?t=390&#34; target=&#34;_blank&#34;&gt;highlighting &amp;ldquo;fix&amp;rdquo; they&amp;rsquo;re talking about&lt;/a&gt; is too much.
Here are the screenshots from the talk before and after Tree-sitter:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/before.png&#34;
         alt=&#34;Figure 1: In some languages, variables have different colors depending on context, types have different colors, struct fields don&amp;amp;rsquo;t have their own color, etc.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 1: &lt;/span&gt;In some languages, variables have different colors depending on context, types have different colors, struct fields don&amp;rsquo;t have their own color, etc.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/after.png&#34;
         alt=&#34;Figure 2: Everything is consistent now.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 2: &lt;/span&gt;Everything is consistent now.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here&amp;rsquo;s an important quote from that talk:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It might seem like a subtle improvement to you but I really think it actually achieves the goal of making it so that you can kind of get the structure of the code just from glancing at the colors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Structure, from &lt;em&gt;colors&lt;/em&gt;.
I don&amp;rsquo;t know, I have never tried to infer &lt;strong&gt;structure&lt;/strong&gt; from &lt;strong&gt;colors&lt;/strong&gt; in the code.
I do write mostly in lisps today, and to infer structure there I, personally, use semantic indentation, not parentheses, despite a popular belief.
There are people who use colored parentheses to make it easier to see where the expression starts and ends, but in my practice, there&amp;rsquo;s no need for that, and colors can be used for other things.&lt;/p&gt;
&lt;p&gt;Before lisps, I was writing mostly in &lt;a href=&#34;https://en.wikipedia.org/wiki/Generational_list_of_programming_languages#ALGOL_based&#34; target=&#34;_blank&#34;&gt;ALGOL-based&lt;/a&gt; languages, and again, there aren&amp;rsquo;t many places where I could infer &lt;em&gt;structure&lt;/em&gt; from colors.
I don&amp;rsquo;t think I can do that with the provided screenshot of the code in the talk either.
I can see the semantics better, yes, since types and field accesses are uniformly highlighted, but this tells me nothing about code structure - the indentation kinda does.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;m being picky here.
The word structure is not the word I have problems with in particular.
And I can get what they mean - you know that the type color is blue, so when you switch languages, and the language defines types at different positions, you can spot them easily, despite being in a different place.
It can be seen when they show C and Go examples on the slides with Tree-sitter highlighting.
While all of that is cool, I think that coloring everything is the wrong approach.&lt;/p&gt;
&lt;p&gt;Before Emacs, I used NeoVim and Kakoune, and I was too obsessed with colors in my editor.
NeoVim by default highlights many things, and I&amp;rsquo;ve even created &lt;a href=&#34;https://gitlab.com/andreyorst/dotfiles/commit/124dfbc1e255ba646a18b901bf000437539f0e55&#34; target=&#34;_blank&#34;&gt;additional rules&lt;/a&gt; to highlight struct fields in C code.
Kakoune also &lt;a href=&#34;https://gitlab.com/andreyorst/dotfiles/commit/820ccc844efd874f911983a919495c7e8fe63fc3&#34; target=&#34;_blank&#34;&gt;allowed&lt;/a&gt; me to define my own extra rules and I used it plenty of times.&lt;/p&gt;
&lt;p&gt;But when I moved to Emacs, I noticed that, unlike other editors, syntax highlighting is much more &lt;em&gt;reserved&lt;/em&gt;.
For example, Emacs usually highlights function definitions, but not function calls.
At first, I was upset, and searched for &lt;a href=&#34;https://gitlab.com/andreyorst/dotfiles/commit/51435e0f4362e947cc996ddf7447fc17a30abd00&#34; target=&#34;_blank&#34;&gt;ways&lt;/a&gt; to enable more syntax highlighting, but quickly learned that the underlying system is slow, and the more you highlight the choppier Emacs gets.&lt;/p&gt;
&lt;p&gt;But after years with Emacs, I began to like the more reserved highlighting.
By highlighting &lt;strong&gt;important&lt;/strong&gt; parts, Emacs helps me focus on important parts of the code.
A function call is not that important, you call them all the time - the definition, however, is.
The macro call, on the other hand, is important, as it transforms the code inside its body, so it is better to know that something that looks like an ordinary call isn&amp;rsquo;t actually an ordinary call.
Rust solved this by forcing the use of &lt;code&gt;!&lt;/code&gt; in macro names, but Emacs had a different solution to the same problem in lisps for a long time by using syntax highlighting.&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/elisp-highlighting-specials.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;The following screenshot demonstrates how Emacs highlights the macro calls.
The calls to &lt;code&gt;defun&lt;/code&gt;, &lt;code&gt;defmacro&lt;/code&gt;, and &lt;code&gt;when&lt;/code&gt; are calls to macros.
They aren&amp;rsquo;t special language constructs, just macros, but they do affect the language, as they transform the code given to them into something else and that&amp;rsquo;s why it&amp;rsquo;s important to see that you&amp;rsquo;re calling a macro.
Calls to the ordinary functions like &lt;code&gt;apply&lt;/code&gt;, or &lt;code&gt;log-level-satisfies&lt;/code&gt; aren&amp;rsquo;t highlighted because there is not much significance with them - they return some values and that&amp;rsquo;s it.&lt;/p&gt;
&lt;p&gt;The macros above are both standard to and known by Emacs but users can define their own macros.
And we need to somehow highlight these macros as well as already known ones.
Tree-sitter parsers can do that in a very limited way - if the macro comes from the same file it can be detected easily.
If not - you have to analyze all of the files imported by the current file.
Which becomes harder, when files are in external dependencies, possibly in compressed archives, and so on.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s where the dynamic highlighting comes naturally.
Emacs controls everything related to Emacs Lisp so it can do that without much effort, but that can&amp;rsquo;t be said about every other language.&lt;/p&gt;
&lt;p&gt;This is how Emacs highlights the Clojure code for me:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/clojure-var-off.png&#34;
         alt=&#34;Figure 3: No dynamic highlighting&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 3: &lt;/span&gt;No dynamic highlighting&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Emacs can&amp;rsquo;t know anything about Clojure, and thus we can&amp;rsquo;t apply the same logic when it comes to highlighting user-defined macros and other runtime stuff.
But, the CIDER package provides a facility to highlight things dynamically, based on the state of the program.
In this particular case it helps to see that some names came from the global scope:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/clojure-var-on.png&#34;
         alt=&#34;Figure 4: Dynamic highlighting shows a global var&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 4: &lt;/span&gt;Dynamic highlighting shows a global var&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Similarly, CIDER can highlight user-defined macros:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/cider-macro-off.png&#34;
         alt=&#34;Figure 5: Dynamic highlighting is enabled, but the macro wasn&amp;amp;rsquo;t made known to the REPL process&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 5: &lt;/span&gt;Dynamic highlighting is enabled, but the macro wasn&amp;rsquo;t made known to the REPL process&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Same as in Emacs Lisp, Clojure macros are small compilers, so it&amp;rsquo;s better to indicate that certain calls are special:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/cider-macro-on.png&#34;
         alt=&#34;Figure 6: After sending the macro to the REPL, dynamic highlighting shows the user-defined macro&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 6: &lt;/span&gt;After sending the macro to the REPL, dynamic highlighting shows the user-defined macro&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Another use for dynamic highlighting can be found in languages with dynamic scoping, which today are mostly only lisps.
It is important to know if the variable you&amp;rsquo;re using is a dynamically scoped one.
Especially so, when &lt;code&gt;let&lt;/code&gt; can both introduce locals and re-bind dynamically scoped variables.
For example, again, in Emacs Lisp:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/elisp-dynamic-var-off.png&#34;
         alt=&#34;Figure 7: No dynamic highlighting&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 7: &lt;/span&gt;No dynamic highlighting&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The issue here is subtle, but if &lt;code&gt;foo&lt;/code&gt; uses &lt;code&gt;dynamic-var&lt;/code&gt; internally, it will see the value changed to &lt;code&gt;20&lt;/code&gt;.
Dynamic highlighting helps to indicate that &lt;code&gt;dynamic-var&lt;/code&gt; is special and important:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/elisp-dynamic-var-on.png&#34;
         alt=&#34;Figure 8: Dynamic highlighting shows a global var&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 8: &lt;/span&gt;Dynamic highlighting shows a global var&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;m using descriptive names, like &lt;code&gt;local-var&lt;/code&gt; and &lt;code&gt;dynamic-var&lt;/code&gt; but in actual code, it&amp;rsquo;s not as obvious, when something is dynamic.
A few months ago, I encountered a weird bug in one of the projects I maintain, and it was related to dynamic var being used as a local one:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/bug.png&#34;
         alt=&#34;Figure 9: compile-command is a dynamic var&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 9: &lt;/span&gt;&lt;code&gt;compile-command&lt;/code&gt; is a dynamic var&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here, the variable named &lt;code&gt;compile-command&lt;/code&gt; is just a string, used to store a command to compile the source code.
The name is innocent and semantically appropriate.
However, it is a dynamic var introduced in the &lt;code&gt;compile.el&lt;/code&gt; package, and changing &lt;code&gt;compile-command&lt;/code&gt; can cause problems for any functions called from the body of the &lt;code&gt;let&lt;/code&gt; block.
In my case, some other package adviced one of the calls inside this &lt;code&gt;let&lt;/code&gt; block, overriding the &lt;code&gt;compile-command&lt;/code&gt; for them, breaking the package I maintain.&lt;/p&gt;
&lt;p&gt;Sadly, by default, Emacs doesn&amp;rsquo;t highlight such variables.
And, as I said, Tree-sitter can&amp;rsquo;t really help with any of the above cases without parsing &lt;em&gt;all&lt;/em&gt; of the code.
But it often can be achieved with an external package that consults with the runtime, like CIDER for Clojure and &lt;code&gt;highlight-defined.el&lt;/code&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; for Emacs Lisp.
Luckily the fix was easy, but if Emacs highlighted dynamic vars by default, this would not be an issue at all.&lt;/p&gt;
&lt;p&gt;So my point is - highlight the &lt;strong&gt;important stuff&lt;/strong&gt;, not &lt;em&gt;just stuff&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Of course, for lisps all these nuances existed even before dynamic highlighting, so lisp hackers had to deal with them somehow.
Thus, dynamically scoped vars use different naming conventions (e.g. &lt;code&gt;*dynamic-var*&lt;/code&gt;), macros use different code indentation rules, and so on.
These rules are still widely used today but highlighting is an additional visual aid, making important stuff easier to notice.&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s go back to the main topic.
I&amp;rsquo;ve called this post &amp;ldquo;Overcolorization&amp;rdquo; for a reason, after all.
I wanted to show you the importance of selective highlighting first, and I hope it will make more sense on why I think there is a problem.&lt;/p&gt;
&lt;p&gt;One of the reasons, Emacs highlighting is reserved only for important parts of the code, is because it is slow.
Tree-sitter makes everything much faster, and thus Emacs should be able to highlight everything now without slowdown.
That fact worried me, as it would mean that code could suddenly become a mess of colors when the &lt;code&gt;&amp;lt;lang&amp;gt;-ts-mode&lt;/code&gt; becomes the standard.
I held hopes that due to Emacs being used by generally more conservative people, the old approach to syntax highlighting would remain, what would change is that actually finding the important parts of the code will be easier, because the parser gives us all of the info.&lt;/p&gt;
&lt;p&gt;Recently, I was toying with the Elixir language, and decided to use the now inbuilt &lt;code&gt;elixir-ts-mode&lt;/code&gt;, because the non-tree-sitter mode has issues with automatic indentation, and jumping across the delimiters.
Tree-sitter should naturally fix this, and it indeed does, but &lt;strong&gt;what am I looking at&lt;/strong&gt;?&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-17-overcolorization/elixir.png&#34;
         alt=&#34;Figure 10: elixir-mode on the left, elixir-ts-mode on the right&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;span class=&#34;figure-number&#34;&gt;Figure 10: &lt;/span&gt;&lt;code&gt;elixir-mode&lt;/code&gt; on the left, &lt;code&gt;elixir-ts-mode&lt;/code&gt; on the right&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Everything is purple.
I&amp;rsquo;ve included the old mode on the left, so you can see what it looked like before I switched to the new one, which is actually &lt;a href=&#34;https://github.com/elixir-editors/emacs-elixir/commit/19ed1ffaccbc14f4b1777a24f0951918bab537b4#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R7-R10&#34; target=&#34;_blank&#34;&gt;suggested by the developers of the original &lt;code&gt;elixir-mode&lt;/code&gt;&lt;/a&gt;.
I mean, it&amp;rsquo;s pretty, and the colors do look nice in my personal opinion, but remember the quote I mentioned?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It actually achieves the goal of making it so that you can kind of get the structure of the code just from glancing at the colors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The colors here clearly do not help the matter here.
You could object by saying, that the colors should be used consistently, and I agree with you, here the main issue is that the &lt;code&gt;font-lock-keyword-face&lt;/code&gt; is used for both keywords and method calls, as well as for parentheses.
The &lt;code&gt;font-lock-function-name-face&lt;/code&gt; is used both for function definitions, parameter names, and calls.
But not for all calls, some use &lt;code&gt;font-lock-keyword-face&lt;/code&gt; and I&amp;rsquo;m not talking about method calls: see &lt;code&gt;raise&lt;/code&gt;.
The &lt;code&gt;font-lock-type-face&lt;/code&gt; is used both for types and &lt;code&gt;:symbols&lt;/code&gt;.
The operators aren&amp;rsquo;t highlighted only because the &lt;code&gt;font-lock-operator-face&lt;/code&gt; is not set in my theme.&lt;/p&gt;
&lt;p&gt;The highlighting is all over the place.
Thanks to the speed and power of the tree sitter the code was made to look pretty, not informative.
The same happened for a few other languages, but Elixir is probably the most offending in my opinion.&lt;/p&gt;
&lt;p&gt;And I can&amp;rsquo;t fix that!
Say, I want to remove highlighting of the method calls - but I can&amp;rsquo;t because for that I need to unset the face used by them, removing highlighting of the language keywords too.
Same with the argument colors - they use function name colors, and I do want to see function definitions highlighted.
I could override the &lt;code&gt;treesit-font-lock-rules&lt;/code&gt; used by Elixir, only the &lt;code&gt;elixir-ts--font-lock-settings&lt;/code&gt; is a private var, and it can silently be removed, renamed, and changed, breaking everything I&amp;rsquo;ve tried to fix.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t want this post to be a bashing of &lt;code&gt;elixir-ts-mode&lt;/code&gt; developers, I&amp;rsquo;m glad that the new mode was made, as it is better in many ways, but the problem with syntax highlighting exists, and other languages are in trouble too.
In Lua, the &lt;code&gt;lua-ts-mode&lt;/code&gt; does highlight table access but doesn&amp;rsquo;t highlight method calls which differs from table access by one symbol.
Are table accesses more important than method calls?
I think they both are equally unimportant.&lt;/p&gt;
&lt;p&gt;The real problem here is that Emacs isn&amp;rsquo;t ready for semantic highlighting.
It does now have Tree-sitter, which is a common interface between most text editors, so the problem isn&amp;rsquo;t with Emacs speed anymore.
What it doesn&amp;rsquo;t have is enough standardized faces to use across all languages.&lt;/p&gt;
&lt;p&gt;Or does it?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;font-lock-comment-face&lt;/code&gt; - face used to highlight comments.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-misc-punctuation-face&lt;/code&gt; - face used to highlight miscellaneous punctuation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-delimiter-face&lt;/code&gt; - face used to highlight delimiters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-bracket-face&lt;/code&gt; - face used to highlight brackets, braces, and parens.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-punctuation-face&lt;/code&gt; - face used to highlight punctuation characters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-property-use-face&lt;/code&gt; - face used to highlight property references.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-property-name-face&lt;/code&gt; - face used to highlight properties of an object.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-operator-face&lt;/code&gt; - face used to highlight operators.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-number-face&lt;/code&gt; - face used to highlight numbers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-escape-face&lt;/code&gt; - face used to highlight escape sequences in strings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-regexp-grouping-construct&lt;/code&gt; - face used to highlight grouping constructs in Lisp regexps.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-regexp-grouping-backslash&lt;/code&gt; - face for backslashes in Lisp regexp grouping constructs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-regexp-face&lt;/code&gt; - face used to highlight regexp literals.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-preprocessor-face&lt;/code&gt; - face used to highlight preprocessor directives.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-negation-char-face&lt;/code&gt; - face used to highlight easy to overlook negation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-warning-face&lt;/code&gt; - face used to highlight warnings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-constant-face&lt;/code&gt; - face used to highlight constants and labels.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-type-face&lt;/code&gt; - face used to highlight type and class names.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-variable-use-face&lt;/code&gt; - face used to highlight variable references.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-variable-name-face&lt;/code&gt; - face used to highlight variable names.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-function-call-face&lt;/code&gt; - face used to highlight function calls.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-function-name-face&lt;/code&gt; - face used to highlight function names.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-builtin-face&lt;/code&gt; - face used to highlight builtins.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-keyword-face&lt;/code&gt; - face used to highlight keywords.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-doc-markup-face&lt;/code&gt; - face used to highlight embedded documentation mark-up.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-doc-face&lt;/code&gt; - face used to highlight documentation embedded in program code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-string-face&lt;/code&gt; - face used to highlight strings.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-lock-comment-delimiter-face&lt;/code&gt; - face used to highlight comment delimiters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are all of the faces defined in the &lt;code&gt;font-lock.el&lt;/code&gt; package.
There are separate faces for function definitions and function calls - good stuff.
I don&amp;rsquo;t see the face that can be used for method calls specifically, but are there any major differences from ordinary function calls?
It even has a face for variable usage, not just definition.&lt;/p&gt;
&lt;p&gt;Thing is, many of these faces are new, and did not exist prior to Emacs 29.1.
It&amp;rsquo;s good that they were added &lt;a href=&#34;https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=a795c51f6053272de61bc1721305e3c15ff424ee&#34; target=&#34;_blank&#34;&gt;specifically to be used with Tree-sitter&lt;/a&gt;-based modes.
You can say that I should submit a bug to Emacs about the &lt;code&gt;elixir-ts-mode&lt;/code&gt; not using faces properly, and I did, but the problem isn&amp;rsquo;t specific to &lt;code&gt;elixir-ts-mode&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Elixir mode is just a convenient way to illustrate the problem
The problem when lifting technical limitations can cause problems.
A famous quote says &amp;ldquo;Limitations breed creativity&amp;rdquo;, and I feel like that&amp;rsquo;s exactly what was going on with the old method of syntax highlighting in Emacs.
It was slow, and couldn&amp;rsquo;t handle too much highlighting or too complex rules, so it was kept to the minimum, highlighting what was absolutely necessary.
But with Tree-sitter parsers being able to process files with 20k lines under 30ms the performance is out of the question.
Well, mostly, you still need to draw everything on the screen, processing abstract syntax trees generated by these parsers.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I want to end this post with a message to fellow major mode maintainers.&lt;/p&gt;
&lt;p&gt;Please, use syntax &lt;strong&gt;highlighting&lt;/strong&gt; to &lt;strong&gt;highlight&lt;/strong&gt;, not to &lt;em&gt;color&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Syntax highlighting is a tool, and highlighting important parts of the code makes programming less convoluted.
Pretty colors are nice to have, but if the point is only to make the code look fancy, there&amp;rsquo;s no real value for what we&amp;rsquo;re trying to do in the end - write better code.
Let&amp;rsquo;s help each other at least at that.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;BTW, &lt;code&gt;highlight-defined.el&lt;/code&gt; by default tries to highlight &lt;em&gt;everything&lt;/em&gt; defined, so I limited it down to only highlighting known dynamic vars.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Overcolorization&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Fri, 17 Nov 2023 22:45:00 +0300</pubDate>
    </item><item>
      <title>Why no one uses @media (prefers-color-scheme: dark)?</title>
      <link>https://andreyor.st/posts/2023-11-15-why-no-one-uses-media-prefers-color-scheme-dark/</link>
      <guid>https://andreyor.st/posts/2023-11-15-why-no-one-uses-media-prefers-color-scheme-dark/</guid>
      <description>&lt;p&gt;I don&amp;rsquo;t get it - it&amp;rsquo;s the simplest way to enable dark mode for your website that works across all platforms.
It works on Linux, Android, MacOS, iOS, and maybe even on Windows (I didn&amp;rsquo;t test it, I have no Windows).
I see websites that either use JS to do that or do no dark mode at all.&lt;/p&gt;
&lt;p&gt;Some time ago I started using the &lt;a href=&#34;https://github.com/openstyles/stylus&#34; target=&#34;_blank&#34;&gt;Stylus extension&lt;/a&gt; for Firefox to override CSS for websites that don&amp;rsquo;t do the dark mode.
And you know what?
For a quick fix for most pages, it is as simple as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;font-weight:bold&#34;&gt;media&lt;/span&gt; (prefers-color-scheme: dark) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    html { &lt;span style=&#34;font-weight:bold&#34;&gt;filter&lt;/span&gt;: &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;invert&lt;/span&gt;(100&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;) &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;hue-rotate&lt;/span&gt;(180&lt;span style=&#34;font-weight:bold&#34;&gt;deg&lt;/span&gt;); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    img { &lt;span style=&#34;font-weight:bold&#34;&gt;filter&lt;/span&gt;: &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;invert&lt;/span&gt;(100&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;) &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;hue-rotate&lt;/span&gt;(180&lt;span style=&#34;font-weight:bold&#34;&gt;deg&lt;/span&gt;); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, this introduces problems on some pages, for example, embedded videos, especially YouTube ones are hard to invert back.
Images may look horrific if they&amp;rsquo;re done with something else than the &lt;code&gt;img&lt;/code&gt; tag, gradients and colors are preserved but shadows are still inverted, and so on.
But if you have a web page on the web it is you who can do it right, so I don&amp;rsquo;t have to fix it for myself.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-scss&#34; data-lang=&#34;scss&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@media&lt;/span&gt; (prefers-color-scheme: dark) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  body, textarea { color: #dedddc; background-color: #1e1e1e }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  code { background-color: #2b2b2b; color: #dedddc; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  pre code { background-color: #ddd; color: #333; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  kbd { background-color: #161b22; color: #dedddc; border: solid 1&lt;span style=&#34;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rgb&lt;/span&gt;(57, 63, 71); border-bottom-color: &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rgb&lt;/span&gt;(37, 43, 51); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  tbody tr:nth-child(2n+1) td, tbody tr:nth-child(2n+1) th { background-color: #212121; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  table { border: 1&lt;span style=&#34;font-weight:bold&#34;&gt;px&lt;/span&gt; solid #333; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  td, th { border: 1&lt;span style=&#34;font-weight:bold&#34;&gt;px&lt;/span&gt; solid #333; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.post-list&lt;/span&gt; { h1 a { color: #dedddc; &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;:hover { color: #7a5fa4; } } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  a { color: #7a5fa4; &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;:hover { color: #7a5fa4; } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  header h2 { a { color: #86868e &lt;span style=&#34;font-weight:bold&#34;&gt;!important&lt;/span&gt;; &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;:hover { color: #7a5fa4 &lt;span style=&#34;font-weight:bold&#34;&gt;!important&lt;/span&gt;; } &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;:active { color: #7a5fa4 &lt;span style=&#34;font-weight:bold&#34;&gt;!important&lt;/span&gt;; } } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  blockquote { border-left: .25&lt;span style=&#34;font-weight:bold&#34;&gt;em&lt;/span&gt; solid #424242; color: #bebebe; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.org-svg&lt;/span&gt; { filter: &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;invert&lt;/span&gt;(90&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.invertable&lt;/span&gt; { filter: &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;invert&lt;/span&gt;(100&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hue-rotate&lt;/span&gt;(180&lt;span style=&#34;font-weight:bold&#34;&gt;deg&lt;/span&gt;) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;contrast&lt;/span&gt;(79&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.highlight&lt;/span&gt; { filter: &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;invert&lt;/span&gt;(100&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;hue-rotate&lt;/span&gt;(180&lt;span style=&#34;font-weight:bold&#34;&gt;deg&lt;/span&gt;) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;contrast&lt;/span&gt;(110&lt;span style=&#34;font-weight:bold&#34;&gt;%&lt;/span&gt;); scrollbar-color: #9c9c9c #ddd; background-color: #ddd; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.title-group&lt;/span&gt; { &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.title&lt;/span&gt; { a { color: #dedddc &lt;span style=&#34;font-weight:bold&#34;&gt;!important&lt;/span&gt;; &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;:hover { color: #7a5fa4 &lt;span style=&#34;font-weight:bold&#34;&gt;!important&lt;/span&gt;; } &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;:active { color: #7a5fa4 &lt;span style=&#34;font-weight:bold&#34;&gt;!important&lt;/span&gt;; } } } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  input, input[type=file], button { background-color: #161b22; color: #dedddc; border: solid 1&lt;span style=&#34;font-weight:bold&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rgb&lt;/span&gt;(57, 63, 71); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.diff-added&lt;/span&gt; { background-color: #e2c3da; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;.diff-removed&lt;/span&gt; { background-color: #9fe0d6; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is all it takes to make dark mode for this page and the rest of the site.
Yes, I use some &lt;code&gt;filter&lt;/code&gt; attributes for specially constructed images and code, and yes, some browsers may not support it, but it generally works well.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I&amp;rsquo;m bringing this up not because I&amp;rsquo;m some freak who wants dark themes everywhere - no.
My PC switches to the dark theme in the evening automatically to be easier on the eyes with the overall brightness, and when I open some web page it often blinds me with a white background.
Not only it is unpleasant, but it is also bad for your health, as at the end of the day your body is more relaxed, and a sudden flash of light forces your eye muscles to suddenly contract.
It&amp;rsquo;s advised not to look at the screen for several hours before bedtime, but I have a lot of work to do after work, so I can&amp;rsquo;t really afford that.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t browse the web &lt;em&gt;that much&lt;/em&gt;, but have a look at the pages I did adjust because I refer to them fairly often:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2023-11-15-why-no-one-uses-media-prefers-color-scheme-dark/fixes.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Most of these are static pages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is it that hard to add a single &lt;code&gt;@media (prefers-color-scheme: dark) {...}&lt;/code&gt; block to the style file?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well, I&amp;rsquo;m not a web developer.
I don&amp;rsquo;t know your ways and such.
You can say that your pages are complicated, your stylesheets are complex and generated with some preprocessor, or you&amp;rsquo;re using some framework that doesn&amp;rsquo;t allow you to do that or whatnot.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll happily take even the stupid invert filter - as long as it is easy on the eyes I&amp;rsquo;m good.
And yes, there are ways of forcing dark mode on everything, so I guess you can pretend there is no issue here, but it is not true.&lt;/p&gt;
&lt;p&gt;Dark mode makes it easier to read stuff in the dark without causing too much eye fatigue - that&amp;rsquo;s the whole point.
Tone your colors down a bit, bring the contrast down so the light text isn&amp;rsquo;t cutting the eyes against a pure black background and you&amp;rsquo;re good.
And please, don&amp;rsquo;t use JS for that, or at least &lt;a href=&#34;https://stackoverflow.com/a/59621903&#34; target=&#34;_blank&#34;&gt;consult&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; with the &lt;code&gt;prefers-color-scheme&lt;/code&gt; query, so that your JS code synchronizes with my system-wide preferences.&lt;/p&gt;
&lt;p&gt;Please add dark mode to your static pages.
It&amp;rsquo;s not that hard.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;The stackoverflow was painfully bright at the time of writing this post&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Why no one uses @media (prefers-color-scheme: dark)?&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Wed, 15 Nov 2023 22:50:00 +0300</pubDate>
    </item><item>
      <title>Game2 W4/4</title>
      <link>https://andreyor.st/posts/2023-08-25-game2-w44/</link>
      <guid>https://andreyor.st/posts/2023-08-25-game2-w44/</guid>
      <description>&lt;p&gt;Well, It&amp;rsquo;s unfortunate, but I couldn&amp;rsquo;t make the game in these 4 weeks.
August is just too much of a pain in terms of the amount of different events - maybe even the busiest month in the whole year, for me personally.
Judging by the commit history, I was able to work only for 11 days out of the 28 days given to me by the challenge - and I tried to do at least some work every day and commit everything I did.
I&amp;rsquo;m saying 11 days, but maybe 5 of these were in this last week.&lt;/p&gt;
&lt;p&gt;This last week was all about level generation and, unfortunately, I failed at it.
I was trying to implement the &lt;a href=&#34;https://andreyor.st/posts/2022-05-10-wave-function-collapse-algorithm-in-clojurescript/&#34;&gt;Wave Function Collapse&lt;/a&gt; algorithm in Lua and for some weird reason, it didn&amp;rsquo;t work.
After three failed attempts, I decided to cheat.
My idea was to go and port my ClojureScript version to Fennel form by form and compile it to Lua after it works.
However, it didn&amp;rsquo;t work as well, no idea why.
For some reason, some branches in my code are just never visited at all, although I did everything the same way, and implemented missing pieces from Clojure&amp;rsquo;s standard library for this code to work.
At least it terminated, but instead generated a complete gibberish:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt; (wfc (gen_world 9 9) coast)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[[&amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟫&amp;#34;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is supposed to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user&amp;gt; (wfc (gen-world 9 9) coast)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[[&amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟫&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟩&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34; &amp;#34;🟦&amp;#34;]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This reminded me of the importance of data structures and their properties I keep forgetting when programming in Clojure.
Because in Clojure it&amp;rsquo;s just the thing you have, so you don&amp;rsquo;t think much about it.&lt;/p&gt;
&lt;p&gt;You see, Clojure has 4 main data structures, baked up with literals: list &lt;code&gt;(a, b)&lt;/code&gt;, map &lt;code&gt;{k1 a, k2 b}&lt;/code&gt;, vector &lt;code&gt;[a, b]&lt;/code&gt;, and set &lt;code&gt;#{a, b}&lt;/code&gt;.
Each of these has its own set of properties, for example, lists are only fast to add elements at the front, and vectors are fast to add elements to the tail.
Maps are mostly like Lua&amp;rsquo;s tables, and sets are like maps in the way that they can only store unique elements, and are indexed like a map but iterated like a list.&lt;/p&gt;
&lt;p&gt;In my ClojureScript implementation of the WFC algorithm, I used all these data structures and relied on their behavior.
For example, the superposition for a cell after the world&amp;rsquo;s initialization is a set of all possible values for the cell:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I used maps to store the recipe:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt; {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:up&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:down&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:left&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:right&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt; {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:up&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:down&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:left&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:right&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt; {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:up&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:down&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:left&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:right&lt;/span&gt; #{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;}}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The map itself stores maps with sets that are later used in the algorithm.
Other data structures are at play for other parts of the whole process, but I&amp;rsquo;m not going to touch them here.&lt;/p&gt;
&lt;p&gt;Lua on the other hand has only one collection type - the table.
It can serve as a vector, to which you can add elements at the tail, and you can use them as hash tables, where there can only be a single instance of a given key.
Sets can also be represented if we just use a table where keys are elements and values are the same elements or something like &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-lua&#34; data-lang=&#34;lua&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local&lt;/span&gt; superposition = {[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is actually what the &amp;ldquo;Programming in Lua&amp;rdquo; book &lt;a href=&#34;http://www.lua.org/pil/11.5.html&#34; target=&#34;_blank&#34;&gt;suggests&lt;/a&gt;.
The recipe then looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-lua&#34; data-lang=&#34;lua&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- Sorry, not sorry for formatting - all these brackets on separate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- lines take a stupidly big amount of vertical space&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]={up={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         down={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         left={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         right={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]={up={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         down={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         left={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         right={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;}},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]={up={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         down={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         left={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         right={[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;}}}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- also, the table syntax is just horrible - why square brackets are&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- needed around strings for keys? Why can&amp;#39;t we write {&amp;#34;foo bar&amp;#34;=&amp;#34;baz&amp;#34;}?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- And why can we write {foo=&amp;#34;bar&amp;#34;}? Why variable foo is not the same&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- foo when used in the table? Why foo is now a string?  The syntax is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;-- too smart for its own good.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, here&amp;rsquo;s the problem - how do we tell apart sets from key-value tables?
We can write some predicates, like &lt;code&gt;is_set&lt;/code&gt; that either check that all values are &lt;code&gt;true&lt;/code&gt;, or create a constructor function that will add a hidden &lt;code&gt;__index&lt;/code&gt; metatable with a &lt;code&gt;is_set&lt;/code&gt; key set to true, but it&amp;rsquo;s all pretty janky, in my opinion at least.
A proper data structure should have its own syntax literal and a set of functions to check the type.
Lua doesn&amp;rsquo;t even give you the ability to distinguish sequential tables from associative ones&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.
I don&amp;rsquo;t think that Clojure&amp;rsquo;s choice of &lt;code&gt;#{}&lt;/code&gt; for sets is great, but it&amp;rsquo;s a lot better than nothing.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s also another issue with our implementation of sets - iteration support.
In Clojure, you can iterate over any type of data structure, and it will actually do it in a meaningful way.&lt;/p&gt;
&lt;p&gt;If you try to iterate over a list, vector, or set - it will just iterate over the elements:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;map identity &lt;/span&gt;&amp;#39;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:b&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:b&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;map identity &lt;/span&gt;[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:b&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:b&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;map identity &lt;/span&gt;#{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:b&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:b&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the case of a set the order is not determined, so the order of results is arbitrary.
If you try to iterate over a map though, it&amp;rsquo;s a bit different:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;user&amp;gt;&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;map identity &lt;/span&gt;{&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;d&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:e&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;f&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;([&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:c&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;d&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:a&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;b&amp;#34;&lt;/span&gt;] [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;:e&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;f&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Same as for the set, the order is arbitrary, but you can see that the elements in the list are vectors.
That&amp;rsquo;s right, when we iterate over the map we get back vectors, containing the key and its value, which we can then use in the function that we iterate with.&lt;/p&gt;
&lt;p&gt;You may be thinking: &amp;ldquo;Well, that&amp;rsquo;s the same as what &lt;code&gt;pairs&lt;/code&gt; does in Lua&amp;rdquo; but it&amp;rsquo;s not.
Unlike what I have shown to you, &lt;code&gt;pairs&lt;/code&gt; will &lt;strong&gt;always&lt;/strong&gt; return the key and value, even for vectors.
Yes, in the case of a vector, the key will be an index, and we can pretty much ignore it most of the time, but what about our sets?
In the case of our set implementation, the key is actually the value we&amp;rsquo;re looking for, because the value is just &lt;code&gt;true&lt;/code&gt;.
So no, implementing sets as tables that store &lt;code&gt;true&lt;/code&gt; as a value is a no-go for most use cases, we have to store the value both as a key and a value in a set:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-lua&#34; data-lang=&#34;lua&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;local&lt;/span&gt; superposition = {[&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟦&amp;#34;&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟩&amp;#34;&lt;/span&gt;, [&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;]=&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;🟫&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I digress.&lt;/p&gt;
&lt;p&gt;Another, very important property of Clojure&amp;rsquo;s data structures is that they&amp;rsquo;re both immutable and persistent.
This means that whatever we do with the vector doesn&amp;rsquo;t actually change the vector - it returns a new vector that shares the unchanged part of the structure with the old one plus new stuff.
Clojure author went to great lengths to make this process fast and reliable.&lt;/p&gt;
&lt;p&gt;I used this heavily in the CLJS version of the algorithm.
Perhaps too heavily, but it&amp;rsquo;s a different topic.
What it gave me though is the ability to change the world without mutating the original world, so every step of the algorithm actually produced a new world, and I could trace it back to the very beginning if I wanted to.
It is not as fast as the mutation-based approach but is much more sane to work with and debug.&lt;/p&gt;
&lt;p&gt;My first implementation of the algorithm in Lua, however, was mutating the world.
Not only it is hard to debug, Lua makes it harder because you can&amp;rsquo;t view the data structure you&amp;rsquo;re working with.
You probably don&amp;rsquo;t remember it, but I made a &lt;a href=&#34;https://andreyor.st/posts/2021-01-09-pretty-printing-for-fennel-language/&#34;&gt;complete rewrite&lt;/a&gt; of the pretty printer for the Fennel language, and I did it mostly because the original one produced the representation that was hard to read due to indentation going all over the place.
Well, guess what, Lua doesn&amp;rsquo;t have its own pretty printer whatsoever.
You have to write your own or import some library that does it for you, which is not feasible with TIC-80, as its screen resolution is too small to comfortably read anything, especially huge tables that contain lots of elements, like the world I was trying to generate.
Can you feel my frustration?&lt;/p&gt;
&lt;p&gt;Then I made a hacky &lt;code&gt;deepcopy&lt;/code&gt; function (another part that lacks from Lua because &lt;em&gt;tables&lt;/em&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;) to at least get a copy-on-write type of immutability in Lua.
This slowed down things to the point that it was useless for the sizes of worlds I wanted to construct.
And it didn&amp;rsquo;t fix the problem, so it was somewhere else.
I wasn&amp;rsquo;t ready to re-implement the whole thing again, so I stopped here.&lt;/p&gt;
&lt;p&gt;Another thing I keep forgetting is how great Clojure&amp;rsquo;s standard library for manipulating collections is.
There are all sorts of operations on sets, you can transform maps, iterate on anything, rename keys, etc., etc.
Lua has a very rudimentary library for tables, that can&amp;rsquo;t do almost anything.
Sure, there are libraries like &lt;a href=&#34;https://github.com/lunarmodules/Penlight&#34; target=&#34;_blank&#34;&gt;Penlight&lt;/a&gt;&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;, and &lt;a href=&#34;https://github.com/lua-stdlib/lua-stdlib&#34; target=&#34;_blank&#34;&gt;lua-stdlib&lt;/a&gt; that could help with that, and I made a &lt;a href=&#34;https://gitlab.com/andreyorst/fennel-cljlib&#34; target=&#34;_blank&#34;&gt;port&lt;/a&gt; of Clojure&amp;rsquo;s core namespace to Fennel (and therefore Lua) but again - TIC-80 makes it hard to use these libraries.
So, no, Lua isn&amp;rsquo;t great at manipulating data structures, and porting Clojure code to it is a pain.
What did I expect?
Well, I&amp;rsquo;ve ported all the functions I used in my CLJS WFC implementation, things like &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;keep&lt;/code&gt;, &lt;code&gt;map-indexed&lt;/code&gt;, &lt;code&gt;get-in&lt;/code&gt;, &lt;code&gt;assoc-in&lt;/code&gt;, etc., but there were still problems with how to iterate over tables, as there is no sequence abstraction in Lua, and it&amp;rsquo;s hard to make it properly.
I tried multiple times before, and the best I got was my &lt;a href=&#34;https://gitlab.com/andreyorst/lazy-seq&#34; target=&#34;_blank&#34;&gt;lazy sequence&lt;/a&gt; library for Fennel, but even there I took some shortcuts regarding the situation with tables that have both associative and array parts.
Sorry Lua fans, but combined data structures like that are stupid.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m writing this a bit early, as there are clearly some more days until the end of this week, but then again, I won&amp;rsquo;t have time to do any work neither this evening, nor tomorrow, or the day after that.
It&amp;rsquo;s a shame though, I don&amp;rsquo;t want to abandon this roguelike idea, and maybe I&amp;rsquo;ll get back to it after the challenge ends.
Or maybe not, I don&amp;rsquo;t know.
I will have another chance to make a top-down game, but probably without an isometric projection.&lt;/p&gt;
&lt;p&gt;Looking back on the development process for this game I can see that choosing the isometric projection was probably the wrong decision.
First, I struggled with the projection itself, then with the projection of screen coordinates back to the world coordinates.
Halfway through I ditched the isometric projection and started drawing stuff without it, in hopes that I will just enable it back when the world generation is ready.
But then the world generation was a problem.
I didn&amp;rsquo;t even get to work on enemies, items, menus, and so on.
Well, a roguelike is a lot more involved than a simple platformer, so it is expected that it takes more time to do stuff, but I didn&amp;rsquo;t have that time unfortunately.&lt;/p&gt;
&lt;p&gt;Lua isn&amp;rsquo;t the one to blame for all these problems though.
While I did get some frustrations with its syntax, overall the experience wasn&amp;rsquo;t that bad.
However, I will not probably use it for upcoming projects.
The &lt;code&gt;lua-mode&lt;/code&gt; package for Emacs is in a rough shape - there is no pairing support for &lt;code&gt;do end&lt;/code&gt; keywords, like in &lt;code&gt;sh-mode&lt;/code&gt; or &lt;code&gt;ruby-mode&lt;/code&gt;.
Indentation is extremely slow and often unpredictable.&lt;/p&gt;
&lt;p&gt;The next game on the list is a Card game but I&amp;rsquo;m not in the mood for that at all, so I&amp;rsquo;m going to change this and make a side-scrolling shoot-em-up kind of game.
It should be much easier, and with all of the frustration built up over the last month, this is probably just what I need.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I know that the distinction isn&amp;rsquo;t possible because the data structure combines both sequential and associative parts in one package, but at least Lua could give us some predicates that would check if the associative part has any values and if the sequential part is not empty.
This way we would be able to detect if the table we&amp;rsquo;re looking at only has the array part, or only has the associative part which is much better than nothing.
It can be done in Lua but it is slow and often unreliable.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;It is hard to properly implement deep copying because of metatables.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;I keep forgetting its name and every single time I have to search for it as &amp;ldquo;lua batteries included&amp;rdquo; to only find a reddit post with a link to the repo that just lists lots of libraries for Lua.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Game2 W4/4&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Fri, 25 Aug 2023 18:35:00 +0300</pubDate>
    </item><item>
      <title>Spam Rant</title>
      <link>https://andreyor.st/posts/2022-07-26-spam-rant/</link>
      <guid>https://andreyor.st/posts/2022-07-26-spam-rant/</guid>
      <description>&lt;p&gt;Who doesn&amp;rsquo;t dislike spam?
Well, apparently &lt;a href=&#34;https://www.ted.com/talks/james_veitch_this_is_what_happens_when_you_reply_to_spam_email&#34; target=&#34;_blank&#34;&gt;this guy loves it&lt;/a&gt;, but in a kind of special way.
I definitively don&amp;rsquo;t like spam, even a &lt;em&gt;clever one&lt;/em&gt;, but unfortunately, we all have to deal with it to some extent.
This is a rant on spam because I was just fed up with it once again.&lt;/p&gt;
&lt;p&gt;There are several categories of spam in my life:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;E-Mail spam.&lt;/li&gt;
&lt;li&gt;Spam calls.&lt;/li&gt;
&lt;li&gt;SMS spam.&lt;/li&gt;
&lt;li&gt;Promoted spam.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think you all are familiar with the first three categories, and as much as I dislike these, there&amp;rsquo;s not much to say about them.&lt;/p&gt;
&lt;p&gt;At least, the first two are kind of easy to deal with.
I don&amp;rsquo;t read 99% of e-mails that end up in my spam folder, conveniently managed for me by GMail.
While I don&amp;rsquo;t really like GMail, and rather move to a self-hosted mail, Google does a pretty decent job at tracking spam, even with all smart features being &amp;ldquo;disabled&amp;rdquo;.
I never answer calls that aren&amp;rsquo;t on my contact list, unless I&amp;rsquo;m waiting for some kind of delivery.
This is an easy fix because you can make your phone only make a sound when someone from your contact list is calling you, or even writing a message.&lt;/p&gt;
&lt;p&gt;But the last two categories are hard to deal with.&lt;/p&gt;
&lt;h2 id=&#34;sms-spam&#34;&gt;SMS spam&lt;/h2&gt;
&lt;p&gt;SMS is &lt;a href=&#34;https://en.wikipedia.org/wiki/SMS#SMS_today&#34; target=&#34;_blank&#34;&gt;still widely used&lt;/a&gt; for delivering various kinds of information to people.
And for some reason, it is considered an &lt;em&gt;official&lt;/em&gt; communication channel by a lot of people.
For example, when you need to secure an operation in your bank, it will likely send you an SMS with a confirmation code.
At least this was the case until some years ago when most banks I use switched to using their respecting application on Android/iOS, which is used to show the code now.&lt;/p&gt;
&lt;p&gt;But still, some companies may send you such codes from time to time, and that&amp;rsquo;s basically the only useful case for SMS today.
Well, emergency notifications are also distributed via SMS in my country, which may give SMS a kind of official status, but these are also broadcasted via other channels.
However, mainly due to the confirmation codes it&amp;rsquo;s really hard to ignore SMS, and most of the SMS messaging apps (on Android, at least) use a modern conversation view, as opposed to how older phones worked with SMS as per separate messages.
And this leads to a problem.&lt;/p&gt;
&lt;p&gt;The first one is that you can&amp;rsquo;t block SMS from non-contacts, because these security codes are likely to be sent from some corporate number.
While you could add such numbers to your contact lists, and be done with it, the problem arises when you want an SMS from a new service, so you basically have to disable filtering.
Which defeats the purpose.&lt;/p&gt;
&lt;p&gt;And the second problem is worse because the spam comes from the same phone numbers that are used to send you security codes.
My bank, for example, not only shows me ads when I use their official app but also occasionally sends me ads via SMS from the same number they use to confirm operations.
And they use the same number for spam calls too!
You basically have to accept this.
I call it &lt;em&gt;promoted spam&lt;/em&gt; because it uses the same communication channel it uses for important stuff, just so you couldn&amp;rsquo;t block it easily.&lt;/p&gt;
&lt;p&gt;Other companies, like clothing companies, for instance, have these club cards, that you register in order to have a discount from time to time or some other benefits.
Guess what, in order to register such a card you have to supply both your phone number and your e-mail.
And while e-mail is easy enough to ignore, they just keep spamming you with new ads even though the last time you&amp;rsquo;ve bought anything from them was 3 years ago.
For that matter, you can&amp;rsquo;t give them a fake phone number because of the confirmation code they sent you before applying for a discount.&lt;/p&gt;
&lt;p&gt;The most fun part is that my phone operator has a paid feature that blocks such SMS spam!
And they remind me of it every single time I get a spam message that they have an anti-spam feature I can subscribe to.
It&amp;rsquo;s like, they &lt;strong&gt;know&lt;/strong&gt; it&amp;rsquo;s a &lt;strong&gt;spam&lt;/strong&gt; SMS, they &lt;strong&gt;let it through&lt;/strong&gt;, and immediately after it reaches you, they &lt;strong&gt;send another spam SMS&lt;/strong&gt; that &lt;strong&gt;advertises&lt;/strong&gt; you an &lt;strong&gt;anti-spam feature&lt;/strong&gt;!
I&amp;rsquo;d rather change my operator than subscribe to something like that.&lt;/p&gt;
&lt;h2 id=&#34;just-unsubscribe&#34;&gt;Just unsubscribe&lt;/h2&gt;
&lt;p&gt;This is what I often hear when I brag about spam or read about it online.&lt;/p&gt;
&lt;p&gt;Well, first of all, not all spam is something I&amp;rsquo;ve willingly subscribed to.
Some of it is automated when you register a club card or some other account of sorts, and usually, there&amp;rsquo;s no way to disable these messages upon registration.&lt;/p&gt;
&lt;p&gt;Some services don&amp;rsquo;t even have a way to cancel subscriptions!
I once participated in a micro-controller-related conference, and in order to sign up, I used my personal e-mail, instead of the work one.
It was a giant mistake, motivated by the fact, that due to security reasons it was impossible to use work e-mail outside the office, and I thought that I may need my e-mail for confirmation or some paperwork when I&amp;rsquo;ll arrive.
Not only I didn&amp;rsquo;t need it, but I still receive yearly emails about upcoming conferences, highlights, and other stuff.
And there&amp;rsquo;s no way to unsubscribe.
I don&amp;rsquo;t have an account, their e-mail is random every time, they don&amp;rsquo;t respect GMail&amp;rsquo;s &amp;ldquo;unsubscribe me&amp;rdquo; feature, and there&amp;rsquo;s no e-mail to write back to a real human to take me off the mailing list.
I&amp;rsquo;ve made a custom rule that deletes messages from them automatically, but it still manages to come through somehow sometimes.&lt;/p&gt;
&lt;p&gt;And I&amp;rsquo;m not even talking about malicious spam, which is a different kind of beast at all.
You can&amp;rsquo;t unsubscribe from it, obviously.
Otherwise, it wouldn&amp;rsquo;t be as effective as it is.&lt;/p&gt;
&lt;p&gt;But as I&amp;rsquo;ve said - e-mail is easy enough to automate and ignore, but SMS isn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Much like the situation with the conference, you can&amp;rsquo;t unsubscribe from most promoted SMS spam.
Companies just don&amp;rsquo;t even admit they&amp;rsquo;re sending anything to you like these messages don&amp;rsquo;t exist.
I&amp;rsquo;ve tried coming to a customer&amp;rsquo;s office and speaking with a manager about the issue, but they basically told me to give up, and accept the situation as is.
I&amp;rsquo;ve changed operator since then, but the story repeated.&lt;/p&gt;
&lt;p&gt;So, no.
You can&amp;rsquo;t unsubscribe from the majority of annoying spam.
It&amp;rsquo;s not a solution.
Forget it.&lt;/p&gt;
&lt;h2 id=&#34;just-ignore-it&#34;&gt;Just ignore it&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve used e-mail for a long time, and in the early days, I spent a lot of time marking stuff as spam, because filters weren&amp;rsquo;t that good enough yet, and spammers were creative.
But not all people spent time, analyzing what is a spam message and what isn&amp;rsquo;t.
And, more importantly, spammers are creative, and when we&amp;rsquo;re talking about malicious spam, which wants not only to advertise something you don&amp;rsquo;t want but also to steal your money or personal information, scammers today are as creative as they have ever been.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve linked a TED talk at the beginning of this rant, and there&amp;rsquo;s one example of spam, that I&amp;rsquo;m not getting too often, but still see in the spam folder from time to time.
But I know some people, who were victims of such emails, sending money to scammers.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s the problem.
Spam works, and spammers get what they want more often than you think.&lt;/p&gt;
&lt;p&gt;There are a lot of similarities between spam messages/calls and TV advertisements.
They steal your time and focus and promote you something you likely don&amp;rsquo;t even need.
And unfortunately, nothing can be done about it really.&lt;/p&gt;
&lt;p&gt;You can be extremely careful, and not put your e-mail and phone number anywhere, and while this cuts a lot, there are still systems that probe wide ranges of phone numbers automatically.
I&amp;rsquo;ve actually tried it several times, and still somehow managed to get spam, even though nobody knows about this e-mail address, and phone number.&lt;/p&gt;
&lt;p&gt;With SMS there&amp;rsquo;s also a point of trust because so much secure information comes through this communication channel, I&amp;rsquo;m a bit afraid to use anti-spam systems.
As they will likely send my SMS to their server, and we all know that user personal data leaks every week these days.&lt;/p&gt;
&lt;p&gt;So, yeah, unfortunately, spam is something I just had to accept, and I&amp;rsquo;m not sure if the situation will ever get better.&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Spam Rant&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Tue, 26 Jul 2022 20:21:00 +0300</pubDate>
    </item><item>
      <title>Why is Paredit is so un-Emacsy?</title>
      <link>https://andreyor.st/posts/2021-09-30-why-is-paredit-is-so-un-emacsy/</link>
      <guid>https://andreyor.st/posts/2021-09-30-why-is-paredit-is-so-un-emacsy/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a quick rant, kinda.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve become a huge fan of structural editing recently.
My structural editing journey was maybe a bit unusual, but I&amp;rsquo;ve finally settled on Smartparens for quite some time.
Originally I was introduced to structural editing by &lt;a href=&#34;https://github.com/DogLooksGood/parinfer-mode&#34; target=&#34;_blank&#34;&gt;Parinfer mode&lt;/a&gt; which had some Paredit functionality, so it got me interested.
Then I have been using &lt;a href=&#34;https://github.com/eraserhd/parinfer-rust&#34; target=&#34;_blank&#34;&gt;Parinfer-rust&lt;/a&gt; for some time, but when I got the job in Clojure, I realized that it has a major problem of generating too much noise in the Git in commits because it needs to reformat the whole buffer to work, which is way too aggressive, especially when working with multiple people.
I&amp;rsquo;ve already written a &lt;a href=&#34;https://andreyor.st/posts/2020-12-03-we-need-to-talk-about-parentheses/&#34;&gt;small article&lt;/a&gt; about parentheses, and various structural editing solutions in this blog, but I&amp;rsquo;ve actually changed my mind regarding Paredit style editing since that time.
Learning some additional shortcuts and chords was not as bad as I thought it would be.&lt;/p&gt;
&lt;p&gt;So I switched to Paredit, but it had some bugs related to Clojure, and I&amp;rsquo;ve replaced it with Smartparens with Paredit keybindings, and I was basically (mostly) satisfied ever since.
But recently I&amp;rsquo;ve read &lt;a href=&#34;https://www.masteringemacs.org/&#34; target=&#34;_blank&#34;&gt;Mastering Emacs&lt;/a&gt; (a great book) and decided to greatly simplify my Emacs config.
I was tempted to replace Smartparens with Paredit because it felt simpler and more focused, but I&amp;rsquo;ve figured out that it breaks the &lt;em&gt;tempo&lt;/em&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.
I&amp;rsquo;ve tried to fix most shortcomings of Paredit, but it felt too clunky, and I&amp;rsquo;ve dropped the idea, since Smartparens doesn&amp;rsquo;t have such problems, so I&amp;rsquo;ve returned to it.
Funnily enough, the sole reason I wanted to replace Smartparens with Paredit was that I felt that the configuration is way too complicated, but in the end, with all these fixes, Paredit&amp;rsquo;s configuration was even longer than the one I had for Smartparens.&lt;/p&gt;
&lt;p&gt;So here are some problems with Paredit that I&amp;rsquo;ve noticed after reading Mastering Emacs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Most commands that should accept negative (or just an arbitrary prefix argument) due to being mapped on a key that &lt;strong&gt;usually&lt;/strong&gt; accepts it, completely ignore it instead.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;M-- M-d&lt;/code&gt; that acts as &lt;code&gt;M-backspace&lt;/code&gt; in most editing modes, just ignores the negative argument in Paredit.
I&amp;rsquo;ve managed to fix that with the following advice, but I&amp;rsquo;m not sure that every such command can be fixed this way, as there might be no counterpart command.
This aspect of modifying commands with the (negative) argument became really important to me, because I&amp;rsquo;ve started using more motion-related commands, and Paredit basically prevented me from becoming more efficient.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-emacs-lisp&#34; data-lang=&#34;emacs-lisp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defvar&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-reversable-commands&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#39;((&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-forward-delete&lt;/span&gt; . &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-backward-delete&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-forward-kill-word&lt;/span&gt; . &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-backward-kill-word&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Alist of &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;`paredit-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt; command and their backward motion equivalents.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst/paredit-support-negative-arg&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;orig-fn&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;rest&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Find ORIG-FN in &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;`aorst--paredit-reversable-commands&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt; variable,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;and apply its counterpart, when the &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;`current-prefix-arg&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt; is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;negative.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;eq&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;current-prefix-arg&lt;/span&gt; &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#39;-&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;let&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;current-prefix-arg&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;if-let&lt;/span&gt; ((&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;cdr&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;assoc&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;this-command&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-reversable-commands&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;apply&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;if-let&lt;/span&gt; ((&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;car&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;rassoc&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;this-command&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-reversable-commands&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;apply&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;apply&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;orig-fn&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;apply&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;orig-fn&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;dolist&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;flatten-list&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-reversable-commands&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;advice-add&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;:around&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;#&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst/paredit-support-negative-arg&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Transient mark mode deletion feature that was added in Emacs 24 is purposely ignored by Paredit.&lt;/p&gt;
&lt;p&gt;This again can be fixed with advice, and actually works reliably enough, making me only wonder why Paredit maintainers decided to &lt;a href=&#34;https://github.com/emacsmirror/paredit/blob/8330a41e8188fe18d3fa805bb9aa529f015318e8/paredit.el#L1352-L1355&#34; target=&#34;_blank&#34;&gt;put &lt;strong&gt;censored swearing&lt;/strong&gt; in the comment&lt;/a&gt; instead of implementing the feature itself.
Paredit has everything needed to do this, and yet chose to ignore the feature.
It&amp;rsquo;s easy to fix this with advice, but again, Smartparens does respect Emacs&amp;rsquo; design decisions.
Here&amp;rsquo;s the code I&amp;rsquo;ve written to fix this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-emacs-lisp&#34; data-lang=&#34;emacs-lisp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defvar&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-delete-region-functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#39;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-forward-delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-backward-delete&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;List of &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;`paredit-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt; functions that should support tmm region deletion.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defvar&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-kill-region-functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#39;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-forward-kill-word&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-backward-kill-word&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;List of &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;`paredit-mode&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt; functions that should support tmm region killing.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defun&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst/paredit-fix-tmm&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;orig-fn&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;rest&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Allow deleting/killing a region if the expression is balanced.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;transient-mark-mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;mark-active&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;cond&lt;/span&gt; ((&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;memq&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;this-command&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-delete-region-functions&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-delete-region&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;region-beginning&lt;/span&gt;) (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;region-end&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ((&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;memq&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;this-command&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-kill-region-functions&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;paredit-kill-region&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;region-beginning&lt;/span&gt;) (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;region-end&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;t&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;apply&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;orig-fn&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;apply&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;orig-fn&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;args&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;dolist&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;append&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-delete-region-functions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                   &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst--paredit-kill-region-functions&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;advice-add&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;:around&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;#&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;aorst/paredit-fix-tmm&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the end, after simplifying my config, I&amp;rsquo;ve completely turned off the transient mark mode, as it is really not as important as I thought.
So maybe this is why Paredit authors didn&amp;rsquo;t bother to implement this feature, but I still think it&amp;rsquo;s not an excuse not to support it, some users may rely on it.
And I still occasionally enable &lt;code&gt;tmm&lt;/code&gt; with the &lt;code&gt;C-space C-space&lt;/code&gt; shortcut, so support for this feature is still important to me.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Point position is wrong after using splice commands in inferior-lisp type REPLs and any other REPLs I&amp;rsquo;ve tried so far.&lt;/p&gt;
&lt;p&gt;This actually looks like a bug, but I&amp;rsquo;ve also noticed that not a lot of people use structural editing in the REPL (maybe because of that?).
It&amp;rsquo;s a minor inconvenience, however, I&amp;rsquo;ve had zero problems with Smartparens strict mode enabled in REPLs so far.
This is very important to me, as I often write code directly in the REPL buffer, so I&amp;rsquo;d like to have all my structural editing features.
Sadly, I wasn&amp;rsquo;t able to fix this myself.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All this may sound like I&amp;rsquo;m praising the Smartparens here, but it&amp;rsquo;s actually not true.
Smartparens has a lot of its own quirks and problems, that I had to deal with.
It is by no means a silver bullet, but in the current state it&amp;rsquo;s good enough, I guess.&lt;/p&gt;
&lt;p&gt;My dream package would be something based off &lt;a href=&#34;https://tree-sitter.github.io/tree-sitter/&#34; target=&#34;_blank&#34;&gt;Tree-sitter&lt;/a&gt; (or any other blazingly fast parser) because it actually understands the structure of the code unlike Smartparens, which uses the regex-based search for that.
Yes, regex can be used for implementing structural editing, and it works to some degree for languages other than Lisps, as shown by Smartparens, but it&amp;rsquo;s not near as accurate, as it would be with a proper parser.
I&amp;rsquo;ve tried my best at implementing an algorithm that implements a backward syntax parser for Elixir to make Smartparens better understand the &lt;code&gt;do:&lt;/code&gt; keyword, as it doesn&amp;rsquo;t require matching the &lt;code&gt;end&lt;/code&gt; keyword, which broke the parser.
It kinda works, but still breaks on some combination of symbols in the code, so it&amp;rsquo;s not nearly as good as a solution with a proper parser.&lt;/p&gt;
&lt;p&gt;However, even with its quirks, Smartparens with the Paredit compatibility layer, and respect for the Emacs editing model, make it overall a better Emacs citizen than Paredit.
Yes, it&amp;rsquo;s somewhat clunkier to configure, but in the end, you have more EMACSy structural editing than you get with Paredit itself, and I actually don&amp;rsquo;t really understand why is that, as Paredit is older, and was used by Emacs hackers for quite a long time before Smartparens appeared.
If anyone knows the story behind that, please let me know!&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested to try out Smartparens, &lt;a href=&#34;https://gitlab.com/andreyorst/dotfiles/blob/21ef6737690678a5e29d47e0563df095e25cd873/.config/emacs#smartparens&#34; target=&#34;_blank&#34;&gt;here&amp;rsquo;s my configuration&lt;/a&gt;, that makes it work similarly to Paredit in most aspects, so you don&amp;rsquo;t have to re-learn your muscle memory.
I&amp;rsquo;m suggesting you try out Smartparens for yourself, and see if these features I&amp;rsquo;ve talked about are really worth the switch.
It was for me for sure.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Tempo is the term from the Mastering Emacs book, which basically means that you don&amp;rsquo;t need to release Ctrl or Meta keys for some chord progressions, like when using &lt;code&gt;M-- M-d&lt;/code&gt; instead of &lt;code&gt;C-- M-d&lt;/code&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Why is Paredit is so un-Emacsy?&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Thu, 30 Sep 2021 23:40:00 +0300</pubDate>
    </item><item>
      <title>Goodbye Google Play Music</title>
      <link>https://andreyor.st/posts/2020-06-24-goodbye-google-play-music/</link>
      <guid>https://andreyor.st/posts/2020-06-24-goodbye-google-play-music/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://killedbygoogle.com/&#34; target=&#34;_blank&#34;&gt;Another good service&lt;/a&gt; is going to be shut down by Google.
I was a happy user of Google Play Music (GPM) for a long time, and although it had some problems, I still enjoyed it.
It has a decent web interface, and a fine mobile app, but most importantly I&amp;rsquo;ve listened to a lot of new music every day, and my library constantly evolved, thanks to Google recommendation systems, which is actually good.&lt;/p&gt;
&lt;p&gt;But today I received a mail, in which Google asked if I want to transfer my GPM library into the YouTube Music service.
I knew almost nothing about YouTube Music, so I searched a bit, and that seemed to be quite the same service, just re-branded, and with a bit more tight integration with the YouTube platform.
So I&amp;rsquo;ve transferred my library and went testing their new service.
At first, everything seemed to be OK, but eventually, I stumped into some issues.&lt;/p&gt;
&lt;h2 id=&#34;music-availability-and-topics&#34;&gt;Music availability and Topics&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s one album that I wanted to listen to:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/ebonivory-gpm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;But since I&amp;rsquo;ve transferred my music into YouTube Music, I&amp;rsquo;ve opened it there:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/ebonivory-ytm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;And, it is not the same.
It seems that even though I&amp;rsquo;ve imported music &lt;em&gt;collection&lt;/em&gt; from GPM into YouTube Music, apparently this doesn&amp;rsquo;t mean that I will have the &lt;em&gt;same music&lt;/em&gt;.
To make it more clear, in case you don&amp;rsquo;t see what&amp;rsquo;s the problem, here&amp;rsquo;s a table with song durations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Song Name&lt;/th&gt;
&lt;th&gt;Duration (Google Play Music)&lt;/th&gt;
&lt;th&gt;Duration (YouTube Music)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Violet Storm&lt;/td&gt;
&lt;td&gt;2:09&lt;/td&gt;
&lt;td&gt;2:09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Space Between&lt;/td&gt;
&lt;td&gt;4:28&lt;/td&gt;
&lt;td&gt;3:26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swineheart&lt;/td&gt;
&lt;td&gt;5:28&lt;/td&gt;
&lt;td&gt;0:34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Silent Sirens&lt;/td&gt;
&lt;td&gt;5:02&lt;/td&gt;
&lt;td&gt;2:17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Machine&lt;/td&gt;
&lt;td&gt;5:59&lt;/td&gt;
&lt;td&gt;1:15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ouroboros&lt;/td&gt;
&lt;td&gt;4:38&lt;/td&gt;
&lt;td&gt;0:28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;King of the Thistle Field&lt;/td&gt;
&lt;td&gt;6:00&lt;/td&gt;
&lt;td&gt;0:17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;More Than Numbers&lt;/td&gt;
&lt;td&gt;1:41&lt;/td&gt;
&lt;td&gt;1:18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Only Constant&lt;/td&gt;
&lt;td&gt;17:27&lt;/td&gt;
&lt;td&gt;17:27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Violent Sky&lt;/td&gt;
&lt;td&gt;7:03&lt;/td&gt;
&lt;td&gt;3:31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;60 minutes&lt;/td&gt;
&lt;td&gt;32 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Obviously, something is not right, as some songs are way too short, but there&amp;rsquo;s no clear way how to submit a report, and I&amp;rsquo;m totally unsure if the issue is going to be fixed since this band has quite a low amount of subscribers - 63 at the moment of writing this.
And the reason for this problem is this &lt;a href=&#34;https://www.youtube.com/watch?v=NK_X-yLsCnw&amp;amp;list=OLAK5uy_nQlMBluANlN2KArpMsOT4taT8LB2HqsIo&#34; target=&#34;_blank&#34;&gt;YouTube topic&lt;/a&gt;.
These topics are created automatically and will contain any garbage that was uploaded by someone, even if the tracks are cropped or edited.
This is not acceptable for a music service.
These topics can really hurt music bands.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve then checked some other rather obscure bands (I don&amp;rsquo;t mean to offend anyone from these bands, your music is amazing), and found some differences in these albums.
For example, Known/Learned album by Arcane - many tracks have different lengths in these two services, but song versions seem to be the same.
Also strange thing:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/unturning.png&#34;/&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;band-discover-ability&#34;&gt;Band discover-ability&lt;/h2&gt;
&lt;p&gt;Another problem is that bands with names that consist only of generic words are still handled poorly.
This was an issue in GPM, and still an issue in YouTube Music.
For example, if you open &lt;a href=&#34;https://needband.bandcamp.com/&#34; target=&#34;_blank&#34;&gt;Need&lt;/a&gt; band page from their latest album at GPM you&amp;rsquo;ll see a page for the &amp;ldquo;need®&amp;rdquo; band:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/need-gpm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;m not sure if &amp;ldquo;need®&amp;rdquo; is a real thing.
But if we will do the same thing on YouTube Music we will see this page:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/need-ytm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;For some reason, the page is called &amp;ldquo;I need $$&amp;rdquo;, has the cover from the &amp;ldquo;Every Day&amp;rdquo; album by completely another band, and even does not display this album alongside the two others.
Well, at least in GPM it was just lowercase &amp;ldquo;need®&amp;rdquo;, which is still incorrect, but closer.
However, Need band also has another of their albums at GPM, which are listed on a separate page, and if we will open this band page from one of those albums, we will see an entirely different band page:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/need-real-gpm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;The page is named &amp;ldquo;The Need&amp;rdquo; which is not the correct name once again, and there is a band with such a name, which is listed along with albums that I&amp;rsquo;m actually looking for.
We can do the same thing on YouTube Music, and the resulting page has mostly the same content:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/need-real-ytm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;So in the case of this band, it seems that YouTube Music fully inherited the problems of GPM, and I&amp;rsquo;m not sure if re-branding is supposed to fix this or not with their topic system.
And if it was my band I would be offended by such poor handling of band names.&lt;/p&gt;
&lt;h2 id=&#34;library-management&#34;&gt;Library management&lt;/h2&gt;
&lt;p&gt;Another issue with YouTube Music is that it sorts my library in a very strange way.
E.g. when I open the &amp;ldquo;Artists&amp;rdquo; section I see this:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/ytm-artist-sort.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;And there&amp;rsquo;s no way to sort this thing in a more manageable way, like alphabetically.
Why would I want this?
It&amp;rsquo;s way easier to navigate and search for some artists if they are grouped together.
Currently, I have a lot of trouble finding one specific artist I want to listen to because of this.
Literally spent about 7 minutes to find it, because I didn&amp;rsquo;t remember the artist&amp;rsquo;s name, but at least I remembered that it started with &amp;ldquo;R&amp;rdquo;, and there aren&amp;rsquo;t many artists that share this property, so in an alphabetically sorted list I would find it way faster.&lt;/p&gt;
&lt;p&gt;And, GPM had a very handy feature, which seems to be missing from YouTube Music.
Here&amp;rsquo;s how the artist page looks in GPM:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/hord-gpm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;You can see that I&amp;rsquo;ve decided to add their latest album to my library, because I&amp;rsquo;ve liked it, and perhaps want to listen to it in the future.
Here&amp;rsquo;s this artist&amp;rsquo;s page on YouTube Music:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/hord-ytm.png&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;ve imported my collection from GPM, and this artist is presented in my library, but there&amp;rsquo;s no way to tell which albums are in my library unless I explicitly check every and each of their albums for the &amp;ldquo;REMOVE FROM LIBRARY&amp;rdquo; button, and, perhaps, put this somewhere on the napkin so I won&amp;rsquo;t forget which albums I was actually interested in:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://andreyor.st/2020-06-24-goodbye-google-play-music/hord-album.png&#34;/&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;youtube-music-identity&#34;&gt;YouTube Music identity&lt;/h2&gt;
&lt;p&gt;I feel that the change, that motivated Google to shut down GPM and move its users into YouTube Music is simply because the Google Play thing is not attractive to an average user.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m thinking this way, because Google had a lot of services with such naming, e.g. Google Play Games, Google Play Books, Google Play Movies, e.t.c.
And the Google Play thing seemed to be a big part of the branding, but perhaps not big enough for Music.
And there are no new services that have Google Play in their names.&lt;/p&gt;
&lt;p&gt;YouTube on the other hand is a very popular service, and its popularity increases daily, so calling another service after the most popular may seem like a good marketing move.
&lt;em&gt;I wonder if YouTube books will ever become a thing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;However, I don&amp;rsquo;t think that YouTube Music makes sense as a service.
YouTube&amp;rsquo;s primary content type is videos.
Sure there are music videos, and many artists used YouTube to host their entire albums as playlists with album cover art as a video, and some sort of a table of contents.&lt;/p&gt;
&lt;p&gt;And YouTube Music seems to take this idea and turn it into a music service, where the video is not the main content, as not all tracks have music videos filmed for those.
Previously GPM listed music videos under the album page, so you could watch these if you wanted to, but YouTube music takes the opposite approach, makes albums out of videos, and allows an option to play album versions of songs instead of music video versions (only in the paid plan).
This kinda hurts the experience, because the video version is usually edited - it is often shorter, or simply a radio edit version.
Or even, has some kind of generic band outro that breaks the flow when you&amp;rsquo;re listening to an album as a whole.&lt;/p&gt;
&lt;p&gt;So for YouTube fans, YouTube Music may seem like a cool service, but for Music fans, YouTube Music may seem more of a downgrade as a service.
Because we love music and do not usually care for the videos.&lt;/p&gt;
&lt;h2 id=&#34;alternatives&#34;&gt;Alternatives&lt;/h2&gt;
&lt;p&gt;GPM will be shut down in December 2020.
I don&amp;rsquo;t think YouTube Music will become any better by this time since too much music needs fixing, which was not done during the GPM lifetime, and will not likely be done until shutdown.
And all this YouTube topic thing is not a good replacement for real albums that were available on GPM.&lt;/p&gt;
&lt;p&gt;Thankfully there are alternatives like Spotify, Apple Music, and TIDAL, though their prices are different, and the music library maybe not be as big as GPM&amp;rsquo;s one.
Although the library can be even bigger.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve liked GPM because it allowed me to legally listen to countless amounts of tracks, albums, and bands without buying every single track I&amp;rsquo;ve liked.
And I would not want to buy anything on Google Play Music or any other streaming service because of DRM.
If there&amp;rsquo;s a way to buy something without DRM I will always prefer it.
But paid subscription that allows me to listen to music without borders is an amazing thing, even if I do not own any of this music.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m thinking to transfer my library to another service, and I guess it is goodbye for Google Play Music and YouTube Music altogether.&lt;/p&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: Goodbye Google Play Music&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Wed, 24 Jun 2020 17:53:00 +0300</pubDate>
    </item><item>
      <title>C Needs Better Syntax and Macros</title>
      <link>https://andreyor.st/posts/2020-04-06-c-needs-a-better-syntax/</link>
      <guid>https://andreyor.st/posts/2020-04-06-c-needs-a-better-syntax/</guid>
      <description>&lt;p&gt;Today I&amp;rsquo;ve decided that I want to write a little rant on the topic of language design.
Since I&amp;rsquo;m a C programmer, and I use it on a daily basis, I&amp;rsquo;ve had many thoughts about C syntax over the past few years.
I like C, and it is a good language, that defined modern operating systems, and software.
But I think that C still needs a better syntax and macro system.
Not because C syntax is inconvenient to use, but because the syntax is a tool of expression.
And macros can extend this concept even further.&lt;/p&gt;
&lt;p&gt;You know, we, programmers, use a programming &lt;strong&gt;language&lt;/strong&gt; to communicate with computers.
When people are communicating with other people, the language is constantly improved, by adding new constructions and removing inconvenient and old ones.
This helps language to be up with the current realities, and not die like, for example, the Latin language.
No one speaks Latin not because it is inconvenient, but because it is poor.
And C syntax is poor as well.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s have a little test.
Imagine that you&amp;rsquo;re a C compiler, I&amp;rsquo;ll give you the beginning of the line, and you will try to guess what is happening in it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; something&lt;span style=&#34;&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What could it be?
This is certainly a declaration of something, but what exactly?
Maybe it is an integer variable declaration, like &lt;code&gt;int something = something_else;&lt;/code&gt;.
Or maybe it is function declaration, like &lt;code&gt;int something(something_else);&lt;/code&gt;.
Or maybe &lt;code&gt;something&lt;/code&gt; is actually a macro?
Then &lt;code&gt;something&lt;/code&gt; may actually be &lt;code&gt;something_else&lt;/code&gt;.
And don&amp;rsquo;t think that if you see &lt;code&gt;something(something_else);&lt;/code&gt; it always will be a function, because it can actually be a function-like macro that expands to whatever it likes to!
Ah, that also can be an array pointer - &lt;code&gt;int something[]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no way to tell…
unless we look ahead.
Sure, it is not a problem for the compiler, and such things as functions, or arrays have obvious and well-known syntax.
In the case of macros, well, you can&amp;rsquo;t know without checking it yourself or using some sort of a tool that can show if the thing under the cursor is macros or not.
This may not be a problem for compilers, but for us, humans, this is an issue, because we see code, but can&amp;rsquo;t immediately see the connection between the code and what actually will happen.
This may seem like a minor issue, but when dealing with large codebases which use tons and tons of complex macros, it became really tedious.
Most of the time good tooling can help with that, but more certainty would be an improvement.&lt;/p&gt;
&lt;p&gt;Of course, we can complain all we want, so instead let&amp;rsquo;s discuss how to improve this.
I&amp;rsquo;ve programmed for more than 5 years in C at work, mostly writing bare metal software for SoC, so my interest lies in the niche of compiled/system languages.
I&amp;rsquo;ve also occasionally used C++ at work, seen some Java syntax, and recently got into Rust.
Rust is somewhat known as a language with quite ugly and verbose syntax, but I don&amp;rsquo;t think so, and its syntax improves much upon C and C++.
And I&amp;rsquo;m sorry C++ guys, but C++ syntax is pile of trash&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s compare the example code above with Rust variants for each of our guesses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;fn&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;something&lt;/span&gt;&lt;span style=&#34;&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Variable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;let&lt;/span&gt; something&lt;span style=&#34;&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pointer and array:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;let&lt;/span&gt; something&lt;span style=&#34;&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;let&lt;/span&gt; something&lt;span style=&#34;&#34;&gt;…&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Uh oh, pointers and arrays are declared exactly how variables are, but that&amp;rsquo;s not a problem, because in Rust variables hold pointers, and type of the variable is specified &lt;strong&gt;after&lt;/strong&gt; variable name and before assignment.
So it is not an issue, because we know, that plain variable, array, pointer, hash-map, and everything else will have the same syntax.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Macro:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;something!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bang!
Macros always end with &lt;code&gt;!&lt;/code&gt;, which makes those easy to see and expect something to happen.
This means, that Rust covers 3 out of four complaints I had, and the remaining one is meaningful in terms of the language itself, so this is kind of 4 out of 4.
Though these are not the only problems with C syntax, so let&amp;rsquo;s dive more in-depth.&lt;/p&gt;
&lt;h2 id=&#34;type-cyntax&#34;&gt;Type &lt;em&gt;Cyntax&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s discuss the type syntax.
C features these default types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;char&lt;/code&gt; - for 8 bit sized data.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;short&lt;/code&gt; - for at least 16 bit sized data.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt; - for at least 16 bit sized data.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;long&lt;/code&gt; - for at least 32 bit sized data.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;float&lt;/code&gt; - for single precision floating point data.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;double&lt;/code&gt; - for double precision floating point data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far so good.
Well, sizes are &lt;em&gt;bit&lt;/em&gt; strange, like &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;short&lt;/code&gt; are both at least 16 bit, but this is architecture dependent, and that&amp;rsquo;s another problem and another topic.
There&amp;rsquo;s one more type that exists but doesn&amp;rsquo;t quite fit the previous list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;long long&lt;/code&gt; - for at least 64bit sized data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Did you notice that?
No?
Then let me point it out.
It uses two words to describe a single idea while previously we had single-worded types, that represented a single concept.&lt;/p&gt;
&lt;p&gt;Well, actually all types that were listed above can use two words, e.g. &lt;code&gt;unsigned char&lt;/code&gt;, but this describes that this very type is not only for 8bit but also has no sign.
There are two concepts that are described with two words.
Not for &lt;code&gt;unsigned long long&lt;/code&gt; though.&lt;/p&gt;
&lt;p&gt;To be honest, all those types except &lt;code&gt;int&lt;/code&gt;, and &lt;code&gt;char&lt;/code&gt;, can be written using two words that specify length.
We can rewrite the previous list as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;char&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;short int&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;long int&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It makes sense for &lt;code&gt;char&lt;/code&gt; not to be &lt;code&gt;short&lt;/code&gt; or &lt;code&gt;long&lt;/code&gt; because it is widely used to store ASCII characters, which need 256 variants and this always needs 8bits.
This also makes sense for &lt;code&gt;int&lt;/code&gt; to be &lt;code&gt;short&lt;/code&gt; or &lt;code&gt;long&lt;/code&gt;, to fit 16 or 32 bits.
&lt;strong&gt;Not for &lt;code&gt;long long&lt;/code&gt; though!&lt;/strong&gt; See:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;long long int&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again three words to describe the concept of two.
Anyway, I&amp;rsquo;m nitpicking because there&amp;rsquo;s a better approach available.
And most C programmers use it, I believe:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;signed&lt;/span&gt;   &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; int64;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; uint64;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we&amp;rsquo;re now describing the whole concept with a single word: &lt;code&gt;uint64&lt;/code&gt;.
Look how easy it is now to get everything we need to know.
Size?
&lt;code&gt;64&lt;/code&gt;.
Has sign?
Check for &lt;code&gt;u&lt;/code&gt; at the start of the word.
This can be repeated for all other types:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;signed&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;char&lt;/span&gt;   int8;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;char&lt;/span&gt; uint8;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;signed&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;short&lt;/span&gt;   int16;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;short&lt;/span&gt; uint16;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;signed&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;   int32;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; uint32;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;signed&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt;   int64;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;unsigned&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; uint64;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the common pattern?
&lt;code&gt;int&lt;/code&gt; repeats in every type name we defined.
Rust went further and removed it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i8&lt;/code&gt;, &lt;code&gt;u8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i16&lt;/code&gt;, &lt;code&gt;u16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i32&lt;/code&gt;, &lt;code&gt;u32&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i64&lt;/code&gt;, &lt;code&gt;u64&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don&amp;rsquo;t think that &lt;code&gt;i&lt;/code&gt; for signed is the best choice, but I&amp;rsquo;m fine with this.
And types are now very consistent and short.
Although compound multi-word types have one advantage over a predefined set of types because we have more control over whether we want our type to be signed or long, this is also error-prone, because someone may assume that &lt;code&gt;long long long int&lt;/code&gt; is a valid type, while it&amp;rsquo;s not.&lt;/p&gt;
&lt;p&gt;While we&amp;rsquo;re on types, I think that defining types before objects have little sense too.
In Rust type is specified after object name and it reads well, e.g.:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;let&lt;/span&gt;    name  :  &lt;span style=&#34;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;str&lt;/span&gt;       =       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;John&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// |      |     |  |          |       |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// define thing of some type, holding something
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Compared to C:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;font-weight:bold&#34;&gt;char&lt;/span&gt; *       name   =       &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;John&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// |    |       |      |       |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// type pointer thing, holding something
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And it is known practice in C to read declarations in the opposite direction, e.g. &lt;code&gt;name&lt;/code&gt; is &lt;code&gt;pointer&lt;/code&gt; to &lt;code&gt;char&lt;/code&gt;.
There&amp;rsquo;s also a &lt;em&gt;clockwise spiral reading rule&lt;/em&gt; that can be applied to functions and arrays:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2020-04-06-c-needs-a-better-syntax-and-macros/clockwise-array.svg&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;code&gt;str&lt;/code&gt; is an &lt;code&gt;array&lt;/code&gt; of &lt;code&gt;10&lt;/code&gt; &lt;code&gt;pointers&lt;/code&gt; to a &lt;code&gt;char&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Although for the function pointer it seems that counterclockwise fits better:&lt;/p&gt;
&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2020-04-06-c-needs-a-better-syntax-and-macros/clockwise-function.svg&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;code&gt;fp&lt;/code&gt; is a &lt;code&gt;pointer&lt;/code&gt; to a &lt;code&gt;function&lt;/code&gt; that accepts an &lt;code&gt;int&lt;/code&gt; and a &lt;code&gt;pointer&lt;/code&gt; to a &lt;code&gt;char&lt;/code&gt; and returning a &lt;code&gt;pointer&lt;/code&gt; to a &lt;code&gt;char&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;English is not &lt;a href=&#34;https://en.wikipedia.org/wiki/Right-to-left&#34; target=&#34;_blank&#34;&gt;RTL&lt;/a&gt;, why would we need that stuff in C?
Rust uses one more construct to define this and it is &lt;code&gt;:&lt;/code&gt;, which in C is used in &lt;code&gt;?:&lt;/code&gt; operator and in &lt;code&gt;case&lt;/code&gt; statements.
And, &lt;code&gt;case _:&lt;/code&gt; is a well-defined syntax, since &lt;code&gt;case&lt;/code&gt; is a keyword, so we can&amp;rsquo;t mix it up with type annotation.
&lt;code&gt;?:&lt;/code&gt; is used only after &lt;code&gt;=&lt;/code&gt;, so it&amp;rsquo;s also well defined:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          a     :  &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;   =       42;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;//        |     |  |     |       |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// define thing of type, holding 42
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;font-weight:bold&#34;&gt;case&lt;/span&gt; 0 :           a     :  &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;  = cond  ?      1 :  0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// |    | |           |     |  |    |       |      | |  |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// case 0 then define thing of type holding either 1 or 0, depending on cond
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Case with &lt;code&gt;case&lt;/code&gt; is like an &lt;em&gt;edge case&lt;/em&gt;, and quite uncommon even with current syntax, so I don&amp;rsquo;t think that this is a problem.
There is another use for &lt;code&gt;:&lt;/code&gt;, which I&amp;rsquo;ve never actually seen in my practice, that it can be used for bit size indication instructs, so unfortunately we can&amp;rsquo;t use it for types in the same way how we use &lt;code&gt;*&lt;/code&gt; both for pointers and multiplication:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; item {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; f : 28; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// 28-bit field
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this just means that for bit fields we can specify that it is valid to use &lt;code&gt;:&lt;/code&gt; for the second time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; item {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f: &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; : 28;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just like that in official syntax &lt;code&gt;:&lt;/code&gt; as a bit-field specifier is the only valid syntax in &lt;code&gt;struct&lt;/code&gt;, the second use of &lt;code&gt;:&lt;/code&gt; could be also only valid in &lt;code&gt;struct&lt;/code&gt;.
This way we could use uniform syntax for type declaration.
Here&amp;rsquo;s a side-by-side comparison of the standard syntax, and what I think is a better alternative:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; my_IEEE754 {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;union&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; sf_t {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; fraction : 23;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; exp : 8;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; sign : 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } sf;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;float&lt;/span&gt; hf;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } f;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} my_float;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; argc, &lt;span style=&#34;font-weight:bold&#34;&gt;char&lt;/span&gt; * argv[])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    my_float pi = { .f.hf = 3.1415f };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now with imaginary syntax.
I&amp;rsquo;ve kept those constructs that I&amp;rsquo;m fine with, such as &lt;code&gt;struct&lt;/code&gt; initializer, and I also changed &lt;code&gt;char*[]&lt;/code&gt; to &lt;code&gt;[*char]&lt;/code&gt; so it could be read as &lt;code&gt;array&lt;/code&gt; of &lt;code&gt;pointer&lt;/code&gt; to &lt;code&gt;char&lt;/code&gt; without falling back RTL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; my_IEEE754 {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f: &lt;span style=&#34;font-weight:bold&#34;&gt;union&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sf: &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; sf_t {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            fraction: &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; : 23;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            exp: &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; : 8;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sign: &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; : 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        hf: &lt;span style=&#34;font-weight:bold&#34;&gt;float&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} my_float;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;main(argc: &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;, argv: [*&lt;span style=&#34;font-weight:bold&#34;&gt;char&lt;/span&gt;]) -&amp;gt; &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pi: my_float = { .f.hf = 3.1415f }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I understand that not everyone will agree with me here.
I also prefer trailing return type, as in Rust, and even though &lt;code&gt;-&amp;gt;&lt;/code&gt; is reserved for accessing &lt;code&gt;struct&lt;/code&gt; fields from pointers, which is also true for C++11, C++11 still can use it for specifying the return type of the function, so I think C also could do that.&lt;/p&gt;
&lt;p&gt;Although it seems that without a type at the beginning it&amp;rsquo;s harder to tell when we&amp;rsquo;re declaring something, and when we using a defined variable, Rust uses a special keyword &lt;code&gt;let&lt;/code&gt;, since types can be omitted:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;mut&lt;/span&gt; a = 42; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// declaration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;        a = 27; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// later use
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, in C we always specify type when declaring a variable so &lt;code&gt;let&lt;/code&gt; can be omitted:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a: &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; = 42; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// declaration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;     a = 27; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// usage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pointers in C also have one interesting error-prone syntax case.
Look at this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * a, b;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2020-04-06-c-needs-a-better-syntax-and-macros/double-clockwise.svg&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;If we read this right to left or use clockwise rule, we will see, that &lt;code&gt;b&lt;/code&gt; is of type &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;a&lt;/code&gt; is a &lt;code&gt;pointer&lt;/code&gt; to &lt;code&gt;int&lt;/code&gt;.
But during my practice, I&amp;rsquo;ve seen many cases when other programmers read this left to right, e.g. &amp;ldquo;declaration of the integer pointers &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;&amp;rdquo;.
Unless you really know what&amp;rsquo;s going on, you can spend a lot of time wondering why your code misbehaves.
And one reason for it to misbehave could be that someone forgot to add another asterisk:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * a, * b;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class=&#34;invertable&#34;&gt;&lt;img src=&#34;https://andreyor.st/2020-04-06-c-needs-a-better-syntax-and-macros/double-pointer-clockwise.svg&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;Because of this problem, there are three different conventions on where to place the asterisk - near the type, near the variable, or in between.
I, personally use the last one, as you can see, but this code can also be rewritten in this form:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;* a, * b; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// quite ugly
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; *a, *b;  &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// more concise
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first one is quite ugly, and error-prone, because it may seem, that we&amp;rsquo;ve specified &lt;code&gt;int*&lt;/code&gt; type and all objects will have that type.
And the second asterisk looks really out of place because of that.
The second one is a bit better, but I think that it fails to define an idea that we&amp;rsquo;re pointing to type.
I rarely write such constructions, because it gets messy when you specify values, so I split this into two lines:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * a; &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;// easy to refactor
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * b;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is less error-prone and easier to refactor.
And I think that I would rework this to be&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a: *&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;b: *&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s another interesting moment.
C supports multi-word types, but users can declare only single-word types.
This is a kinda double standard for me.
And even if we could create multi-word types would be a total mess!
And multi-word types ruin one major aspect, and it is used in macros.&lt;/p&gt;
&lt;h2 id=&#34;macros&#34;&gt;Macros&lt;/h2&gt;
&lt;p&gt;Macros in C are not like macros in languages that feature macro systems as part of the language, like Lisp, or even Rust.
In C macros are &lt;em&gt;simple&lt;/em&gt; text substitutions, and expanded before compilation by using a preprocessor.
So if you will write &lt;code&gt;#define five 5&lt;/code&gt; and then use &lt;code&gt;five&lt;/code&gt; in several places, right before compilation, preprocessor will substitute &lt;code&gt;five&lt;/code&gt; with &lt;code&gt;5&lt;/code&gt;.
&lt;code&gt;five&lt;/code&gt; is not a variable, but simply a placeholder for specific data.
This is the most simple macro, but that&amp;rsquo;s not all that a preprocessor offers.
We can use many things like &lt;code&gt;#if&lt;/code&gt;, &lt;code&gt;#ifndef&lt;/code&gt;, &lt;code&gt;#else&lt;/code&gt; e.t.c.
to alter how our source code will be compiled.
And this is a source of some problems as well.&lt;/p&gt;
&lt;p&gt;The first problem we stated was that macros can mimic the code too well.
Because macros in C differ from actual C code and are a foreign thing, there should be a way of knowing what is a macro.
The other problem with C macros is that those are text &lt;em&gt;substitutions&lt;/em&gt;.
Consider the following macro:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#define vec(T)          \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    struct vector_##T { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        T * data;       \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        size_t size;    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can use such a pattern to create &lt;a href=&#34;https://en.wikipedia.org/wiki/Generic_programming&#34; target=&#34;_blank&#34;&gt;generics&lt;/a&gt; or templates if you&amp;rsquo;re familiar with C++.
This macro then can be used like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vec(&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt;) vector_of_ints = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vec(&lt;span style=&#34;font-weight:bold&#34;&gt;float&lt;/span&gt;) vector_of_floats = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To understand what&amp;rsquo;s going on here, let&amp;rsquo;s substitute the macro call by hand.
First, we substitute &lt;code&gt;vec(int)&lt;/code&gt;, with macro body and remember &lt;code&gt;int&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; vector_&lt;span style=&#34;&#34;&gt;##&lt;/span&gt;T {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    T * data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    size_t size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} vector_of_ints = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve passed &lt;code&gt;int&lt;/code&gt; as a parameter to our macro so we substitute T with it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; vector_&lt;span style=&#34;&#34;&gt;##&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    size_t size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} vector_of_ints = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, concatenation will happen at each &lt;code&gt;##&lt;/code&gt;, producing &lt;code&gt;vector_int&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; vector_int {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    size_t size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} vector_of_ints = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point, we&amp;rsquo;ve got macro fully expanded, and that&amp;rsquo;s how &lt;code&gt;main&lt;/code&gt; function looks:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; vector_int {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        size_t size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } vector_of_ints = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vec(&lt;span style=&#34;font-weight:bold&#34;&gt;float&lt;/span&gt;) vector_of_floats = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then this repeats to &lt;code&gt;vec(float)&lt;/code&gt;, producing the final code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; vector_int {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; * data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        size_t size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } vector_of_ints = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;struct&lt;/span&gt; vector_float {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;font-weight:bold&#34;&gt;float&lt;/span&gt; * data;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        size_t size;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } vector_of_floats = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty cool, right?
&lt;strong&gt;Wrong&lt;/strong&gt;.
We can&amp;rsquo;t do the same with &lt;code&gt;long long&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vec(&lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;long&lt;/span&gt;) vector_of_long_longs = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because &lt;code&gt;vector_##long long&lt;/code&gt; will produce &lt;code&gt;vector_long long&lt;/code&gt;, and we can&amp;rsquo;t have multi-word user types.
We can fix this by creating &lt;code&gt;typedef&lt;/code&gt;, but this means that we have to create &lt;code&gt;typedef&lt;/code&gt; for any type that we want to pass into such macros.
Meaning that the pointers also gonna need &lt;code&gt;typedefs&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;char&lt;/span&gt; * string;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vec(string) vector_of_strings = { .data = &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;NULL&lt;/span&gt;, .size = 0 };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is one side of the problem.
The other side is that we&amp;rsquo;re operating with &lt;strong&gt;text&lt;/strong&gt;, not &lt;strong&gt;AST&lt;/strong&gt;.
To understand the difference let&amp;rsquo;s look at some Lisp macros, for instance, Clojure &lt;code&gt;time&lt;/code&gt; macro:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defmacro &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;expr&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  `(&lt;span style=&#34;font-weight:bold&#34;&gt;let &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;start#&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;. &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;System&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nanoTime&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ret#&lt;/span&gt; ~&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;expr&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;prn &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;str &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Elapsed time: &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;/ &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;double &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;- &lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;. &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;System&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nanoTime&lt;/span&gt;)) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;start#&lt;/span&gt;)) 1000000.0)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; msecs&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ret#&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;m not going to teach you Lisp here, but still, let&amp;rsquo;s quickly understand what&amp;rsquo;s going on here.&lt;/p&gt;
&lt;p&gt;This macro does three things, first, it creates local binding &lt;code&gt;start#&lt;/code&gt; (&lt;code&gt;#&lt;/code&gt; will generate a unique suffix for the name), which will hold current system time in nanoseconds, creates local binding &lt;code&gt;ret#&lt;/code&gt;, that holds the result of an expression that we pass to &lt;code&gt;time&lt;/code&gt; and then prints line which shows the amount of &lt;code&gt;msecs&lt;/code&gt; that it took to evaluate the expression.
In the end, the &lt;code&gt;ret#&lt;/code&gt; is returned out of this macro.&lt;/p&gt;
&lt;p&gt;To understand this, let&amp;rsquo;s expand macro in this expression &lt;code&gt;(time (+ 1 2 3)&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;let*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;start1&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;. &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;System&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nanoTime&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ret1&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;+ &lt;/span&gt;1 2 3)] &lt;span style=&#34;color:#888;font-style:italic&#34;&gt;;; our sum expression&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;prn &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;str &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;Elapsed: &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;/ &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;double &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;- &lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;. &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;System&lt;/span&gt; (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;nanoTime&lt;/span&gt;)) &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;start1&lt;/span&gt;)) 1000000.0)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34; msecs&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;ret1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So what macro did is, took the given AST, took &lt;code&gt;(+ 1 2 3)&lt;/code&gt; expression out of it, and generated a new AST, where this expression is used in &lt;code&gt;let*&lt;/code&gt; form, and the result is returned after time is measured.
It&amp;rsquo;s a simple macro, but it shows some neat things about Lisp macros.
Of course, it doesn&amp;rsquo;t represent Lisp macro system as a whole, but the general idea of Lisp macros is that you take one AST and change it to produce another AST at compile time.
And you need to understand that we did not manipulate text here, but the code itself.&lt;/p&gt;
&lt;p&gt;This macro is simple enough, so let&amp;rsquo;s write it in C:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&amp;lt;time.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#define time(code)                                           \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    __extension__({                                          \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        clock_t _begin      = clock();                       \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        __typeof(code) _res = __extension__({ code; });      \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        printf(&amp;#34;Elapsed: %g sec.\n&amp;#34;,                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;               (double)(clock() - _begin) / CLOCKS_PER_SEC); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        _res;                                                \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;void&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    printf(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;%d&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;, time(1 + 2 + 3));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sure it works, and it uses some compile time magic, like &lt;code&gt;__typeof&lt;/code&gt;, and GCC statement expression extension &lt;code&gt;__extension__({ … })&lt;/code&gt;, but in the end this is still textual work.
It will fail if we can&amp;rsquo;t get &lt;code&gt;__typeof&lt;/code&gt; correctly, for example, if we want to measure our for loop:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;time(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;volatile&lt;/span&gt; &lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; i = 0; i &amp;lt; 0x1000; i++) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        printf(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;%d&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;, i);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will error, because &lt;code&gt;for&lt;/code&gt; has no type.
The problem is that we can&amp;rsquo;t return something if there&amp;rsquo;s nothing to return.
If we could analyze AST and produce valid code it would work.&lt;/p&gt;
&lt;p&gt;Another example of a C macro, that disables the evaluation of code is this &lt;code&gt;log&lt;/code&gt; macro.
The idea here is that the function, that we pass to &lt;code&gt;log&lt;/code&gt; may be slow, because it needs to get a huge amount of data, so we need a way to disable logging while still keeping all of our code.
Before looking at C, here&amp;rsquo;s the Ruby variant:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$debug = &lt;span style=&#34;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;log&lt;/span&gt;(&amp;amp;code)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;if&lt;/span&gt; $debug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $stderr.puts code.yield
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log {&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;message: &lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;#{&lt;/span&gt;expensive_logger&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the idea here is that we&amp;rsquo;re passing a function, and it will be evaluated only if &lt;code&gt;$debug&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.
Here&amp;rsquo;s the C version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;bool&lt;/span&gt; debug &lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#define log(fmt, ...)                          \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    do {                                       \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        if (debug)                             \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;            fprintf(stderr, fmt, __VA_ARGS__); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    } while (0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; main(&lt;span style=&#34;font-weight:bold&#34;&gt;void&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;message: %s&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;&lt;/span&gt;, expensive_logger());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we can see &lt;code&gt;…&lt;/code&gt; and &lt;code&gt;__VA_ARGS__&lt;/code&gt;.
These are special ways to pass and access a variadic amount of arguments to the macro.
This may seem like a little bit of AST manipulation, but again, this is just textual work, as &lt;code&gt;__VA_ARGS__&lt;/code&gt; expands to the sequence of arguments separated by commas, which means that we can&amp;rsquo;t do much with it.&lt;/p&gt;
&lt;p&gt;To understand why direct access to variadic arguments can be needed in macros, let&amp;rsquo;s look at another more complex macro from Clojure - thread first macro &lt;code&gt;-&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;defmacro &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt; &amp;amp; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;forms&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;font-weight:bold&#34;&gt;loop &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;, &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;forms&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;forms&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;font-weight:bold&#34;&gt;if &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;forms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;font-weight:bold&#34;&gt;let &lt;/span&gt;[&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;form&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;first &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;forms&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;threaded&lt;/span&gt; (&lt;span style=&#34;font-weight:bold&#34;&gt;if &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;seq? &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;form&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;with-meta &lt;/span&gt;`(~(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;first &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;form&lt;/span&gt;) ~&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt; ~@(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;next &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;form&lt;/span&gt;)) (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;meta &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;form&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;list &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;))]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;recur&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;threaded&lt;/span&gt; (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;next &lt;/span&gt;&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;forms&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;x&lt;/span&gt;)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a more complex macro, that transforms AST, as shown in the &lt;code&gt;time&lt;/code&gt; example, but in a different way.
To understand it, we have to see what form of code it takes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;-&amp;gt; &lt;/span&gt;1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;+ &lt;/span&gt;2)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;* &lt;/span&gt;3 4)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;/ &lt;/span&gt;5))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And what code it produces:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;/ &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;* &lt;/span&gt;(&lt;span style=&#34;font-weight:bold;font-style:italic&#34;&gt;+ &lt;/span&gt;1 2) 3 4) 5)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What essentially happens here is, that we pass list of expressions to &lt;code&gt;-&amp;gt;&lt;/code&gt;, which is &lt;code&gt;(1 (+ 2) (* 3 4) (/ 5))&lt;/code&gt; (lists in Lisp are delimited with parentheses), then we take first out, which is &lt;code&gt;1&lt;/code&gt;, and put it to the second position in &lt;code&gt;(+ 2)&lt;/code&gt; thus creating &lt;code&gt;(+ 1 2)&lt;/code&gt;.
Then we repeat it until we produce the final expression.
Here&amp;rsquo;s a table of steps, where &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;form&lt;/code&gt;, and &lt;code&gt;forms&lt;/code&gt; illustrate the values that these variables are holding in the macro above at each step of the &lt;code&gt;loop&lt;/code&gt;.
When &lt;code&gt;form&lt;/code&gt; turns empty, we return &lt;code&gt;x&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;x&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;form&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;forms&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(+ 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;((* 3 4) (/ 5))&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(+ 1 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(* 3 4)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;((/ 5))&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(* (+ 1 2) 3 4)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(/ 5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(/ (* (+ 1 2) 3 4) 5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;What&amp;rsquo;s interesting in this macro, and in Lisp in general, is that your code &lt;strong&gt;is&lt;/strong&gt; your AST.
This means that you can take it and change it however you want to, with the language itself.
There are no other facilities for this, but those you use when you write regular code.
This is not suitable for C, because the relationship between syntax and AST is not 1 to 1 as in Lisp, so we have to use preprocessors.&lt;/p&gt;
&lt;p&gt;In the example above, we saw iteration over forms that we are passing to macro.
You can do something similar to that in C:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#define first(x, ...) #x
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#define rest(x, ...)  #__VA_ARGS__
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;#define destructive(...)                              \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    do {                                              \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        printf(&amp;#34;first is: %s\n&amp;#34;, first(__VA_ARGS__)); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;        printf(&amp;#34;rest are: %s\n&amp;#34;, rest(__VA_ARGS__));  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;    } while (0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;main&lt;/span&gt;(&lt;span style=&#34;font-weight:bold&#34;&gt;void&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    destructive(1, 2, 3);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;font-weight:bold&#34;&gt;return&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because &lt;code&gt;__VA_ARGS__&lt;/code&gt; is just a comma-separated list of arguments, we can pass it to the macro, which takes 1 explicit argument &lt;code&gt;x&lt;/code&gt; and a variadic amount of rest arguments.
The example above converts arguments to strings, but you can spot the general idea.
Executing this code will print:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;first is: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rest are: 2, 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this is not really useful in the context of the language, because there&amp;rsquo;s no way to continuously iterate over such lists of arguments without defining a finite amount of iterator macros for a given amount of maximum possible arguments &lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;.
So such a thing as thread first macro isn&amp;rsquo;t really possible.
Which is a shame, because if there was a way to loop through macro at compile time something like &lt;code&gt;printf&lt;/code&gt; could easily be defined as a macro.
I&amp;rsquo;ve done printing macros based on this technique, which used Assembler primitive functions to display strings and numbers, and it performed way better than classic &lt;code&gt;printf&lt;/code&gt;.
This may seem like a strange point because usually performance of &lt;code&gt;printf&lt;/code&gt; is not something you really want to improve, but it depends on the task, and in my domain, a serious performance drop is noticeable when using &lt;code&gt;printf&lt;/code&gt; while working with bare metal emulation, which I do.
Also, Rust doesn&amp;rsquo;t support variadic arguments in functions, but its &lt;code&gt;println!&lt;/code&gt; macro surely does, because it is implemented in terms of iteration over the argument list and produces final printing code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;macro_rules! println {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    () =&amp;gt; (&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;$crate&lt;/span&gt;::print!(&lt;span style=&#34;color:#666;font-style:italic&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;$($arg&lt;/span&gt;:&lt;span style=&#34;color:#666;font-weight:bold;font-style:italic&#34;&gt;tt&lt;/span&gt;)*) =&amp;gt; ({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;$crate&lt;/span&gt;::io::_print(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;$crate&lt;/span&gt;::format_args_nl!(&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;$($arg&lt;/span&gt;)*));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What I&amp;rsquo;m trying to say is that defining Domain Specific Language (DSL) for your project is &lt;strong&gt;good&lt;/strong&gt; both for you and your project.
It will make you more productive, and your code will be cleaner and usually much more compact and readable.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s the point of the macro system in the language - it changes language to fit your concrete problem.
Most C programmers say that macros are bad and should be avoided, but I think they are just scared of those because the implementation is really bad and you can make your code very tangled up with all &lt;code&gt;#if&lt;/code&gt; &lt;code&gt;#else&lt;/code&gt; &lt;code&gt;#endif&lt;/code&gt; stuff.
So what we can do with that?&lt;/p&gt;
&lt;h2 id=&#34;macro-processor-alternatives&#34;&gt;Macro processor alternatives&lt;/h2&gt;
&lt;p&gt;While we can&amp;rsquo;t really change C, it is still possible to fix some issues outside.
Let&amp;rsquo;s take a look at other macro implementations for C:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://insanecoding.blogspot.com/2011/10/stronger-cc-preprocesser.html&#34; target=&#34;_blank&#34;&gt;PHP&lt;/a&gt; - Use PHP to produce C code.
&lt;strong&gt;I dare you&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/interpreters/perlpp&#34; target=&#34;_blank&#34;&gt;Perl Preprocessor&lt;/a&gt; - use Perl to produce text or generate code.
Not strictly related to C, but can be used in C, as well as in any other language.
While I think that Perl is a fine choice, many people probably will not think alike.
But Perl is excellent for text processing, so I think it may be a good one for C macros.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/eudoxia0/cmacro&#34; target=&#34;_blank&#34;&gt;cmacro&lt;/a&gt; - macro processor for C written in Common Lisp.
This is the most interesting discovery so far.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.gnu.org/software/m4/&#34; target=&#34;_blank&#34;&gt;m4&lt;/a&gt; - GNU M4 macro processor.
It should improve the current state of the art in C macros, but I&amp;rsquo;ve never seen someone using it.&lt;/li&gt;
&lt;li&gt;Probably tons of other projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cmacro got my attention.
It seems to provide good AST manipulation primitives for C code and seems like a complete solution.
It also comes with a set of usable macros in a separate &lt;a href=&#34;https://github.com/eudoxia0/magma&#34; target=&#34;_blank&#34;&gt;package&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem is, that if you&amp;rsquo;re going to use any of the solutions listed above, you will add additional development dependency for your project.
This is not a huge issue but may cause problems or inconvenience for other developers that decided to contribute to your project.
That&amp;rsquo;s why built-in support for a good macro system is needed.&lt;/p&gt;
&lt;p&gt;While macro processors can be changed and integrated on a per-project basis, it&amp;rsquo;s not that easy to change syntax.
Yes, good macro preprocessors can extend syntax, as Cmacro does, but this will not solve all problems of C syntax, such as pointers to functions, that return pointer to function and take several pointers to functions, that take some arguments, as arguments.
Just try to write this, and any Lisp code will look like candy for you.
Look here&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt; if you don&amp;rsquo;t want to waste your time, as I&amp;rsquo;ve wasted mine already.&lt;/p&gt;
&lt;h2 id=&#34;alternative-syntax&#34;&gt;Alternative syntax?&lt;/h2&gt;
&lt;p&gt;Although I&amp;rsquo;ve listed some different preprocessors that can fix or enhance the macro system in C, who said that we need to solve the problems of C syntax and macro system independently?&lt;/p&gt;
&lt;p&gt;Instead, we can create language, that transpires to valid C.
There are many languages that took this approach, and while I think that most of those change syntax too radically, it still may be good for us.
To name a few:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://gambitscheme.org/wiki/index.php/Main_Page&#34; target=&#34;_blank&#34;&gt;Gambit Scheme&lt;/a&gt; - CGambit is a compiler for Scheme, that generates portable C code.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://nim-lang.org/&#34; target=&#34;_blank&#34;&gt;Nim&lt;/a&gt; - a language with Python-like syntax that can be compiled into C.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://wiki.gnome.org/Projects/Vala&#34; target=&#34;_blank&#34;&gt;Vala&lt;/a&gt; and &lt;a href=&#34;https://wiki.gnome.org/action/show/Projects/Genie&#34; target=&#34;_blank&#34;&gt;Genie&lt;/a&gt; - GNOME projects aimed toward C with improved syntax and support for classes through GObject.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So C as a host language is not a new idea, but which of these languages expand macro abilities, while still staying close to C syntax, just improving it in different areas?
None.
Vala is more like C++, but the macro system is not enhanced compared to C.
Genie and Nim are Python-like, though Nim got pretty good macro support because you actually can change AST.
Gambit also greatly enhances the macro system, because it is a Scheme.&lt;/p&gt;
&lt;p&gt;However all these languages, except, maybe, Vala, differ from C dramatically, so this may not be a good choice for everyone.
I&amp;rsquo;m sure that there may be a language that suits this better, but I&amp;rsquo;m unaware of it as of today.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s one more language though, that catches my eye as a C developer, and it is &lt;a href=&#34;https://github.com/zetzit/zz&#34; target=&#34;_blank&#34;&gt;ZZ&lt;/a&gt;.
It features Rust-like syntax, which is good for me, and procedural macros.
Though it&amp;rsquo;s not compiled down to C, it uses C ABI, so this may actually be a good point of transition of your codebase.
The language is quite young, and I did not use it personally, so I can&amp;rsquo;t really recommend it.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;To summarize my thoughts through this rant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C is an old language, its syntax is ugly, and it doesn&amp;rsquo;t have to be this way,&lt;/li&gt;
&lt;li&gt;Macro system in C is very lacking, and it doesn&amp;rsquo;t have to be this way either,&lt;/li&gt;
&lt;li&gt;There are solutions to the problem, but neither of those can fix everything without caveats.&lt;/li&gt;
&lt;li&gt;New languages, such as Rust and ZZ, are trying to fix problems of C, but the amount of legacy code written in C will not make the adoption easy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So in my opinion the best solution to all of this is for C developers to provide a new standard with improved syntax and macro system, and keep the name of the language, so new code could be written in it, while still keeping old code working by using compiler flag that specifies standard, &lt;em&gt;obviously&lt;/em&gt;.
While this is not optimal, and a lot of work, I think the future of C depends on this.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;C++ in my opinion has the worst syntax because it has so many of it for a single thing.
Like there&amp;rsquo;s too much different initializer syntax, two ways of specifying return types, a lot of implicit things that are syntactic sugar, and so on.
When I was trying to get into C++ it was a mess, like where do I use this initialization, and where do I use that?
For professional C++ developers that might be a good thing, but do we really need this kind of thing at all?
This is something like a balance between lines of code and amount of different syntax for the same thing, and I would prefer more lines of code wrapped into a macro that is used for this particular task I&amp;rsquo;m working on, and not several different syntax variants that are available all the time.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/a/1872506&#34; target=&#34;_blank&#34;&gt;This SO answer&lt;/a&gt; describes how to iterate over variadic arguments in C macro.
General idea is that you define a finite amount of reduction macros, and call one depending on the overall amount of parameters that were passed initially.
E.g. you define &lt;code&gt;helper1&lt;/code&gt;, &lt;code&gt;helper2&lt;/code&gt;, &lt;code&gt;helperN&lt;/code&gt;, and when you call your macro with &lt;code&gt;5&lt;/code&gt; arguments it will select &lt;code&gt;helper5&lt;/code&gt; and call it, &lt;code&gt;helper5&lt;/code&gt; then will call &lt;code&gt;helper4&lt;/code&gt;, and so on until it reaches the last one.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;&lt;code&gt;void(*f0(void(*f1)(void(*f2(void(*f3)(void)))(void))))(void(*f4(void(*f5)(void)))(void)) {return f1;}&lt;/code&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class=&#34;comment-link&#34;&gt; &lt;a href=&#34;mailto:%61%6e%64%72%65%79%6f%72%73%74%2b%62%6c%6f%67%40%67%6d%61%69%6c%2e%63%6f%6d?subject=Comment: C Needs Better Syntax and Macros&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Comment via email&lt;/a&gt;&lt;/div&gt;</description>
      <pubDate>Mon, 06 Apr 2020 15:42:00 +0300</pubDate>
    </item>
  </channel>
</rss>
