piegames.de Zola 2025-12-23T00:00:00+00:00 https://piegames.de/atom.xml Beyond consent 2025-12-23T00:00:00+00:00 2025-12-23T00:00:00+00:00 piegames https://piegames.de/dumps/beyond-consent/ <p><strong>Content Note: Explicit mention of various hard kinks and discussion of non-consensual activities, including rape</strong></p> <p><img src="https://piegames.de/dumps/beyond-consent/./crying-is-not-an-emergency.jpg" alt="Photo of a rollercoaster with a big sign: &quot;THIS RIDE ONLY STOPS IN AN EMERGENCY. CRYING IS NOT AN EMERGENCY&quot;" title="Roller coasters are an example of meta-consensual situations outside of kink: When entering the ride, you consent that it won&#39;t stop for you regardless of how you feel about it." /></p> <!-- TOC --> <h2 id="the-basics-fries"><a class="zola-anchor" href="#the-basics-fries" aria-label="Anchor link for: the-basics-fries">The basics: FRIES</a></h2> <p>Let's first look at some simple and straightforward consent models. Most people have probably heard of "no means no", or, preferable in most cases, "yes means yes". But those quickly fail to capture the complex depth of what it means to consent to something.</p> <p>A more thorough tool to ensure proper consent is called <em>FRIES</em>. It's an acronym: <strong>F</strong>ree, <strong>R</strong>evocable, <strong>I</strong>nformed, <strong>E</strong>nthusiastic, <strong>S</strong>pecific. Even if you haven't heard of FRIES before, its definition should hopefully be intuitively clear and easily be mapped onto common practice. If all of this is complete news to you, I'd suggest sticking to this for now.</p> <p><a href="https://text.lilahexe.top/fries">More on FRIES in this excellent writeup by LilaHexe</a></p> <h2 id="cnc-consensual-non-consent"><a class="zola-anchor" href="#cnc-consensual-non-consent" aria-label="Anchor link for: cnc-consensual-non-consent">CNC: Consensual non-consent</a></h2> <p>CNC is an umbrella term for various practices in which consent is still present, but not always as clear cut as in the above. However beware, people may sometimes say CNC to refer to a specific practice, and moreover they may be having wildly different and incompatible definitions about it. In order to explain these differences, understanding the concept of safewords is essential.</p> <p>At its most basic, as safeword is a word or phrase which when spoken will pause or end a scene. Common safewords include Pause/Stop, Green/Yellow/Red (Red means Stop, Green and Yellow can mean different things so always make sure to clarify which one is what), and Mayday (commonly used by kink events to call for external help). Safewords can also be uncommon words with no associated meaning, like "Penguin". Having nonverbal safewords is strongly recommended, for example snapping fingers or "tapping out".</p> <p>For some people, CNC means the following:</p> <blockquote> <p>CNC is play that <strong>requires the usage of safewords</strong>. In CNC, "no" does not necessarily mean "no" anymore, it can be ignored, but a safeword cannot. This contrasts CNC to fully consensual play where no dedicated safewords are necessary, because every word can keep its usual literal meaning. Examples include scenes that involve begging for mercy or rape play. Fundamentally, CNC is about role-playing non-consensual scenes.</p> </blockquote> <p>However for other people, CNC means this:</p> <blockquote> <p>CNC is play with <strong>limited or no safeword options</strong>. Consent is mainly or fully pre-negotiated, which contrasts to fully consensual play where consent is always revocable at any point in time. Examples include "five more hits after the safeword" or hard kinks like torture. Fundamentally, CNC is consensually pre-committing to the enactment of possibly non-consensual elements within a scene.</p> </blockquote> <p>See the difference? See the <em>big, glaring issue</em> that may arise when people miscommunicate what they specifically mean?</p> <p>People who use the second definition argue that the first definition is not CNC, it's just common role play. People who use the first definition argue that the second one is not sufficiently consensual and should not be enacted or endorsed.<sup class="footnote-reference"><a href="#1">1</a></sup> Some people argue that both definitions fit onto the spectrum of "CNC" as an umbrella term. In my experience, when prompted of a definition, people will say the first definition, but when they talk about actual "CNC" activities then often times those will include some aspects of the second definition instead.</p> <p>I argue that at this point, the term "CNC" is semantically burnt and should be avoided in favor of more specific communication. "Meta-consent" is already on the rise as the new umbrella term for <em>all</em> kinds of play where consent is not as straightforward as with FRIES. Additionally, I propose the term "Pseudo non-consent (PNC)" to talk about consensual roleplay of non-consent (our first definition here).</p> <h2 id="meta-consent"><a class="zola-anchor" href="#meta-consent" aria-label="Anchor link for: meta-consent">Meta-consent</a></h2> <p>Meta-consent is the preferrable umbrella term for all the kinds of activities where consent is still present, but not always clear cut. The term emphasizes that that at any time, there still is <em>some</em> form of consent on <em>some</em> level, and it avoids the semantic ambiguities of "CNC" (as well as the emotional baggage, as CNC is quite stigma affected).</p> <p>I like to categorize meta-consent by the aspects of FRIES that are missing in the negotiation. We've already talked in detail about <em>revocability</em> of consent during the scene as part (or not) of CNC, and that is the most common form of meta-consent given. If instead, the consent is unspecific and/or uninformed, this leads to <em>blanket consent</em> instead (see below). Consent may be given "unfreely", for example under the influence of drugs or manipulation.</p> <p>All of these can be necessary for certain kinds of play, but should not be goals on their own. For example you should never accept the consent of someone on drugs, unless that aspect specifically is an <em>integral</em> part of a scene, <em>and</em> the other person (meta-)consented to this happening in advance.</p> <h2 id="pnc-pseudo-non-consent"><a class="zola-anchor" href="#pnc-pseudo-non-consent" aria-label="Anchor link for: pnc-pseudo-non-consent">PNC: Pseudo non-consent</a></h2> <p>I'm coining this term to give the first definition of CNC a dedicated name: Pseudo non-consent defines all kinds of play that desires to mimic non-consensual situations. This typically requires dedicated safewords to allow interrupting the play where necessary. The name emphasizes that this is about <em>pretense</em> of non-consent. Elements of the play may require meta-consent, but this needs additional communication.</p> <h2 id="blanket-consent"><a class="zola-anchor" href="#blanket-consent" aria-label="Anchor link for: blanket-consent">Blanket consent</a></h2> <p>Blanket consent is the meta-consent given in advance for broad, unspecified kinds of play or under partial lack of information. This can be as ordinary as the consent to bite each other at any time (to the point that some people forget this does require consent!<sup class="footnote-reference"><a href="#2">2</a></sup>) or as exciting as agreeing to a surprise scene.</p> <p>In most situations, consent is not a commodity: It is not something you "obtain" and then "keep" and "use"; instead it is a property of any given situation and has no value in any similar future situation. Blanket consent as a concept is important precisely because it is the exception from this rule.</p> <h3 id="treat-success-as-consent"><a class="zola-anchor" href="#treat-success-as-consent" aria-label="Anchor link for: treat-success-as-consent">"Treat success as consent"</a></h3> <p>This specific form of blanket consent is worth explicitly mentioning, because most consent models fall short for various "mental" kinks like corruption, training, manipulation, hypnosis, personality erasure etc. In these kinks, the dom may initiate some action with the goal to (semi-)permanently alter the sub's mind in a certain way. Given that these processes typically happen on a deep, unconscious level and may affect the sub's desires, it quickly becomes impossible to properly ensure that any consent was given fully freely. (Of course, these questions of "free will" and desire in the presence of e.g. trauma are always of relevance for kink, but nowhere are they as prominent and unavoidable as here.)</p> <p>"Treat success as consent" sidesteps these issues by transforming the question of consent into a meta-level tug-of-war game: "You may try to clicker train me in whatever way you want, but treat success as consent." If it succeeds, we assume that the sub consented to the mind alteration. If it fails, this could either be because of a lack of skill in the dom's execution, or because of a lack of consent. The ambiguity here is unavoidable: When playing on such a deep, raw level of the mind, consent (or the lack thereof) may be decided on an unconscious level. Importantly, in that "failure" scenario nobody is to blame. The dom may thus try again with more skill or a different approach (unless the consent is revoked in the meantime).</p> <p>It is important to understand that this approach inherently assumes that the subconscious tends to be more averse to change which is not desired. In reality, this is of course not as clear-cut, and some people may be more suggestible of being manipulated into things they don't actually want than others. Be aware of these risks before getting involved into play with such profound consequences.</p> <h2 id="closing-words"><a class="zola-anchor" href="#closing-words" aria-label="Anchor link for: closing-words">Closing words</a></h2> <p>When planning a scene or initiating a dynamic or a scene, it is important to always look for ways to have the fullest consent possible. Every meta layer carries the risk for things to go wrong, and should be avoided if possible. Always ask questions like: Do I <em>really</em> need uninformed consent? Or is there a way to fulfill that kink with full information? A lot of the time, kinky activities can be made a lot safer with only minor adjustments that don't impact the experience, and that's what we always should be striving for.</p> <h2 id="further-reading"><a class="zola-anchor" href="#further-reading" aria-label="Anchor link for: further-reading">Further reading</a></h2> <ul> <li><a href="https://text.lilahexe.top/fries">FRIES</a></li> <li><a href="https://everydayfeminism.com/2016/07/metaphor-for-consent/">This Metaphor for Consent Might Be Just the Thing You Need to Make It Click</a></li> </ul> <hr /> <p><strong>Footnotes:</strong></p> <div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> <p>It is easy to point at some foreign kink and say "this is too unsafe, this shouldn't exist". But especially for those high-risk activities, it is important to provide people with alternative safety and consent tools for practicing harm reduction. I'm here to help, not to judge.</p> </div> <div class="footnote-definition" id="2"><sup class="footnote-definition-label">2</sup> <p>There is something to be said here about non-verbal or even implicit expressions of consent, but that is a topic for another time.</p> </div> <p><em>Thank you to Nia and leela for helping me shape this text into its current form. Thanks to LilaHexe for writing about FRIES this well so that I did't have to, and thanks to Orange for being a (sadly rare) role model on healthy hard kink.</em></p> <!-- NOTES: https://thicc.horse/@orange/114044382902226264 https://flausch.social/@piegames/111619857207733481 https://flausch.social/@piegames/114046454152045163 https://flausch.social/@piegames/114853067921986975 --> Relationship PVP 2025-10-16T00:00:00+00:00 2025-10-16T00:00:00+00:00 piegames https://piegames.de/dumps/relationship-pvp/ <p>Most people treat their relationships like a co-op game. However, that is only one way—and admittedly a fairly boring one at that—to approach that topic. For those who crave more intense emotions, the game of relationship also offers a host of more competitive modes.</p> <p>This is a tutorial for how to win at any adversarial relationship, against partners and metas alike. Fun for the entire polycule!</p> <!-- TOC --> <h2 id="the-core-game-loop-martyrship-and-victimization-olympics"><a class="zola-anchor" href="#the-core-game-loop-martyrship-and-victimization-olympics" aria-label="Anchor link for: the-core-game-loop-martyrship-and-victimization-olympics">The core game loop: Martyrship and victimization olympics</a></h2> <p>You are <em>good</em>, actually, and your partner is <em>flawed</em>. Your love for this seemingly-unlovable person and how much you are self-sacrificing just to be with them is proof of your true love. Unlike in co-op mode, where the goal is to get everyone's needs met as best as possible, in PVP mode the goal is to be as loud as possible about not having your needs met. Optics is key here: Everyone needs to know about your suffering, and how good you actually are, and about all the mistakes your partners are all making.</p> <p>It is of utmost importance to use any opportunity where you have been wronged. <em>Demand</em> apologies. If multiple people were wronged, make it very clear how you have been hurt the most.</p> <p>Remember: Every time you succumb to a partner's need, remind them of the sacrifice you are doing <em>for them</em>. They got what they wanted, and they should feel bad about having needs and inconvening you with them. If this happens repeatedly, congrats, you may then play the <em>people pleaser</em> card. You are now a proud victim of abuse :)</p> <h2 id="how-to-win-at-emotions"><a class="zola-anchor" href="#how-to-win-at-emotions" aria-label="Anchor link for: how-to-win-at-emotions">How to win at emotions</a></h2> <p>The primary goal here is to minimize your own discomfort caused by negative emotions. Emotions are kinda fuzzy, but facts can be <em>true</em> or <em>false</em>, and thus all emotions always need to be grounded in facts so that they may be objectively argued about. When telling others your own emotions, make sure to always justify why you are right to feel this way. When others are causing you discomfort with their negative emotions, tell them they are wrong for doing that. Use <em>facts</em> to argue with them why their emotional response to the situation is inappropriate or overblown or wrong. Why are they making such a big fuss over nothing in the first place; some people simply are way too emotional.</p> <h2 id="indirect-communication-tactics"><a class="zola-anchor" href="#indirect-communication-tactics" aria-label="Anchor link for: indirect-communication-tactics">Indirect communication tactics</a></h2> <p>Direct communication with your partners makes you vulnerable to their counter-arguments. Therefore, the safest play is to talk <em>about</em> them instead of <em>with</em> them. An easy win for example is to whine about all your partners' flaws and wrongdoings at length with other people. A more nuanced strategy is to take the game out on social media, with snarky sub-posts and nonmentions. Other people's failings make for great shitposts, but make sure to be ahead of the current discourse or someone else will one-up you before you can one-up them. When others do the same, complain about all the petty drama they're causing.</p> <h2 id="the-art-of-weaponized-non-apology"><a class="zola-anchor" href="#the-art-of-weaponized-non-apology" aria-label="Anchor link for: the-art-of-weaponized-non-apology">The art of weaponized non-apology</a></h2> <p>Sometimes, you may make a mistake. This is problematic because your partner will immediately counter by being hurt and making you look like a bad person. Fortunately, there is a way to parry: You simply need to apologize. Now, the mistake is undone and you are a good person again. Therefore, if your partner remains upset even after your apology, they are clearly the problem here and you are in your full right to be angry at them about that.</p> <p>This works both with genuine apologies as well as with non-apologies. However beware that genuine apologies make you more vulnerable: Your <del>enemy</del> partner may choose to accept the apology, which will void your parry and lead to a draw.</p> <p>In the end, always remember: Apologies are always about who is <em>right</em> and who is <em>wrong</em>. A genuine apology is akin to admitting defeat, but a good weaponized non-apology can turn the cards in your favor again. Master this skill and you will be above fault.</p> <h2 id="how-to-exit-pvp-mode-again"><a class="zola-anchor" href="#how-to-exit-pvp-mode-again" aria-label="Anchor link for: how-to-exit-pvp-mode-again">How to exit PVP mode again</a></h2> <p>PVP mode can be exited by switching over to a co-op game, or by leaving multiplayer altogether. A crucial observation is that co-op mode can only be entered, well, cooperatively. Trying to cooperate with people who are interested in winning alone is a strategy for self-destruction (Yes this also is a metaphor about our democracies).</p> <p>Unfortunately, there is a complicating factor to leaving PVP mode: Because many highly ranked competitive teams have been carefully selected over years for the purpose of fighting each other, there is no guarantee that their members may be suited and compatible for cooperative play. In such cases, the best strategy is to disband and exit the current relationship game in order to join a different team later on.</p> <hr /> <p>P.S.: I know that <em>someone</em> will end up reading all of this and try to weaponize it against others à la "you're playing in PVP mode and I don't so you're bad and I'm good", and <em>fuck you</em> in advance for that. You've already lost, <em>game over</em>.</p> Globally applying overlays onto Nixpkgs 2025-04-28T00:00:00+00:00 2025-04-28T00:00:00+00:00 piegames https://piegames.de/dumps/nixpkgs-global-overlays/ <p>These days, it is fashionable to have the Nixpkgs for your NixOS system managed alongside its configuration, pinned by some tool of choice. In <a href="https://piegames.de/dumps/pinning-nixos-with-npins-revisited/">Pinning NixOS with npins, this time without flakes for real</a>, I've talked about how I want the same Nixpkgs to be used for both my system configuration as well as all my ad-hoc <code>nix-shell</code>s. However, there is a key distinction between any <code>import &lt;nixpkgs&gt; {}</code> used in the wild and the <code>pkgs</code> provided within the NixOS module system: Only the latter has its overlays applied.</p> <p>I'm a heavy user of overlays, and this discrepancy has bitten me more than once. For example, if I pin a certain version of a tool used for a service, and then do <code>nix-shell -p that-tool</code> to do some maintenance on the service, I suddenly have a version mismatch.</p> <p>The reason for this dilemma is that <code>NIX_PATH</code> contains, well, a <em>path</em>, and that overlays are <em>configuration</em> for a Nixpkgs <em>instance</em>: <code>import &lt;nixpkgs&gt; { overlays = …; }</code>. Evaluating <code>pkgs.path</code> will yield the original path from <code>&lt;nixpkgs&gt;</code>, with any applied configuration erased.</p> <!-- TOC --> <h2 id="spray-n-pray-using-global-overlays"><a class="zola-anchor" href="#spray-n-pray-using-global-overlays" aria-label="Anchor link for: spray-n-pray-using-global-overlays">Spray'n'pray: Using global overlays</a></h2> <p>Nixpkgs has a slightly niche feature: It can automatically load overlays impurely from the environment at instantiation time. This is implemented in <code>&lt;nixpkgs&gt;/pkgs/top-level/impure.nix</code>:</p> <pre data-linenos data-lang="nix" data-name="pkgs/top-level/impure.nix" class="language-nix z-code"><code class="language-nix" data-lang="nix" data-name="pkgs/top-level/impure.nix"><table><tbody><tr><td>39</td><td><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">overlays</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-invalid z-illegal z-reserved z-nix">let</span> </span></td></tr><tr><td>40</td><td><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">isDir</span> <span class="z-invalid z-illegal">=</span> <span class="z-variable z-parameter z-function z-4 z-nix">path</span><span class="z-punctuation z-definition z-function z-nix">:</span> <span class="z-constant z-language z-nix">builtins</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">pathExists</span> <span class="z-punctuation z-definition z-expression z-nix">(</span><span class="z-variable z-parameter z-name z-nix">path</span> <span class="z-keyword z-operator z-nix">+</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>/.<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-definition z-expression z-nix">)</span><span class="z-invalid z-illegal">;</span> </span></td></tr><tr><td>41</td><td><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">pathOverlays</span> <span class="z-invalid z-illegal">=</span> <span class="z-variable z-parameter z-name z-nix">try</span> <span class="z-punctuation z-definition z-expression z-nix">(</span><span class="z-support z-function z-nix">toString</span> <span class="z-string z-unquoted z-spath z-nix">&lt;nixpkgs-overlays&gt;</span><span class="z-punctuation z-definition z-expression z-nix">)</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span><span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-invalid z-illegal">;</span> </span></td></tr><tr><td>42</td><td><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">homeOverlaysFile</span> <span class="z-invalid z-illegal">=</span> <span class="z-variable z-parameter z-name z-nix">homeDir</span> <span class="z-keyword z-operator z-nix">+</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>/.config/nixpkgs/overlays.nix<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-invalid z-illegal">;</span> </span></td></tr><tr><td>43</td><td><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">homeOverlaysDir</span> <span class="z-invalid z-illegal">=</span> <span class="z-variable z-parameter z-name z-nix">homeDir</span> <span class="z-keyword z-operator z-nix">+</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>/.config/nixpkgs/overlays<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-invalid z-illegal">;</span> </span></td></tr></tbody></table></code></pre> <p>as we can see here, it will source and automatically apply overlays from the following locations:</p> <ul> <li><code>&lt;nixpkgs-overlays&gt;</code>, set by putting <code>nixpkgs-overlays=/path/to/my-overlay.nix</code> into <code>NIX_PATH</code>,</li> <li><code>~/.config/nixpkgs/overlays.nix</code>,</li> <li>and all files in <code>~/.config/nixpkgs/overlays/*</code> (n.b.: hard-coding <code>~/.config</code> is <em>not</em> XDG basedir spec compliant!).</li> </ul> <p>This means that if any of these provide any overlays, then any call to <code>import &lt;nixpkgs&gt; {}</code> will behave as if <code>overlays = [ … ]</code> had been passed to it. Equipped with this, we can simply have:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">channel</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">enable</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-constant z-language z-nix">false</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">nix</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">nixPath</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>nixpkgs-overlays=/etc/nixos/nixpkgs-overlays<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">environment</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">etc</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>nixos/nixpkgs-overlays<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">source</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">put</span> <span class="z-variable z-parameter z-name z-nix">path</span> <span class="z-variable z-parameter z-name z-nix">to</span> <span class="z-variable z-parameter z-name z-nix">overlays</span> <span class="z-variable z-parameter z-name z-nix">here</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <p>I decided against putting my overlay into <code>~/.config/nixpkgs/overlays.nix</code>, because I wanted a truly global solution; I'm afraid things could break pre-login when <code>/home</code> is still encrypted. If Nixpkgs implemented the full XDG basedir specification, then we'd be able to simply put our overlay somewhere into <code>/etc/xdg</code> and wouldn't have to bother with also setting <code>NIX_PATH</code>.</p> <p>With this solution, the overlay will be applied to <em>all</em> Nixpkgs instances, not only those of our system's Nixpkgs as found via <code>import &lt;nixpkgs&gt;</code> or behind the scenes in <code>nix-shell -p</code>. Notably, this will also apply to any pinned dev shell à la <code>import sources.pkgs {}</code>, no matter how old or new it is. Impurely meddling with dev environments is a recipe for confusing error messages, and likely to fail: Overlays are typically written to work against a specific Nixpkgs version, and trying to apply it to some very old pinned Nixpkgs pin will likely throw on a missing attribute.</p> <p>This can be a desired outcome, and can work with an overlay carefully designed for such an environment. However, in many other cases it is desirable to only override Nixpkgs instances that aren't explicitly pinned by their own tooling.</p> <h2 id="a-more-selective-approach-applypatches"><a class="zola-anchor" href="#a-more-selective-approach-applypatches" aria-label="Anchor link for: a-more-selective-approach-applypatches">A more selective approach: <code>applyPatches</code></a></h2> <p>So, <code>NIX_PATH</code> wants a path. Let's give it one. The plan is to patch our system's Nixpkgs so that it will automatically apply a specific overlay when imported. Other Nixpkgs instances, pinned from other sources, will be unaffected. We can simply inject ourselves into Nixpkgs's <code>default.nix</code> entry point, like this:</p> <pre data-lang="diff" class="language-diff z-code"><code class="language-diff" data-lang="diff"><span class="z-source z-diff"><span class="z-markup z-deleted z-diff"><span class="z-punctuation z-definition z-deleted z-diff">-</span> import ./pkgs/top-level/impure.nix </span></span><span class="z-source z-diff"><span class="z-markup z-inserted z-diff"><span class="z-punctuation z-definition z-inserted z-diff">+</span> args: (import ./pkgs/top-level/impure.nix) (args // { overlays = [ @PUT_OVERLAY_HERE@ ] ++ args.overlays or []; }) </span></span></code></pre> <p>Applying the patch is slightly involved but straightforward. We need an unpatched Nixpkgs instance for bootstrapping, for accessing <code>applyPatches</code> and other library functions.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">pkgsPath</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">pkgsBootstrap</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">applyPatches</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">src</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">sources</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">pkgs</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> <span class="z-comment z-line z-number-sign z-nix"># Nixpkgs pin here</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">patches</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-expression z-nix">(</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># Replace `${./overlay.nix}` with the correct path in there</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">pkgsBootstrap</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">writeText</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>overlays.patch<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-string z-quoted z-other z-nix"><span class="z-punctuation z-definition z-string z-other z-start z-nix">&#39;&#39;</span> </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> diff --git a/default.nix b/default.nix </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> index bdab048245e2..617d15b6bfd5 100644 </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> --- a/default.nix </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> +++ b/default.nix </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> @@ -27,4 +27,4 @@ if !builtins ? nixVersion || builtins.compareVersions requiredVersion builtins.n </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> <span class="z-markup z-italic"><span class="z-punctuation z-section z-embedded z-begin z-nix">${</span><span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span> <span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-section z-embedded z-end z-nix">}</span></span>else </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> - import ./pkgs/top-level/impure.nix </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> + args: (import ./pkgs/top-level/impure.nix) (args // { overlays = [ <span class="z-markup z-italic"><span class="z-punctuation z-section z-embedded z-begin z-nix">${</span><span class="z-string z-unquoted z-path z-nix">./overlay.nix</span><span class="z-punctuation z-section z-embedded z-end z-nix">}</span></span> ] ++ args.overlays or []; }) </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> <span class="z-punctuation z-definition z-string z-other z-end z-nix">&#39;&#39;</span></span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-expression z-nix">)</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <blockquote> <p><em><strong>Side note:</strong> This is why one does not put patches inline in Nix code. All lines of code that start with a space will be fucked up by the editor any time the indentation changes, moreover the <code>patches</code> list wants paths anyways so it requires wrapping in <code>writeText</code>. The <code>${" "}</code> in the string is a hack to work around the editor eating that one whitespace. Putting the patch in a file with <code>@SUBSTITUTIONS@</code> for the overlay path is the way to go here.</em></p> </blockquote> <p>This solution still has a kink that needs to be ironed out: As it is, the provided <code>overlay.nix</code> would have to be self-contained, i.e. not import any other Nix code. Often times, this is not the case: A system configuration's overlay may import packages from npins or locally vendored packages. One solution is to put the entire configuration surrounding our overlay into the store (this is what flakes do), but this means that the source path changes any time <em>any</em> bit of configuration is touched, even if it has nothing to do with the overlay. Instead, <a href="https://nixos.org/manual/nixpkgs/unstable/#sec-functions-library-fileset"><code>pkgs.lib.fileset</code></a> offers some functionality to only include the files we care about:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">overlaySource</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">pkgsBootstrap</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">lib</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">fileset</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">toSource</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">root</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-unquoted z-path z-nix">./.</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">fileset</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">pkgsBootstrap</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">lib</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">fileset</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">unions</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-string z-unquoted z-path z-nix">./overlay.nix</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># Add all files/folders that the overlay imports here</span> </span><span class="z-source z-nix"> <span class="z-string z-unquoted z-path z-nix">./npins</span> </span><span class="z-source z-nix"> <span class="z-string z-unquoted z-path z-nix">./pkgs</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <p>In the patch, we then simply replace <code>${overlay.nix}</code> with <code>${overlaySource}/overlay.nix</code>.</p> <h2 id="conclusion"><a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">Conclusion</a></h2> <p>If you have an overlay that you want to apply to <em>all</em> Nixpkgs instances, use global overlays. If you have an overlay that you want to <em>globally</em> apply to a <em>specific</em> Nixpkgs instance, apply the overlay as a patch instead.</p> <p>I'm honestly unsure if I'd recommend this approach to anybody else. It works for me and solves this very specific issue I have, though I can imagine most people don't care much about it.</p> Pinning NixOS with npins, this time without flakes for real 2025-04-27T00:00:00+00:00 2025-04-27T00:00:00+00:00 piegames https://piegames.de/dumps/pinning-nixos-with-npins-revisited/ <p>Last year, Jade posted <a href="https://jade.fyi/blog/pinning-nixos-with-npins/">Pinning NixOS with npins, or how to kill channels forever without flakes</a>. In short, what we want is:</p> <ul> <li>Get rid of channels (as in <code>nix-channel</code>, and not any other overloaded meanings of that word) as a source of impurity.</li> <li>Have one blessed Nixpkgs pinned with <code>npins</code> being used for the system's configuration as well as all <code>&lt;nixpkgs&gt;</code> invocations system-wide.</li> <li>The solution should behave like <code>nix-channel</code>: When running <code>npins update</code> <em>–and then deploying the configuration–</em>, the entire system should switch over to using the updated Nixpkgs. <ul> <li>The currently used Nixpkgs being tied to the current configuration is the key feature here. This means that they always stay in sync, even across rollbacks—unlike <code>nix-channel</code>.</li> </ul> </li> <li>The NixOS configuration should be built with the currently pinned Nixpkgs, and <em>not</em> the current system's Nixpkgs. (Otherwise deployments would take two iterations to converge.)</li> </ul> <p>A naive solution might simply export <code>NIX_PATH=nixpkgs=${pkgs.path}</code> in the system configuration. This, however, comes with a show-stopping drawback: Due to the way environment variables propagate, any updates to it would not propagate until the next relog or reboot. The common solution to this is, as usual in this industry, adding a layer of indirection.</p> <p>For that, Jade's post cleverly uses flake infrastructure: By writing an entry that points the <code>nixpkgs</code> "flake" to the local store path of our nixpkgs in the Flake registry at <code>/etc/nix/registry.json</code>, and then simply setting <code>NIX_PATH=nixpkgs=flake:nixpkgs</code>. This works, but it comes with downsides. First of all, it requires the <code>nix-command</code> and <code>flakes</code> experimental features, and the goal of the exercise is to avoid flakes completely. But more importantly, the flake registry suffers from some of the same issues that plagued us with <code>nix-channel</code> in the first time (albeit they are not surfacing in this usage scenario), and therefore the flake registry is <a href="https://github.com/DeterminateSystems/nix-src/issues/37">on its way out</a> as well.</p> <p>There is a much simpler solution to using the flake registry: A plain old symlink.</p> <h2 id="it-s-only-five-lines"><a class="zola-anchor" href="#it-s-only-five-lines" aria-label="Anchor link for: it-s-only-five-lines">It's only five lines</a></h2> <p>We define that <code>/etc/nixos/nixpkgs</code> now is a symlink to our current Nixpkgs (any other path will do, but I liked this one) and simply hardcode that path in <code>NIX_PATH</code> forever.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">nix</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">channel</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">enable</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-constant z-language z-nix">false</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">nix</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">nixPath</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-punctuation z-definition z-list z-nix">[</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>nixpkgs=/etc/nixos/nixpkgs<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">environment</span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">etc</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>nixos/nixpkgs<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span>.<span class="z-entity z-other z-attribute-name z-multipart z-nix">source</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-constant z-language z-nix">builtins</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">storePath</span> <span class="z-variable z-parameter z-name z-nix">pkgs</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">path</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <p>Now, with this setup all ad-hoc shells should already work, however when rebuilding your system configuration after bumping the pins it would still take the current Nixpkgs from <code>/etc/nixos/nixpkgs</code> instead of the freshly bumped one. This means that any updates would take two rebuilds to fully propagate. (Note: This mainly applies to <code>nixos-rebuild</code>, YMMV when using different deployment tools. Some of them allow doing the right thing and using a pinned Nixpkgs instead of <code>&lt;nixpkgs&gt;</code> out of the box.)</p> <p>To fix this, we must override the Nix path when rebuilding. <a href="https://direnv.net/">direnv</a> and a Nix shell to our rescue:</p> <pre data-lang="sh" data-name=".envrc" class="language-sh z-code"><code class="language-sh" data-lang="sh" data-name=".envrc"><span class="z-source z-shell z-bash"><span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">use</span></span><span class="z-meta z-function-call z-arguments z-shell"> nix</span> </span></code></pre> <pre data-lang="nix" data-name="shell.nix" class="language-nix z-code"><code class="language-nix" data-lang="nix" data-name="shell.nix"><span class="z-source z-nix"><span class="z-keyword z-other z-nix">let</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">pkgs</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-support z-function z-nix">import</span> <span class="z-punctuation z-definition z-expression z-nix">(</span><span class="z-support z-function z-nix">import</span> <span class="z-string z-unquoted z-path z-nix">./npins</span><span class="z-punctuation z-definition z-expression z-nix">)</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">nixpkgs</span> <span class="z-punctuation z-definition z-attrset z-nix">{</span> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-keyword z-other z-nix">in</span> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-name z-nix">pkgs</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">mkShell</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">nativeBuildInputs</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-keyword z-other z-nix">with</span> <span class="z-variable z-parameter z-name z-nix">pkgs</span>; <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># I recommend also pinning all the local deployment tools while we&#39;re at it</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">nix</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">home-manager</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">git</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">npins</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">openssh</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">colmena</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">shellHook</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-other z-nix"><span class="z-punctuation z-definition z-string z-other z-start z-nix">&#39;&#39;</span> </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> export NIX_PATH=&quot;nixpkgs=<span class="z-markup z-italic"><span class="z-punctuation z-section z-embedded z-begin z-nix">${</span><span class="z-constant z-language z-nix">builtins</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">storePath</span> <span class="z-variable z-parameter z-name z-nix">pkgs</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">path</span><span class="z-punctuation z-section z-embedded z-end z-nix">}</span></span>&quot; </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> <span class="z-punctuation z-definition z-string z-other z-end z-nix">&#39;&#39;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <blockquote> <p><em><strong>Sneak peek:</strong> If you prefer a minimalistic setup with only direnv and no <code>shell.nix</code>, in a future release of <code>npins</code> the following will also work:</em></p> <pre data-lang="sh" data-name=".envrc" class="language-sh z-code"><code class="language-sh" data-lang="sh" data-name=".envrc"><span class="z-source z-shell z-bash"><span class="z-meta z-function-call z-shell"><span class="z-storage z-modifier z-shell">export</span> <span class="z-variable z-other z-readwrite z-assignment z-shell">NIX_PATH</span><span class="z-keyword z-operator z-assignment z-shell">=</span><span class="z-string z-unquoted z-shell"><span class="z-string z-quoted z-double z-shell"><span class="z-punctuation z-definition z-string z-begin z-shell">&quot;</span>nixpkgs=<span class="z-meta z-group z-expansion z-command z-parens z-shell"><span class="z-punctuation z-definition z-variable z-shell">$</span><span class="z-punctuation z-section z-parens z-begin z-shell">(</span><span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">npins</span></span><span class="z-meta z-function-call z-arguments z-shell"> get-path nixpkgs</span><span class="z-punctuation z-section z-parens z-end z-shell">)</span></span><span class="z-punctuation z-definition z-string z-end z-shell">&quot;</span></span></span></span> </span></code></pre> </blockquote> <p>Note that the shell env approach requires to always <code>nixos-rebuild</code> from that folder, in order to have the path override activated. This isn't a big deal for me as it has always been part of the workflow for various reasons, but if this itches you an alternative solution is to patch <code>nixos-rebuild</code> itself:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-expression z-nix">(</span><span class="z-variable z-parameter z-function z-4 z-nix">self</span><span class="z-punctuation z-definition z-function z-nix">:</span> <span class="z-variable z-parameter z-function z-4 z-nix">super</span><span class="z-punctuation z-definition z-function z-nix">:</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">nixos-rebuild</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">super</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">nixos-rebuild</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">overrideAttrs</span> <span class="z-punctuation z-definition z-expression z-nix">(</span><span class="z-variable z-parameter z-function z-4 z-nix">old</span><span class="z-punctuation z-definition z-function z-nix">:</span> <span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">nativeBuildInputs</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">old</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">nativeBuildInputs</span> <span class="z-keyword z-operator z-nix">or</span> <span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> <span class="z-keyword z-operator z-nix">++</span> <span class="z-punctuation z-definition z-list z-nix">[</span> <span class="z-variable z-parameter z-name z-nix">self</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">makeWrapper</span> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">postInstall</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-name z-nix">old</span><span class="z-keyword z-operator z-nix">.</span><span class="z-variable z-parameter z-name z-nix">postInstall</span> <span class="z-keyword z-operator z-nix">or</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span><span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-keyword z-operator z-nix">+</span> <span class="z-string z-quoted z-other z-nix"><span class="z-punctuation z-definition z-string z-other z-start z-nix">&#39;&#39;</span> </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> wrapProgram $out/bin/nixos-rebuild --set NIX_PATH nixpkgs=/etc/nixos/nixpkgs:nixos-config=/path/to/my/configuration.nix </span></span><span class="z-source z-nix"><span class="z-string z-quoted z-other z-nix"> <span class="z-punctuation z-definition z-string z-other z-end z-nix">&#39;&#39;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-definition z-expression z-nix">)</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-attrset z-nix">}</span><span class="z-punctuation z-definition z-expression z-nix">)</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <h2 id="conclusion"><a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">Conclusion</a></h2> <p>This approach is not free of gotchas, but neither are flakes or <code>nix-channel</code>. In the future, I'd like to see a replacement for <code>nix-channel</code> that makes such a use case a first-class citizen (maybe leveraging profiles?) instead of having to put a symlink somewhere in <code>/etc</code>. The long-term goal is to have feature parity between flakes and third-party tools.</p> <h2 id="further-reading"><a class="zola-anchor" href="#further-reading" aria-label="Anchor link for: further-reading">Further reading</a></h2> <ul> <li><a href="https://jade.fyi/blog/pinning-nixos-with-npins/">Pinning NixOS with npins, or how to kill channels forever without flakes</a> by Jade</li> <li><a href="https://samuel.dionne-riel.com/blog/2024/05/07/its-not-flakes-vs-channels.html">It's not about “Flakes vs. Channels”</a> by samueldr</li> <li><a href="https://piegames.de/dumps/nixpkgs-global-overlays/">Globally applying overlays onto Nixpkgs</a></li> </ul> A soul in an aquarium 2025-02-21T00:00:00+00:00 2025-02-21T00:00:00+00:00 piegames https://piegames.de/dumps/aquarium/ <div class="fiction"> A soul in an aquarium<br /> The inner world so rich<br /> Is shielded from the outside<br /> By blurry glass so thick<br /> <p>A soul lost in a body<br /> Who wasn't made to fit<br /> It's yearning for a safe space<br /> Some place to call a home<br /></p> <p>A soul on auto pilot<br /> To steer while it's away<br /> "I'm human" says the pilot<br /> And even thinks it's true<br /></p> <p>The soul left its enclosure<br /> So fragile and exposed<br /> It's looking for a soulmate<br /> To share aquariums with<br /></p> </div> Two autonomous units 2024-12-26T00:00:00+00:00 2024-12-26T00:00:00+00:00 piegames https://piegames.de/dumps/two-autonomous-units/ <div class="fiction"> Two autonomous units<br /> With a power supply too weak<br /> One recharging in the other<br /> Taking breaks in alternance<br /> Independent but forever linked<br /> <p>Two autonomous units<br /> Each with some broken gears<br /> Switching as to always complement the other<br /> The fragmented illusion of function<br /> In an eternal synchronous dance<br /></p> <p>One autonomous unit</p> </div> Enablers of abuse in online communities 2024-11-05T00:00:00+00:00 2024-11-05T00:00:00+00:00 piegames https://piegames.de/dumps/enablers-of-abuse/ <p>In a lot of conflicts within online communities I've seen, I've noticed a certain pattern of behaviour emerge. To talk about this, let's first distinguish three groups of people that can commonly be found in conflicts:</p> <ol> <li>The troublemakers. The trolls, the assholes, the abusers, the bigots.</li> <li>The enablers. Friends of the trouble makers, people who silently hold the same beliefs (or less silently but with more eloquent words), people who feel entitled to knowing exactly what happened, or just libertarians who disagree with the entire concept of community moderation in the first place.</li> <li>The exhausted, fed up by the troublemakers. They'll either silently retreat or eventually snap and explode loudly.</li> </ol> <p>Community moderation and codes of conduct generally focus on the first group, which makes sense because superficially they are the people actively causing problems. However, upon closer inspection, the second group is causing a lot more problems than the actual obvious trolls, because they are actively sabotaging any moderation efforts, and causing large collateral damages to the community as a whole.</p> <h2 id="enabling-abuse"><a class="zola-anchor" href="#enabling-abuse" aria-label="Anchor link for: enabling-abuse">Enabling abuse</a></h2> <p>The stereotypical script that plays out goes as follows: After many previous incidents involving a problematic person, one incident eventually triggers a moderation action. In response, several enablers will come to the defense of that person and loudly argue against the moderation action. Several heated discussions inflame, dragging in other people, and sucking all air out of the community space to do the things the community actually is set out to do.</p> <p>Alternatively, if the moderation threshold for action was too high in that situation, it can instead happen that a person exhausted by the overall situation snaps. This usually involves mean words and insults towards the problematic person, and sometimes mental breakdowns in public. In that case too, the enablers will come to the defense of the problematic person, reframing this as an attack and abuse, and thus reversing abuser and victim roles. They will complain about the person who snapped to the moderators, and complain about "unjust" or "unfair" treatment.</p> <p>In many cases, the actual incident triggering some moderation action is minor, yet the context for it usually involves a complex and long history of past events. To bystanders without the full context, such action may seem disproportionate, and enablers will happily weaponize this in their opposition of the decision. They will also ask for more and more justification, in the name of "accountability" or "transparency". But no justification will ever be satisfying, as the goal never was to get clarity on the situation in the first place, but to exert social pressure against undesired moderation actions.</p> <p>An important factor is the dynamic of social status. Most troublemakers don't have a big standing in the community, as they tend to not be constructive and productive people overall, and social dynamics in tech communities usually are meritocratic in nature. (Productive problematic people with high status do exist, but are comparatively rare. Read more about them at <a href="https://intenseminimalism.com/2020/the-impact-of-toxic-influencers-on-communities/">The Impact of Toxic Influencers on Communities</a>.) Enablers however tend to be well-known community members, and they give their voice and clout to defend abusive behavior. This is highly problematic, because it drags conflicts which should have been a minor issue right into the heart of the community, turning them into something much more serious.</p> <p>If such stories repeat too often over a prolonged period of time, it can erode and reshape a community. People fed up with bullshit will tend to (more or less silently) leave, while combative people attracted by the drama tend to join. (The latter always exist, but in a functional community they get bored and leave unless they are actually interested in what the community is doing.) This slowly turns the community into a political debate club, fueled by inner conflicts.</p> <h2 id="abuse-happening-outside-of-community-spaces"><a class="zola-anchor" href="#abuse-happening-outside-of-community-spaces" aria-label="Anchor link for: abuse-happening-outside-of-community-spaces">Abuse happening outside of community spaces</a></h2> <p>By default, if a community member is being an asshole in a different corner of the internet, that is of little concern from a community moderation perspective—Community moderation is scoped to what happens within a community. However, the boundaries of where the community starts and ends are of course very fuzzy, and bad actors can weaponize disagreements about where the exact line should be drawn. The <a href="https://www.contributor-covenant.org"><em>Contributor Covenant Code of Conduct</em></a>, a code of conduct commonly used by tech projects says:</p> <blockquote> <p>This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces.</p> </blockquote> <p>The Covenant ignores one major point: A community member being blatantly sexist on Twitter for example cannot be ignored, even if the respective posts are unambiguously outside of the community boundaries. Not taking action would send the wrong signal: "We are tolerating a known sexist in our space, as long as they're nice while we're looking". Marginalized people will hear such a message loud and clear, and accordingly consider a community which does such as less safe.</p> <p>Such decisions get even harder when they involve interpersonal conflicts, things said in semi-private or said a long time ago. But that is not an excuse to ignore all reports of that kind as "out of scope 🤷" (or even worse, as "we're apolitical") without further consideration.</p> <h2 id="what-to-do-about-it"><a class="zola-anchor" href="#what-to-do-about-it" aria-label="Anchor link for: what-to-do-about-it">What to do about it</a></h2> <p>The first step is to acknowledge this phenomenon and decide to expand the scope of what moderation means. It is important to note that on the long run, this will cause <em>less</em> work and not more, because the actual bad actors will become a lot easier to deal with. So, what does this mean in practice?</p> <ul> <li><strong>Moderation is not equally accountable to everyone.</strong> Accountability is important, but it does not imply that fringe members are entitled to knowing everything that happened in full detail. Of course, there should be a space for discussion about how well a moderation case was handled to happen. However, it is crucial to shield it against bad actors abusing the process for their own purposes.</li> <li><strong>Act before someone snaps.</strong> When insults go flying towards a person that already is on the moderation watchlist, this is a clear indication that moderation action should have happened sooner. Moderation should be proactive and not wait until problems blow up in public.</li> <li><strong>Consider enablers of abuse in the Code of Conduct.</strong> Defending hateful behavior is rarely okay, and this should be reflected in the community rules.</li> <li><strong>Handle reports from outside of the official community spaces.</strong> Doing this has nothing to do with "thought policing", instead it is a necessity to keep the community healthy. At the same time, it is important to acknowledge that different rules apply here, and that every case is unique.</li> </ul> Ghost bus 2024-10-30T00:00:00+00:00 2024-10-30T00:00:00+00:00 piegames https://piegames.de/dumps/ghost-bus/ <div class="fiction"> A ghost bus, slowly wating through the thick traffic. <br /> Blurry lights dance on the fogged windows. The outside world is not real. <br /> I blink in and out of existence—In one moment I'm suddenly here, just to slowly fade out again in the next. <br /> When I'm not here, will someone else take my seat? <p>A ghost alights from the bus. Floating shifts into walking, as I eventually materialize again.</p> </div> Passive suicidality scale 2024-07-24T00:00:00+00:00 2024-07-24T00:00:00+00:00 piegames https://piegames.de/dumps/passive-suicidality-scale/ <p><strong>OBVIOUS CW: suicidality and detailed descriptions thereof</strong></p> <p>People sometimes use <a href="https://emmengard.com/2019/05/07/suicide-scale/">this suicide scale</a> to help them communicate their state of unwellbeing to friends more easily in situations of strong distress. That scale was initially created as a shorthand between friends and has no medical aspirations, as explained in <a href="https://emmengard.com/2020/08/20/the-emmengard-suicide-scale-has-been-going-routinely-viral-and-as-a-result-we-have-had-the-chance-to-talk-with-various-people-about-it-in-the-comments-section/">a follow-up post</a> in more detail.</p> <p>In the spirit of the original scale, I created my own scale as a shorthand with friends, and will share it here in the hope that it will be useful to others as well. It is centered around "passive" suicidality, which is any desire to not exist devoid of any aspirations of actually taking action. It is therefore not meant as a replacement for the original (or any other) scale, but rather complementary.</p> <h2 id="the-passive-suicidality-scale"><a class="zola-anchor" href="#the-passive-suicidality-scale" aria-label="Anchor link for: the-passive-suicidality-scale">The <em>passive suicidality</em> scale</a></h2> <ol> <li> <p>Life is great, and even on bad days I am managing.</p> </li> <li> <p>Life is great on good days, but I'm kinda struggling when I'm down.</p> </li> <li> <p>Even on good days, I feel like my joy of living is being dragged down by some latent negativity. I have to fight to keep the negativity at bay.</p> </li> <li> <p>Life is happening to me, I follow along by lack of other choice, but I'm not thrilled. This "being alive" thing is kind of a lot right now, honestly.</p> </li> <li> <p>I somehow get through the day but don't expect me to do anything beyond that. All my resources are bound by "existing", and even that is a tough ask. I still want to fight, somehow, but I'm very exhausted.</p> </li> <li> <p>"Existing" is too much for me right now. I don't know what to do about it, I don't know how to get through the day. Sometimes I just want to give up. <em>I need help.</em></p> </li> <li> <p>I can't take it right now, I don't want to fight anymore. I'm daydreaming of falling asleep, for a month, a year, or longer. In the hope of a better time, far away in a distant future. Or a different life in a better universe. <em>Please give me a break.</em></p> </li> <li> <p>A better future won't help me right now. In my fantasies, I fall asleep and don't wake up anymore. I have many thoughts about me not wanting to exist anymore, I have stopped fighting them. <em>I am done with being alive.</em></p> </li> <li> <p>This is way too much, I can't take it at all, I just want it stop, I don't care how. All my thoughts share the same tenor: <em>I do not want to be.</em></p> </li> <li> <p>Please kill me already. Put me down. Finish me. <em>I need to die.</em></p> </li> </ol> <h2 id="can-i-use-this"><a class="zola-anchor" href="#can-i-use-this" aria-label="Anchor link for: can-i-use-this">Can I use this?</a></h2> <p>Sure, but will it help you? Maybe consider the following questions to figure that out:</p> <ul> <li>Can you empathize with the descriptions, and get a feeling for the mental states they mean to represent?</li> <li>Can you project your own feelings onto the scale, and feel like it describes your mental states, even though you may experience them differently?</li> <li>Does the scale cover a sufficient range of your different mental states? Do you feel like it applies to you in situations where it ought to help you?</li> </ul> <p>If your answer to any of these questions is no, you are probably better off not using it. Find another one which describes what you need better, or consider creating one on your own, tailored to your own states of wellbeing. (For that matter I also recommend asking these questions about the Emmengard suicide scale, if you have been using it.)</p> <h2 id="see-also"><a class="zola-anchor" href="#see-also" aria-label="Anchor link for: see-also">See also</a></h2> <p><em>feel free to suggest more additions</em></p> <ul> <li><strong><a href="https://web.archive.org/web/20180329055637/https://gpscbc.ca/sites/default/files/uploads/Pain_082.0_Comparative_Pain_Scale_PR.pdf">Comparative Pain Scale</a></strong> for physical pain</li> <li><strong><a href="https://lb-lee.dreamwidth.org/1213382.html">The Psychological Badness Scale</a></strong> for non-depressive psychological pain</li> <li><strong><a href="https://tech.lgbt/@nina_kali_nina/112014343182655077">Subjective Units of Distress</a></strong></li> </ul> <h2 id="addendum-talking-about-passive-suicidality-with-others"><a class="zola-anchor" href="#addendum-talking-about-passive-suicidality-with-others" aria-label="Anchor link for: addendum-talking-about-passive-suicidality-with-others">Addendum: Talking about passive suicidality with others</a></h2> <p>Many people who are not accustomed to the phenomenon—and this unfortunately includes a lot of medical professionals who should know better—completely stop listening once they hear "suicidal". Ill-informed preconceptions and stereotypes about what suicidality is and how it actually feels like will override everything else you say in their head. Especially in the context of medical professionals, this can easily lead to harmful or even dangerous situations.</p> <p>When interacting with untrusted people, it is thus advised to reframe these emotions in terms of <em>emotional pain</em> or <em>subjective distress</em>.</p> <hr /> <p><em>A big thanks to leela and several other proofreaders for helping out with writing this</em></p> Against leading commas 2024-03-06T00:00:00+00:00 2024-03-06T00:00:00+00:00 piegames https://piegames.de/dumps/leading-commas/ <p>I've been one of the main people driving automatic formatting for the Nix language forward. As such, a major point of contention which repeatedly appeared is the one about where to put the commas separating function arguments — before or after each item?</p> <p>This question is not new though, and the style can occasionally be observed in other languages as well, like Haskell for example.</p> <p>In this text I will, mostly using the Nix language as an example, show you that commas or other separators need to be put <em>after</em> the item. I will also go into the few rare exceptions. I approach the topic purely from a code formatting perspective, and try to discuss it as exhaustively as possible. I will thus not go into other topics affected by this, like parsing and code generation for example, or languages where this encodes a semantic difference.</p> <p><strong>TL;DR:</strong></p> <ul> <li>If your language supports trailing delimiters on the last item, using them is the strictly superior solution</li> <li>Therefore, <strong>every language should support them</strong> in all cases where this issue may arise</li> <li>In languages which don't, leading commas are the best possible workaround</li> <li>Having a leading delimiter is fine <em>as long as</em> it is also allowed in front of the first item, for example a YAML list starting each element with <code>-</code>.</li> </ul> <!-- TOC --> <h2 id="the-issue"><a class="zola-anchor" href="#the-issue" aria-label="Anchor link for: the-issue">The issue</a></h2> <p>Let's say you have a list of items separated by a delimiter. I chose commas because this is where you see this issue arise the most often, but this generalizes to any token-separated list of expressions.</p> <p>Let's start with lists as an example, because they are simple and easy: <code>[ 1, 2, 3 ]</code>. Now, let's say the list is getting a bit long and you want to put every item onto its own line:</p> <pre data-lang="haskell" class="language-haskell z-code"><code class="language-haskell" data-lang="haskell"><span class="z-source z-haskell">[ </span><span class="z-source z-haskell"> <span class="z-constant z-numeric z-integer z-decimal z-haskell">1</span><span class="z-punctuation z-separator z-comma z-haskell">,</span> </span><span class="z-source z-haskell"> <span class="z-constant z-numeric z-integer z-decimal z-haskell">2</span><span class="z-punctuation z-separator z-comma z-haskell">,</span> </span><span class="z-source z-haskell"> <span class="z-constant z-numeric z-integer z-decimal z-haskell">3</span> </span><span class="z-source z-haskell">] </span></code></pre> <p>One thing which is common to do, is to append an item at the end:</p> <pre data-lang="diff" class="language-diff z-code"><code class="language-diff" data-lang="diff"><span class="z-source z-diff"> [ </span><span class="z-source z-diff"> 1, </span><span class="z-source z-diff"> 2, </span><span class="z-source z-diff"><span class="z-markup z-deleted z-diff"><span class="z-punctuation z-definition z-deleted z-diff">-</span> 3 </span></span><span class="z-source z-diff"><span class="z-markup z-inserted z-diff"><span class="z-punctuation z-definition z-inserted z-diff">+</span> 3, </span></span><span class="z-source z-diff"><span class="z-markup z-inserted z-diff"><span class="z-punctuation z-definition z-inserted z-diff">+</span> 4 </span></span><span class="z-source z-diff"> ] </span></code></pre> <p>As you can see, to do this, one has to touch the line of the previous last element, despite nothing changing on it.</p> <p>Now, if you have a language which allows a trailing comma on the last element, like Rust or Go, you can easily fix this:</p> <pre data-lang="rust" class="language-rust z-code"><code class="language-rust" data-lang="rust"><span class="z-source z-rust"><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-begin z-rust">[</span> </span></span><span class="z-source z-rust"><span class="z-meta z-group z-rust"> <span class="z-constant z-numeric z-integer z-decimal z-rust">1</span><span class="z-punctuation z-separator z-rust">,</span> </span></span><span class="z-source z-rust"><span class="z-meta z-group z-rust"> <span class="z-constant z-numeric z-integer z-decimal z-rust">2</span><span class="z-punctuation z-separator z-rust">,</span> </span></span><span class="z-source z-rust"><span class="z-meta z-group z-rust"> <span class="z-constant z-numeric z-integer z-decimal z-rust">3</span><span class="z-punctuation z-separator z-rust">,</span> </span></span><span class="z-source z-rust"><span class="z-meta z-group z-rust"><span class="z-punctuation z-section z-group z-end z-rust">]</span></span> </span></code></pre> <pre data-lang="diff" class="language-diff z-code"><code class="language-diff" data-lang="diff"><span class="z-source z-diff"> [ </span><span class="z-source z-diff"> 1, </span><span class="z-source z-diff"> 2, </span><span class="z-source z-diff"> 3, </span><span class="z-source z-diff"><span class="z-markup z-inserted z-diff"><span class="z-punctuation z-definition z-inserted z-diff">+</span> 4, </span></span><span class="z-source z-diff"> ] </span></code></pre> <p>The diff only has one line. But if you are on a language which does not support this, like Haskell or JSON, you are out of luck. Since this is a so common and prevalent problem in many situations and many languages, people have converged onto a common workaround:</p> <pre data-lang="haskell" class="language-haskell z-code"><code class="language-haskell" data-lang="haskell"><span class="z-source z-haskell">[ <span class="z-constant z-numeric z-integer z-decimal z-haskell">1</span> </span><span class="z-source z-haskell"><span class="z-punctuation z-separator z-comma z-haskell">,</span> <span class="z-constant z-numeric z-integer z-decimal z-haskell">2</span> </span><span class="z-source z-haskell"><span class="z-punctuation z-separator z-comma z-haskell">,</span> <span class="z-constant z-numeric z-integer z-decimal z-haskell">3</span> </span><span class="z-source z-haskell">] </span></code></pre> <pre data-lang="diff" class="language-diff z-code"><code class="language-diff" data-lang="diff"><span class="z-source z-diff"> [ 1 </span><span class="z-source z-diff"> , 2 </span><span class="z-source z-diff"> , 3 </span><span class="z-source z-diff"><span class="z-markup z-inserted z-diff"><span class="z-punctuation z-definition z-inserted z-diff">+</span>, 4 </span></span><span class="z-source z-diff"> ] </span></code></pre> <p>In situations where a trailing comma is not allowed, this actually is very likely to be the best possible solution to format code in these cases. That's why people have independently converged on it over time again and again. However, as I will show, compared to simply having a trailing comma on the last item, this still has a lot of downsides.</p> <h2 id="case-study-function-arguments-in-nix"><a class="zola-anchor" href="#case-study-function-arguments-in-nix" aria-label="Anchor link for: case-study-function-arguments-in-nix">Case study: Function arguments in Nix</a></h2> <p>Okay, so let's design a format for function arguments and leading commas and try to make it as good as possible. I'll cover all relevant design questions. (More may come up during an actual implementation attempt, but this should cover most of the ground.)</p> <h3 id="basics"><a class="zola-anchor" href="#basics" aria-label="Anchor link for: basics">Basics</a></h3> <p>Let's start simple: Just three arguments, all single-line. There are already two possibilities to choose from.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-maybe z-nix">arg1</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">arg2</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-constant z-language z-nix">null</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">arg3</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 2</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">arg1</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">arg2</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-constant z-language z-nix">null</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">arg3</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>You may say "obviously we want the second one", but this already produces an inconsistency: In lists and attribute sets, we don't start the first element on the same line as the opening bracket/brace.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span> <span class="z-variable z-parameter z-name z-nix">first</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">second</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">third</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">{</span> <span class="z-entity z-other z-attribute-name z-multipart z-nix">one</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>1<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">two</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>2<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">three</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>3<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <p>Of course we could decide to format lists and attribute sets like this for consistency, but pushing this further down the line would create weird situations. In these examples, one cannot easily start the first element on the same line as the opening bracket/brace:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-keyword z-other z-nix">rec</span> <span class="z-punctuation z-definition z-attrset z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">one</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>1<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">two</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>2<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">three</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>3<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-variable z-parameter z-name z-nix">call</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">first</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">second</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">third</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-variable z-parameter z-name z-nix">call</span> <span class="z-comment z-line z-number-sign z-nix"># long line</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">[</span> <span class="z-variable z-parameter z-name z-nix">first</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">second</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">third</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>This may even have ripple effects onto other syntax constructs like parentheses. So it looks like accepting the one inconsistency in function declarations is the less bad option here.</p> <p>Now let's have a look at the same situation, but with trailing commas. Same decision as last time:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">arg1</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">arg2</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-constant z-language z-nix">null</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">arg3</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 2</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> <span class="z-variable z-parameter z-function z-1 z-nix">arg1</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">arg2</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-constant z-language z-nix">null</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">arg3</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>This time it is easy though, we can simply pick style #1 where no consistency issues arise.</p> <h3 id="multiline-arguments"><a class="zola-anchor" href="#multiline-arguments" aria-label="Anchor link for: multiline-arguments">Multiline arguments</a></h3> <p>Things are already getting tricky.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> <span class="z-variable z-parameter z-function z-1 z-nix">one</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> <span class="z-keyword z-operator z-nix">?</span> </span><span class="z-source z-nix"> <span class="z-keyword z-other z-nix">if</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-keyword z-operator z-nix">==</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">th</span><span class="z-keyword z-other z-nix">en</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">el</span><span class="z-keyword z-other z-nix">se</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">2</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 2</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> <span class="z-variable z-parameter z-function z-1 z-nix">one</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> <span class="z-keyword z-operator z-nix">?</span> </span><span class="z-source z-nix"> <span class="z-keyword z-other z-nix">if</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-keyword z-operator z-nix">==</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">th</span><span class="z-keyword z-other z-nix">en</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">el</span><span class="z-keyword z-other z-nix">se</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">2</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 3</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> <span class="z-variable z-parameter z-function z-1 z-nix">one</span> <span class="z-keyword z-operator z-nix">?</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> <span class="z-keyword z-operator z-nix">?</span> </span><span class="z-source z-nix"> <span class="z-keyword z-other z-nix">if</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-keyword z-operator z-nix">==</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">th</span><span class="z-keyword z-other z-nix">en</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">el</span><span class="z-keyword z-other z-nix">se</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> <span class="z-keyword z-operator z-nix">?</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">2</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 4</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"> <span class="z-keyword z-operator z-nix">?</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"> <span class="z-keyword z-operator z-nix">?</span> <span class="z-keyword z-other z-nix">if</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-keyword z-operator z-nix">==</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">th</span><span class="z-keyword z-other z-nix">en</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">el</span><span class="z-keyword z-other z-nix">se</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"> <span class="z-keyword z-operator z-nix">?</span> <span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">2</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>There are many other variations to handle the indentation here, but the main issue in all of these is that it is ambiguous where the "base indentation" is. Does indentation start at the comma or at the argument?</p> <p>On the trailing commas side, there is little to debate. The syntax looks so similar to bindings in attrsets, that we can simply copy the rules over 1:1 (replacing <code>=</code> with <code>?</code> and <code>;</code> with <code>,</code>). Yay for consistency!</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> <span class="z-keyword z-operator z-nix">?</span> </span><span class="z-source z-nix"> <span class="z-keyword z-other z-nix">if</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> <span class="z-keyword z-operator z-nix">==</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">th</span><span class="z-keyword z-other z-nix">en</span> <span class="z-constant z-numeric z-nix">2</span> <span class="z-keyword z-other z-nix">el</span><span class="z-keyword z-other z-nix">se</span> <span class="z-string z-quoted z-double z-nix"><span class="z-punctuation z-definition z-string z-double z-start z-nix">&quot;</span>two<span class="z-punctuation z-definition z-string z-double z-end z-nix">&quot;</span></span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-name z-nix">three</span> <span class="z-keyword z-operator z-nix">?</span> <span class="z-variable z-parameter z-name z-nix">function</span> <span class="z-punctuation z-definition z-list z-nix">[</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">1</span> </span><span class="z-source z-nix"> <span class="z-constant z-numeric z-nix">2</span> </span><span class="z-source z-nix"> <span class="z-punctuation z-definition z-list z-nix">]</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <h3 id="pattern"><a class="zola-anchor" href="#pattern" aria-label="Anchor link for: pattern"><code>@</code>-pattern</a></h3> <p>In Nix, you can have the <code>@</code>-pattern either before or after the argument destructuring. Both are equivalent.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-function z-4 z-nix">args</span>@<span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 2</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span>@<span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 3</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span> </span><span class="z-source z-nix">@<span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 4</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span> </span><span class="z-source z-nix">@ <span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 5</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span> @ <span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>Currently in Nix, leading <code>@</code> is the more prevalent convention (estimated through some quick greps through <code>nixpkgs</code>), but clearly it doesn't work here: the first argument without a leading comma just looks too inconsistent. (This is the same issue as three sections up coming to bite us again.)</p> <p>So the only sensible option is to normalize them to the trailing version during formatting. (Note that this is not as trivial as it may look like at a first glance, since any token may have comments associated with it which need proper handling as well. But it should be doable.)</p> <p>On the trailing commas side, it does not matter whether the <code>@</code>-pattern is before or after the arguments:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-function z-4 z-nix">args</span>@<span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span>@<span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 2</span> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-function z-4 z-nix">args</span> @ <span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span> @ <span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 3</span> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-function z-4 z-nix">args</span>@ </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span> </span><span class="z-source z-nix">@<span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 4</span> </span><span class="z-source z-nix"><span class="z-variable z-parameter z-function z-4 z-nix">args</span> @ </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-2 z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span> </span><span class="z-source z-nix">@ <span class="z-variable z-parameter z-function z-3 z-nix">args</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>There are many possibilities on how to place the whitespace here, but in all cases this works without having to normalize the code.</p> <h3 id="comments"><a class="zola-anchor" href="#comments" aria-label="Anchor link for: comments">Comments</a></h3> <p>Arguments may have comments associated with them. These may come in various forms, <code>#</code>, <code>/*</code>, <code>/**</code>, single-line or spanning multiple lines. Trailing comments <em>after</em> the argument name are trivial and don't need to be discussed.</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-comment z-block z-nix">/* comment */</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-comment z-block z-nix">/* </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> multiline </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> comment </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> */</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-comment z-line z-number-sign z-nix"># comment</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-comment z-line z-number-sign z-nix"># multiline</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># comment</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">four</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 2</span> </span><span class="z-source z-nix"><span class="z-comment z-block z-nix">/* comment */</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> <span class="z-variable z-parameter z-function z-maybe z-nix">one</span> </span><span class="z-source z-nix"><span class="z-comment z-block z-nix">/* </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> multiline </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> comment </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix">*/</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">two</span> </span><span class="z-source z-nix"><span class="z-comment z-block z-nix">/* comment */</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">three</span> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># multiline</span> </span><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># comment</span> </span><span class="z-source z-nix"><span class="z-keyword z-operator z-nix">,</span> <span class="z-variable z-parameter z-function z-1 z-nix">four</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <p>Style #1 has the disadvantage that the comma gets increasingly distanced from the argument it is associated with (comments may be long). It also loses the clear vertical line to the left which is one of the big advantages of a leading comma style.</p> <p>Style #2 looks more promising though. When using <code>#</code> comments it even has a clear vertical line for visual reference. It may be tempting to thus decide to normalize all <code>/*</code> comments to <code>#</code> for that purpose. But this may be a controversial change on its own, but more importantly this cannot be done for <code>/**</code> doc comments. Additionally, style #2 has the issue that the first comment collides with a potential comment for the entire function, causing ambiguity which would break any documentation tooling.</p> <p>Therefore, using style #1 for comments is the only viable option here.</p> <p>On the trailing commas sie, nothing really to discuss again:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-comment z-line z-number-sign z-nix"># 1</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset-or-function z-nix">{</span> </span><span class="z-source z-nix"> <span class="z-comment z-block z-nix">/* comment */</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">one</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-comment z-block z-nix">/* </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> multiline </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> comment </span></span><span class="z-source z-nix"><span class="z-comment z-block z-nix"> */</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">two</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># comment</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">three</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># multiline</span> </span><span class="z-source z-nix"> <span class="z-comment z-line z-number-sign z-nix"># comment</span> </span><span class="z-source z-nix"> <span class="z-variable z-parameter z-function z-1 z-nix">four</span><span class="z-keyword z-operator z-nix">,</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-entity z-function z-nix">}</span><span class="z-punctuation z-definition z-function z-nix">:</span> </span><span class="z-source z-nix"> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-list z-nix">[</span><span class="z-punctuation z-definition z-list z-nix">]</span> </span></code></pre> <h2 id="digression-leading-delimiters-all-the-way-down"><a class="zola-anchor" href="#digression-leading-delimiters-all-the-way-down" aria-label="Anchor link for: digression-leading-delimiters-all-the-way-down">Digression: Leading delimiters all the way down</a></h2> <p>There is an interesting observation to be made. Sometimes, expressions are not <em>separated</em> by a delimiter, but they are <em>terminated</em> by one. Think of the semicolon ending statements. Similarly in Nix:</p> <pre data-lang="nix" class="language-nix z-code"><code class="language-nix" data-lang="nix"><span class="z-source z-nix"><span class="z-keyword z-other z-nix">let</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">foo</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-constant z-numeric z-nix">1</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"> <span class="z-entity z-other z-attribute-name z-multipart z-nix">bar</span> <span class="z-keyword z-operator z-bind z-nix">=</span> <span class="z-variable z-parameter z-function z-4 z-nix">x</span><span class="z-punctuation z-definition z-function z-nix">:</span> <span class="z-variable z-parameter z-name z-nix">x</span><span class="z-punctuation z-terminator z-bind z-nix">;</span> </span><span class="z-source z-nix"><span class="z-keyword z-other z-nix">in</span> </span><span class="z-source z-nix"><span class="z-punctuation z-definition z-attrset z-nix">{</span><span class="z-punctuation z-definition z-attrset z-nix">}</span> </span></code></pre> <p>1. The semicolon on the last item is not optional. 2. The trailing comma style with a comma after the last item mirrors this.</p> <p>So, what if we flipped this on its head? After all, we are still trying out a style with leading commas.</p> <pre class="z-code"><code><span class="z-text z-plain">{ </span><span class="z-text z-plain">, foo </span><span class="z-text z-plain">, bar </span><span class="z-text z-plain">}: </span><span class="z-text z-plain"> </span><span class="z-text z-plain">[] </span></code></pre> <p>Okay, this looks very weird, but one can clearly see that it solves one of the major issues with the usual leading commas style which comes up around the first element. One could even indent the commas by one level to solve the indentation ambiguities discussed earlier. This concept also generalizes to attribute sets:</p> <pre class="z-code"><code><span class="z-text z-plain">{ </span><span class="z-text z-plain">; foo = 1 </span><span class="z-text z-plain">; bar = x: x </span><span class="z-text z-plain">} </span></code></pre> <p>Probably the main reason you find this weird is that the comma is an unusual delimiter to start statements. Same concept in another context and suddenly it doesn't look that weird anymore:</p> <pre data-lang="yaml" class="language-yaml z-code"><code class="language-yaml" data-lang="yaml"><span class="z-source z-yaml"><span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">foo</span> </span><span class="z-source z-yaml"><span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">bar</span> </span></code></pre> <p>Alex Rogozhnikov has a great writeup on this topic: <a href="https://arogozhnikov.github.io/2022/11/29/delimiter-comes-first.html">Delimiter-first code</a>. So while this idea requires syntax changes and thus is out of scope from a pure formatting perspective, if you find yourself designing a language, consider to briefly pause and ponder whether <em>starting</em> your statements instead of <em>terminating</em> them with a delimiter might be reasonable.</p> <h2 id="conclusion"><a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">Conclusion</a></h2> <p>When trying to create a good style with leading commas, at every corner we are faced with tough decisions between sub-optimal options and allowing inconsistencies. The only reason to use them in the first place is when they are the less bad option due to language restrictions. In fact, I don't know of any in the wild examples of people using leading commas despite the language supporting a trailing comma on the last item. (If you know of one let me know, but examples where language support was added later on after the style was "established" don't count!)</p> <h2 id="further-reading"><a class="zola-anchor" href="#further-reading" aria-label="Anchor link for: further-reading">Further reading</a></h2> <p>Other people have written about this topic as well. For now I'll just collect it down here, maybe if my text is not convincing enough one of these will do. If you find something, send it to me!</p> <ul> <li><a href="https://devblogs.microsoft.com/oldnewthing/20240209-00/?p=109379">On the virtues of the trailing comma</a></li> <li><a href="https://arogozhnikov.github.io/2022/11/29/delimiter-comes-first.html">Delimiter-first code</a></li> </ul> Things you should read 2024-03-04T00:00:00+00:00 2025-04-15T00:00:00+00:00 piegames https://piegames.de/dumps/links/ <p>This is a collection of texts and books that I've read which I strongly resonate with or which have had an impact on my life. Some I just recommend reading, others I think everybody should have read, and a few are a must if you want to ever discuss certain topics with me.</p> <h2 id="posts-and-articles"><a class="zola-anchor" href="#posts-and-articles" aria-label="Anchor link for: posts-and-articles">Posts and Articles</a></h2> <ul> <li> <p><strong><a href="https://gist.github.com/gcr/47a8f2baaa5810087615b4a858619fe5">undoing the pain – a guide to being free</a> <a href="https://drive.google.com/file/d/17FUfq4KhnUIMIuJbhfC83jm5k_JNbgvQ">(PDF)</a></strong></p> <p>A wholesome text about healing, for people who are deeply broken. This is not your usual self-therapy guide. It merely shows you the map, and you will have to navigate the territory yourself. I try to come back to it and read it again multiple times a year, to see where I am, where I want to go, and how far I've come.</p> </li> <li> <p><strong><a href="https://siderea.dreamwidth.org/1209794.html">The Asshole Filter</a></strong></p> <p>"If everyone around you is an asshole, maybe you are the asshole". This text shows up an alternative explanation than this adage, which also proves to be a very useful framework for interpersonal interactions.</p> </li> <li> <p><strong><a href="https://siderea.dreamwidth.org/1177349.html">Massless Ropes, Frictionless Pulleys: Coordinative Communication</a></strong></p> <p>This post talks a lot about how costs that are not measured are not controlled, how communication in organizations tends to be an unmeasured cost, how organization design affects communication patterns, and more. But its true value comes from adopting the mentality of finding unmeasured costs in everyday life that have gone out of control as a consequence. For example, finding all the small activities which my brain assumes to take zero time has tremendously helped me fight ADHD time blindness.</p> </li> <li> <p><strong><a href="https://stainedglasswoman.substack.com/p/letting-them-let-go">Letting Them Let Go</a></strong></p> <p>Grieving is an important part of life which most of us never really learn. Not processing these emotions holds people back and prevents them from moving on. Most importantly, this text shows how grieving happens so much more often than just when a loved one dies, and that acknowledging the grief in those situations as well is immensely important.</p> </li> <li> <p><strong><a href="https://www.newsweek.com/trans-man-broken-men-1817169">I'm a Trans Man. I Didn't Realize How Broken Men Are</a></strong></p> <p>Many trans people have the unique perspective of witnessing our patriarchal and sexist society from "both sides". And many people are not aware how deeply our patriarchy harms men as much as women, albeit in very different ways. (Also this is why I despise "men-vs-women" feminists)</p> </li> <li> <p><strong><a href="https://blog.apaonline.org/2022/05/10/tone-policing-and-the-assertion-of-authority/">Tone-Policing and the Assertion of Authority</a></strong></p> <p>Especially because I am in a position of social power in several places, I take the issue of tone policing very seriously. This text is my go-to on the subject.</p> </li> <li> <p><strong><a href="https://innig.net/teaching/liberal-arts-manifesto">What Liberal Arts Education Is For</a></strong></p> <p>Even when I was still in school, the "what will I need this for later on in my life??" discourse somehow made me cringe a bit. This text puts all my feelings on this topic and related aspects into words better than I ever could.</p> </li> </ul> <h2 id="books"><a class="zola-anchor" href="#books" aria-label="Anchor link for: books">Books</a></h2> <ul> <li> <p><strong>Adult Children of Emotionally Immature Parents – How to Heal from Distant, Rejecting or Self-Involved Parents</strong></p> <p>Even if your childhood was absolutely fine™, chances are you this wasn't the case for many people you know and maybe love. Partners, dates, friends, superiors—This book explains both how to recognize and deal with emotionally immature people, as well as the many struggles of people whose emotional needs were unmet in the past. Moreover, I've found the approach of viewing all interpresonal conflicts through a lens of unmet or colliding needs as extremely helpful in many situations.</p> </li> <li> <p><strong><a href="https://meaningness.com/">Meaningness</a></strong></p> <p>A web book on the meaning of life, religion, spirituality and so much more.</p> </li> </ul> Tardis 2023-08-30T00:00:00+00:00 2023-08-30T00:00:00+00:00 piegames https://piegames.de/art/2023-tardis/ <p>First non-digital art in the collection: An aquarelle painting.</p> Nix ohne NixOS 2021-05-24T00:00:00+00:00 2021-05-24T00:00:00+00:00 piegames https://piegames.de/dumps/nix-ohne-nixos/ <p>In 2021 hat das <a href="https://www.chaos-inkl.de/">ChaosInKl</a> online eine "OpenTopicNight" zum Thema Nix veranstaltet. Dieser Vortrag über Nix auf nicht-NixOS-Systemen, mit Fokus auf <code>nix-shell</code> und Entwicklungsumgebungen, ist dafür entstanden.</p> Fur balls #2 2018-06-10T00:00:00+00:00 2018-06-10T00:00:00+00:00 piegames https://piegames.de/art/2018-fur-balls-2/ <p>Shamelessly riffing on the success of <a href="/art/2016-fur-balls-1">Fur balls #1</a>, but as we all know the sequel is rarely as good as the original.</p> Purple 2017-08-05T00:00:00+00:00 2017-08-05T00:00:00+00:00 piegames https://piegames.de/art/2017-purple/ <p>Brush study with Krita. Inspired by one of the paintings somewhere on the Documenta Kassel 2017.</p> Network 2017-06-06T00:00:00+00:00 2017-06-06T00:00:00+00:00 piegames https://piegames.de/art/2017-network/ <p>A more serious attempt at using the Animation nodes Blender extension for procedural generation. The image is 100% procedural based on modulated noise; the poodle is just a hallucination of your brain. (Seriously though, if you can't see the poodle then good for you because you won't be able to unsee it.)</p> Minecraft #4 – Floating island 2017-04-30T00:00:00+00:00 2017-04-30T00:00:00+00:00 piegames https://piegames.de/art/2017-minecraft-4/ <p>This is the fourth image of a Minecraft world exported to Blender. It showcases the possibility of using creative materials on the model, in this case an X-ray inspired effect realized through volumetric effects. The third image shows a direct comparison: Minecraft screenshot – X-ray – Fancy render</p> Disorder 2017-04-08T00:00:00+00:00 2017-04-08T00:00:00+00:00 piegames https://piegames.de/art/2017-disorder/ <p>A small procedual texture experiment which never got past these three test renders. While this is 2D, it too has been realized in Blender: An "Emission" material which procedurally generates the texture acts as a pixel shader.</p> Decompose effect 2017-03-05T00:00:00+00:00 2017-03-05T00:00:00+00:00 piegames https://piegames.de/art/2017-decompose-effect/ <p>One of the rare videos I did. This is an early experiment with the Blender extension Animation nodes, although more abstractly speaking one would better describe it as a vertex shader.</p> Minecraft #2 – Panorama 2017-01-29T00:00:00+00:00 2017-01-29T00:00:00+00:00 piegames https://piegames.de/art/2017-minecraft-3/ <p>This is the third image of a Minecraft world exported to Blender. It showcases the theoretically infinite render distance one can achieve. The materials are taken from a texture pack which provides higher resolution and normal/illumination maps for all textures while remaining true to the Default Minecraft look.</p> Planet 2016-12-22T00:00:00+00:00 2016-12-22T00:00:00+00:00 piegames https://piegames.de/art/2016-planet/ Minecraft #2 – Nether 2016-10-29T00:00:00+00:00 2016-10-29T00:00:00+00:00 piegames https://piegames.de/art/2016-minecraft-2/ <p>This is the second image of a Minecraft world exported to Blender. It showcases the raytracing volumetric rendering which creates an atmosphere which cannot be achieved in real time. The second image is a 20% size test render, but which somehow accidentally turned out a lot better than the final version.</p> Minecraft #1 – Flatland 2016-10-14T00:00:00+00:00 2016-10-14T00:00:00+00:00 piegames https://piegames.de/art/2016-minecraft-1/ <p>This is the first successful export from a Minecraft world to Blender using my (unpublished) tool Wavecraft.</p> Pixel factory #2 2016-07-18T00:00:00+00:00 2016-07-18T00:00:00+00:00 piegames https://piegames.de/art/2016-pixel-factory-2/ Floating island 2016-06-11T00:00:00+00:00 2016-06-11T00:00:00+00:00 piegames https://piegames.de/art/2016-floating-island/ Fur balls #1 2016-05-08T00:00:00+00:00 2016-05-08T00:00:00+00:00 piegames https://piegames.de/art/2016-fur-balls-1/ <p>Created by following a tutorial ·. Note that the second image is the original one. The first one is a higher quality re-render from 2019, in which the background was redone together with some other small improvements on the lighting.</p> Pixel factory #1 2016-02-23T00:00:00+00:00 2016-02-23T00:00:00+00:00 piegames https://piegames.de/art/2016-pixel-factory-1/ Entrance 2016-01-16T00:00:00+00:00 2016-01-16T00:00:00+00:00 piegames https://piegames.de/art/2016-entrance/ <p>First 2D image, done with Krita and a graphics tablet. This image initially started as a 64px × 64px canvas with random lines scribbled on it. (All gray, color got only added at the end.) Then it got repeatedly upscaled with details filled in, until it converged to some kind of meaningful image. This is also why the lines in the image are so big and a bit blurry.</p> Music box 2016-01-11T00:00:00+00:00 2016-01-11T00:00:00+00:00 piegames https://piegames.de/art/2016-music-box/ Winter #3 2015-12-24T00:00:00+00:00 2015-12-24T00:00:00+00:00 piegames https://piegames.de/art/2015-winter-3/ <p>Study for a shiny snow material.</p> Winter landscape 2015-12-15T00:00:00+00:00 2015-12-15T00:00:00+00:00 piegames https://piegames.de/art/2015-winter-landscape/ <p>I have deleted all other Christmas-themed winter images except for this one. So, enjoy this rarity.</p> Carousel #3 2015-10-18T00:00:00+00:00 2015-10-18T00:00:00+00:00 piegames https://piegames.de/art/2015-carousel-3/ <p>This image was inspired by "Skyfall", one of the "zero G" free fall towers.</p> Landscape #2 2015-10-12T00:00:00+00:00 2015-10-12T00:00:00+00:00 piegames https://piegames.de/art/2015-landscape-2/ <p>First attempt at a low-poly style.</p> Carousel #2 2015-10-10T00:00:00+00:00 2015-10-10T00:00:00+00:00 piegames https://piegames.de/art/2015-carousel-2/ <p>Continuation of <a href="/art/2015-carousel-1">Carousel #1</a>. The carousel model is slightly improved and placed in a proper scene.</p> Landscape #1 2015-09-06T00:00:00+00:00 2015-09-06T00:00:00+00:00 piegames https://piegames.de/art/2015-landscape-1/ Carousel #1 2015-02-01T00:00:00+00:00 2015-02-01T00:00:00+00:00 piegames https://piegames.de/art/2015-carousel-1/ <p>Small modelling study</p> Marble maze 2015-02-01T00:00:00+00:00 2015-02-01T00:00:00+00:00 piegames https://piegames.de/art/2015-marble-maze/