<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>Hacker News Personal Blogs 2021 | All Blog Posts</title><link>https://hn-blogs.kronis.dev/feed.xml</link><description>A collection of blog posts from users of Hacker News, based on RSS feeds.</description><language>en-US</language><lastBuildDate>Fri, 10 Jul 2026 04:03:21 GMT</lastBuildDate><generator>rfeed v1.1.1</generator><docs>https://github.com/svpino/rfeed/blob/master/README.md</docs><item><title>Big Tech, so bad that even Big Tech dislikes it</title><link>https://stop.zona-m.net/2021/12/big-tech-so-bad-that-even-big-tech-dislikes-it/</link><description>&lt;p&gt;We truly live in interesting times.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 31 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/big-tech-so-bad-that-even-big-tech-dislikes-it/</guid></item><item><title>jumping through hoops</title><link>https://www.gingerlime.com/2021/jumping-through-hoops/</link><description>&lt;p&gt;One recent pet peeve of mine has been how companies make you jump through hoops just to report an issue to support. Trello is a recent one. I used to be able to just email support@trello.com &amp;#8230; Not anymore&amp;#8230; You&amp;#8217;ve emailed support@trello.com, which is no longer available for support. That&amp;#8217;s great! And the best part?&amp;#8230;&lt;/p&gt;
&lt;p&gt;The post &lt;a href="https://www.gingerlime.com/2021/jumping-through-hoops/"&gt;jumping through hoops&lt;/a&gt; appeared first on &lt;a href="https://www.gingerlime.com"&gt;Gingerlime&lt;/a&gt;.&lt;/p&gt;</description><author>Gingerlime</author><pubDate>Fri, 31 Dec 2021 14:00:10 GMT</pubDate><guid isPermaLink="true">https://www.gingerlime.com/2021/jumping-through-hoops/</guid></item><item><title>Three Good Books I Read in 2021</title><link>https://nicolaiarocci.com/three-good-books-i-read-in-2021/</link><description>&lt;p&gt;This year I&amp;rsquo;ve read twenty-one books or 5903 pages. That&amp;rsquo;s fewer books than
last year (28 / 8064), the year before (25 / 8394), and the one before that (30
/ 8447). Heck, I must look back at 2014 to score a win in my very own yearly
reading challenge. What surprises me is not much the number of books but the
pages I read, which constitutes a &lt;a href="https://twitter.com/nicolaiarocci/status/1214857647314362368"&gt;more relevant metric&lt;/a&gt;. Over the previous
three years, that number stayed firmly over 8k while dropping to 6k this year.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 31 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/three-good-books-i-read-in-2021/</guid></item><item><title>My macOS programs and setup</title><link>https://maxleiter.com/notes/mac-setup</link><description>&lt;p&gt;In the off-chance you're reading this today, happy new year! If you're reading this later, I hope $CURRENT_YEAR is treating you well. &lt;/p&gt;
&lt;p&gt;Before this post, I kept a list of my programs in my iCloud in case my computer broke or I was setting up a work laptop. After some recent events, I trust iCloud less than I once did, and figured the list can live here instead. Like the rest of my blog, this exists for me but hopefully you find it useful too. I plan to write an Arch Linux version of this soon. &lt;/p&gt;
&lt;p&gt;I try to use open-source where I can. If you know of an alternative to the few closed applications I use, please let me know!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mozilla.org/en-US/firefox/channel/desktop/" rel="noopener noreferrer" target="_blank"&gt;Firefox Nightly&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.google.com/chrome/" rel="noopener noreferrer" target="_blank"&gt;Chrome&lt;/a&gt; for testing sites&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer" target="_blank"&gt;VisualStudio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://iterm2.com/" rel="noopener noreferrer" target="_blank"&gt;iTerm 2&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://folivora.ai/" rel="noopener noreferrer" target="_blank"&gt;BetterTouchTool&lt;/a&gt; allows advanced customization of the touchbar  ($9 after 45 days)&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.folivora.ai/t/goldenchaos-btt-the-complete-touch-bar-ui-replacement/1281" rel="noopener noreferrer" target="_blank"&gt;GoldenChaos preset&lt;/a&gt;&lt;br /&gt;  &lt;img alt="Image of the GoldenChaos touchbar layout" src="/blog/macos/touchbar.jpg" /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://brew.sh/" rel="noopener noreferrer" target="_blank"&gt;Homebrew&lt;/a&gt; is a package manager&lt;ul&gt;
&lt;li&gt;Handles installing Xcode developer tools&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rectangleapp.com/" rel="noopener noreferrer" target="_blank"&gt;Rectangle&lt;/a&gt; lets you resize and snap windows with your keyboard&lt;br /&gt;  &lt;img alt="Image of the Rectangle settings showing it has many options" src="/blog/macos/rectangle.jpg" /&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fishshell.com/" rel="noopener noreferrer" target="_blank"&gt;Fish&lt;/a&gt; is user-friendly shell (seriously, I hate zsh/bash now)&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jorgebucaran/fisher" rel="noopener noreferrer" target="_blank"&gt;Fisher&lt;/a&gt;: Fish plugin manager&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jorgebucaran/fish-nvm" rel="noopener noreferrer" target="_blank"&gt;fish-nvm&lt;/a&gt;: &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer" target="_blank"&gt;nvm&lt;/a&gt; wrapper&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextdns.io/" rel="noopener noreferrer" target="_blank"&gt;NextDNS&lt;/a&gt; is a secure DNS service (I downloaded the macOS client from the App Store)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lastpass.com/" rel="noopener noreferrer" target="_blank"&gt;LastPass&lt;/a&gt; is my password manager, but I'm currently looking to start self-hosting.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://insomnia.rest/" rel="noopener noreferrer" target="_blank"&gt;Insomnia&lt;/a&gt; is my favorite REST client&lt;/li&gt;
&lt;li&gt;&lt;a href="https://iina.io/" rel="noopener noreferrer" target="_blank"&gt;IINA&lt;/a&gt; is a nice media player &lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniusevia.com/" rel="noopener noreferrer" target="_blank"&gt;VIA Configurator&lt;/a&gt; configures my keyboard&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/skyjake/lagrange" rel="noopener noreferrer" target="_blank"&gt;Lagrange&lt;/a&gt; for viewing Gemini articles&lt;/li&gt;
&lt;li&gt;&lt;a href="https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable" rel="noopener noreferrer" target="_blank"&gt;yarn&lt;/a&gt; is my node package manager of choice&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mysql.com/products/workbench/" rel="noopener noreferrer" target="_blank"&gt;MySQL Workbench&lt;/a&gt; for managing my few MySQL servers&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xm1math.net/texmaker/" rel="noopener noreferrer" target="_blank"&gt;TeXMaker&lt;/a&gt; for the rare times I need to write LaTeX&lt;/li&gt;
&lt;li&gt;I'm going to try and use the Slack and Zoom browser versions&lt;/li&gt;
&lt;li&gt;Qemu: `brew install qemu`&lt;/li&gt;
&lt;li&gt;thefuck: `brew install thefuck`, command line correction tool&lt;ul&gt;
&lt;li&gt;Add `thefuck --alias | source` to `~/.config/fish/config.fish`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;imagemagick: `brew install imagemagick`&lt;/li&gt;
&lt;li&gt;ffmpeg: `brew install ffmpeg`&lt;/li&gt;
&lt;/ul&gt;</description><author>Max Leiter</author><pubDate>Fri, 31 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://maxleiter.com/notes/mac-setup</guid></item><item><title>2021 status report</title><link>https://arnorhs.dev/posts/2021-12-31/2021-status-report/</link><description>What excites me most coming into 2022. Solid.js, Astro, Next.js, Remix, Netlify, Cloudflare workers, trpc, magic.link, planetscale, my npm packages and more.</description><author>arnorhs blog - arnorhs.dev</author><pubDate>Fri, 31 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://arnorhs.dev/posts/2021-12-31/2021-status-report/</guid></item><item><title>Probabilistic spin glass - Part IV</title><link>https://bytepawn.com/probabilistic-spin-glass-part-iv.html</link><description>&lt;p&gt;This is a continuation of the previous articles on probabilistic spin glasses. I run simulations to understand the scaling behaviour for large spin glasses. &lt;br /&gt;&lt;br /&gt;&lt;img alt="." src="/images/spin_glass_p4_2.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Fri, 31 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/probabilistic-spin-glass-part-iv.html</guid></item><item><title>Certain biometric troubles in India...</title><link>https://stop.zona-m.net/2021/12/certain-biometric-troubles-in-india.../</link><description>&lt;p&gt;Are nothing new, really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 30 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/certain-biometric-troubles-in-india.../</guid></item><item><title>Taming My Soda Addiction: What Worked and What Didn’t</title><link>https://blog.nawaz.org/posts/2021/Dec/taming-my-soda-addiction-what-worked-and-what-didnt/</link><description>&lt;a class="reference external image-reference" href="https://blog.nawaz.org/images/sodaaddiction/sodasugar.jpg"&gt;
&lt;img alt="A can of soda filled with sugar" src="https://blog.nawaz.org/images/sodaaddiction/sodasugar.jpg" /&gt;
&lt;/a&gt;
&lt;p&gt;Here I&amp;#8217;ll discuss various ways I&amp;#8217;ve tried to reduce my soda intake -
primarily to avoid sugar. &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want the quick summary of what worked (in no&amp;nbsp;order):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Working from home (no free&amp;nbsp;soda)&lt;/li&gt;
&lt;li&gt;Switching to Spindrift and Izze (more of the&amp;nbsp;former)&lt;/li&gt;
&lt;li&gt;Reducing portion size …&lt;/li&gt;&lt;/ul&gt;</description><author>Beetle Space</author><pubDate>Thu, 30 Dec 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.nawaz.org/posts/2021/Dec/taming-my-soda-addiction-what-worked-and-what-didnt/</guid></item><item><title>Learnings from patching a bug in a modern project</title><link>https://blog.herlein.com/post/modern-node-dev/</link><description>&lt;p&gt;I&amp;rsquo;m continuing to dive deep on modern typescript/node development.  I fixed a bug in a
new tool I am using (&lt;a href="https://github.com/projen/projen/pull/1437"&gt;projen&lt;/a&gt;) and learned
a ton along the way.  This post is to share some of that.&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Thu, 30 Dec 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/modern-node-dev/</guid></item><item><title>The Rites of Passage</title><link>https://godsip.club/articles/the-rites-of-passage/</link><description>&lt;h1 id="flea-markets--source-of-treasures"&gt;Flea Markets = Source of Treasures &lt;a class="anchor" href="#flea-markets--source-of-treasures"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;A few weeks ago, at the local flea market, I was very lucky to find a copy of &lt;em&gt;The Rites of Passage&lt;/em&gt; by Arnold Van Gennep. It is an Italian edition from 1960s and it belonged to the library of a nearby village. It shows the signs of time, but for the price (about 1€) I couldn’t really complain! I immediately started studying it and I was glad to find, since the first pages, references to &lt;em&gt;&lt;a href="https://godsip.club/articles/the-golden-bough/"&gt;The Golden Bough&lt;/a&gt;&lt;/em&gt;, which I finished previously!&lt;/p&gt;
&lt;figure&gt;&lt;img alt="It may be not in a perfect state, but for me it&amp;rsquo;s glorious!" src="https://godsip.club/img/rites-of-passage.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;It may be not in a perfect state, but for me it&amp;rsquo;s glorious!&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I’ll try to (badly) recap some of the most intriguing theories exposed here. Let’s remember that the given examples are general, so similarities can be observed with both our modern society and remote past civilisations around the globe.&lt;/p&gt;
&lt;p&gt;First, the life of a human being is a series of &lt;em&gt;stages&lt;/em&gt;: childhood, puberty, married life , etc. Also, just as important, throughout life, there are &lt;em&gt;communities&lt;/em&gt; which a person belongs to. They can be categorised. The most basic partition is sexual-based: &lt;em&gt;men&lt;/em&gt; and &lt;em&gt;women&lt;/em&gt; – it’s too well-known that they have never been put on the same level. The other clear separation in everyday actions is based on magical-religious aspects: &lt;em&gt;sacred&lt;/em&gt; and &lt;em&gt;profane&lt;/em&gt;. It may seem subtle, but the distinction is very used even nowadays.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Similarities can be observed with both our modern society and even remote past civilisations around the globe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In addition to the previous distinctions, imagine that a civilisation is composed essentially by &lt;em&gt;classes&lt;/em&gt;: based on professions, castes, religious associations,… We could find so many examples. Moreover, you can picture the &lt;em&gt;sub-classes&lt;/em&gt; that we could find, for example, if we divide them geographically (e.g., local parishes).&lt;/p&gt;
&lt;p&gt;IT. IS. A. MESS.&lt;/p&gt;
&lt;h1 id="i-dont-belong-here--t-yorke"&gt;‘&lt;em&gt;I Don’t Belong Here&lt;/em&gt;’ – T. Yorke &lt;a class="anchor" href="#i-dont-belong-here--t-yorke"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Ok, it’s like Monopoly: before starting, we have set &lt;strong&gt;many&lt;/strong&gt; pieces on the table, but it gets easier once the game begins. The golden rule is that &lt;em&gt;&lt;strong&gt;an individual cannot be part of different classes&lt;/strong&gt;&lt;/em&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. So, in order to change group, a &lt;em&gt;rite&lt;/em&gt; (or ceremony) must take place. That’s the focal point and goal of the book: to understand the &lt;em&gt;sequence&lt;/em&gt; and, when possible, the &lt;em&gt;meaning&lt;/em&gt; of the rituals that are performed in such occasions.&lt;/p&gt;
&lt;p&gt;Another powerful picture is given in the very first pages: societies are like &lt;strong&gt;buildings with many rooms&lt;/strong&gt;, one for each “class”. In the primitive civilisations, the walls of the rooms were thicker, so the communication among them was more difficult. In modern society the walls are thinner, so the distinctions are less pronounced. In other words, less rites are required to change class. &lt;em&gt;Progress&lt;/em&gt;, we could say. Very true indeed, if it weren’t for some very archaic remnants part of our inner culture.&lt;/p&gt;
&lt;p&gt;As other anthropologists did before, Van Gennep pointed out that similarities could be found in ceremonies dedicated to completely different situations: for instance, weddings, funerals, and coronations of kings. What is a common point among them?&lt;/p&gt;
&lt;figure&gt;&lt;img alt="The veil was used for many types of celebrations, not just for weddings. It acted as a protection." src="https://godsip.club/img/bride-veil.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;The veil was used for many types of celebrations, not just for weddings. It acted as a protection.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It was common, since Roman times, to cover the spouses with a &lt;em&gt;veil&lt;/em&gt;. The modern bride still wears it while she walks towards the altar. Why, though? It is to &lt;em&gt;protect&lt;/em&gt; her holiness from the profane world that she comes from. The wedding is a change of state to a higher level, in which the woman will be seen with more attributes. The same goes for a king, which wears a cloak during the enthroning ceremony. And the same principle is valid for death: corpses were often covered in sheets because the place where the soul is headed is sacred – its journey must not be impeded by our “filthy” world.&lt;/p&gt;
&lt;h1 id="separation---transition---incorporation"&gt;Separation - Transition - Incorporation &lt;a class="anchor" href="#separation---transition---incorporation"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;So, if the &lt;em&gt;rites of passage&lt;/em&gt; are the ones required to pass from a class to another (or even from a world to a distinct one), this three-step sequence is their key:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Separation rites (from the previous reality)&lt;/li&gt;
&lt;li&gt;Transition rites&lt;/li&gt;
&lt;li&gt;Incorporation rites (to the new reality)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It may seem absurd to force such classification for all the ceremonies, but the results of Van Gennep’s analysis are quite stunning.&lt;/p&gt;
&lt;p&gt;One very important concept is the &lt;em&gt;threshold&lt;/em&gt;. Our home’s door, for example: it protects us &lt;em&gt;from the outside world&lt;/em&gt;. In the past, both Arabs and Jews stroked an effigy or a casket near the door when entering or exiting  the house – an incorporation or separation gesture, depending on the case. Also, in some parts of the world, pregnant women can’t enter through the main door of their houses&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;. Even bodies, in funeral rites, are not let pass through the main door because it is seen as a bad omen.&lt;/p&gt;
&lt;p&gt;Another great example of this concept, often underrated, is the &lt;a href="https://en.wikipedia.org/wiki/Triumphal_arch" rel="noopener" target="_blank"&gt;triumphal arch&lt;/a&gt;: in Roman times, armies and important personalities passed under the arch to enter symbolically and physically into the new reality and to “wash away” the contamination of the &lt;em&gt;outside&lt;/em&gt;&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;. The simplest threshold can be seen as a barrier between worlds: among many civilisations, it is spread the belief that spirits cannot cross &lt;em&gt;water courses&lt;/em&gt;, which in this case act like some other type of boundaries.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="In ancient times, swimmers used to mock spirits that couldn&amp;rsquo;t follow them into the water." src="https://godsip.club/img/woman-river.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;In ancient times, swimmers used to mock spirits that couldn&amp;rsquo;t follow them into the water.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h1 id="is-he-dead-no-its-just-puberty"&gt;‘Is He Dead?’ ‘No, It’s Just Puberty’ &lt;a class="anchor" href="#is-he-dead-no-its-just-puberty"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Other similarities between rituals related to different moments in life can be observed analysing the &lt;em&gt;initiation rites&lt;/em&gt; for special societies or, simply, just the ones for puberty. In a few words, the initiated boy temporarily &lt;em&gt;dies&lt;/em&gt; to exit his previous state and enter the new one (adulthood). The example is clearer in Congo, for instance, where the kid was exiled (&lt;em&gt;separation&lt;/em&gt;) and painted white, which is a color often related to death (&lt;em&gt;margin&lt;/em&gt;). Other than that, when he was re-admitted in his village, he had to act like a baby for weeks, or months: adults had to teach him again the basic tasks, and he also ate baby food (&lt;em&gt;incorporation&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;The book is intriguing because there are many other cases illustrated and explained. One for all: circumcision. Why is it performed, and why at different ages (some cultures do it after a few weeks from birth, others after ten years)? Simple answer: it is a &lt;em&gt;way to show the membership to a special class&lt;/em&gt;, or society. Quoting the text:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The mutilated individual is removed from the common mass of humanity by a rite of separation (this is the idea behind cutting, piercing, etc.) which automatically incorporates him into a defined group.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img alt="&amp;ldquo;Circumcision of Christ&amp;rdquo; by Giovanni Bellini. We&amp;rsquo;re with you, little fella." src="https://godsip.club/img/circumcision-bellini.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;&amp;ldquo;Circumcision of Christ&amp;rdquo; by Giovanni Bellini. We&amp;rsquo;re with you, little fella.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Finally, probably the most obvious rite to assign a newborn to a “category”, even unintentionally, is by giving him or her a name. It’s not about using a middle name, as nowadays. In the past, a child could change name several times: first, it was named according to the sex with a generic one; then, an ancestor’s name was chosen; later, depending on the “phase”, or his age, he could change it again. In totemic societies, at the time of initiation, the name could vary once more. And even when a man becomes father, it could &lt;a href="https://en.wikipedia.org/wiki/Teknonymy" rel="noopener" target="_blank"&gt;be changed again&lt;/a&gt; with his son’s.&lt;/p&gt;
&lt;p&gt;To conclude the overview of this amazing book, which is such dense of notions yet brief, it is a must for me to spread the word that it is &lt;a href="https://archive.org/details/theritesofpassage" rel="noopener" target="_blank"&gt;available for free on the Internet Archive&lt;/a&gt;!&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Of course there are exceptions. It is just a rule to simplify the understanding.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Nursing mothers and pregnant women have always been treated as “special”, but not always in a good way.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Interestingly, Norse people had a similar tradition and divided their world between inside (&lt;em&gt;Innangard&lt;/em&gt;) and outside (&lt;em&gt;Utangard&lt;/em&gt;), and the village walls (&lt;em&gt;gardr&lt;/em&gt;) were very important. We’ll talk about that.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Godsip Club</author><pubDate>Thu, 30 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://godsip.club/articles/the-rites-of-passage/</guid></item><item><title>Guidance for Scaling - Reversible vs. Irreversible Decisions</title><link>/2021/12/29/Guidance-for-Scaling-Reversible-vs.-Irreversible-Decisions/</link><description>&lt;p&gt;Was having a conversation with a founder earlier today and the topic of hiring functional leaders came up. I offered one of my common pieces of advice which was don&amp;rsquo;t hold the reins too tightly once you hire them. It&amp;rsquo;s something I see happen over and over to first time founders. You hire a new VP of Product and then still continue to oversee so much of the product process yourself. It is understandable, it&amp;rsquo;s your baby, you&amp;rsquo;ve spent years building it to this point, they don&amp;rsquo;t love it the same way you do.&lt;/p&gt;
&lt;p&gt;The likely outcome is your new VP of product won&amp;rsquo;t find success. They&amp;rsquo;ll feel they&amp;rsquo;re not able to execute on a vision of their own. They&amp;rsquo;ll feel micromanaged. Even the smallest decisions they&amp;rsquo;ll feel aren&amp;rsquo;t fully theirs and get second guessed.&lt;/p&gt;
&lt;p&gt;Perhaps you could do it better, that&amp;rsquo;s not necessarily the question. What you&amp;rsquo;re focused on is growing and scaling, this is the reason you hired them. So how do you do this without the business careening off tracks? Well first, empower them to make decisions, but beyond that there are a few things you can do so you feel more comfortable entrusting them with their functional area.&lt;/p&gt;
&lt;h3 id="communication-is-key"&gt;
&lt;div&gt;
Communication is key
&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;First, clearly communicate your priorities and thought process. This doesn&amp;rsquo;t mean tell them what to do, but priorities&amp;hellip; Bob Iger states it as &amp;ldquo;You have to convey your priorities clearly and repeatedly. In my experience, it’s what separates great managers from the rest&amp;rdquo;. I couldn&amp;rsquo;t immediately find the reference, but recall reading that he started each week with his execs communicating his top 5 priorities across the company. This gives you a line of sight into the leadership&amp;rsquo;s thinking. This could be as simple as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;m worried about our pipeline for next year&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m worried about retention and how it affects long term growth&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m worried that we have the tech stack to scale for next 3 years&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Something similar to that last one came up in the conversation, which led us down a brief conversation of reversibles and irreversibles. In product management one of the most common mistakes I see of folks is not making a decision and heading in a single direction, rather they want all the info before they make a decision which can cause as much harm as good (but I&amp;rsquo;m getting off track).&lt;/p&gt;
&lt;h3 id="reversible-vs-irreversible-decisions"&gt;
&lt;div&gt;
Reversible vs. Irreversible Decisions
&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Communicating to new execs a framework around which decisions are critical to get right and which ones can be recovered from if wrong is huge. Not all decisions are critical to get right. If ship a typo in a blog post, you can easily fix it. If you send a wrong message to a single customer, it could create a bad experience for that customer but not impact all the rest. Knowing the scope and impact of a failure is important in a framework for decision making.&lt;/p&gt;
&lt;p&gt;We discussed that architecture and engineering decisions it&amp;rsquo;s harder to reverse than say marketing. But I&amp;rsquo;m not sure that holds true, if you have a huge PR nightmare from bad marketing it can take years to recover and heavily set back the business just like a bad architecture decision. Even within engineering decisions you can use a framework of how costly is this to commit to and how easy is it to reverse.&lt;/p&gt;
&lt;p&gt;One of the best engineers I&amp;rsquo;ve ever worked with, for going on 15 years now states it as not foreclosing future options. Leaving optionality open in engineering is one of those 10x things. It&amp;rsquo;s not elegant code or overengineering, it&amp;rsquo;s highly immeasurable (at least to me it is), but trying to grow this is huge to enable in your functional leadership as well as more senior individual contributors.&lt;/p&gt;
&lt;p&gt;Which decisions can later be changed and the cost of that is a great framework for how involved you should be vs. not, and the more you can guide your team through that the smoother growing the team will be.&lt;/p&gt;</description><author>CRAIG KERSTIENS</author><pubDate>Wed, 29 Dec 2021 23:30:56 GMT</pubDate><guid isPermaLink="true">/2021/12/29/Guidance-for-Scaling-Reversible-vs.-Irreversible-Decisions/</guid></item><item><title>New US Act about chipmaking is important for EVERYBODY</title><link>https://stop.zona-m.net/2021/12/new-us-act-about-chipmaking-is-important-for-everybody/</link><description>&lt;p&gt;Meanwhile, Europe and China&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 29 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/new-us-act-about-chipmaking-is-important-for-everybody/</guid></item><item><title>Tree Survey using Machine Learning</title><link>https://blog.nobugware.com/post/2021/tree-survey-using-machine-learning/</link><description>My buddy and I bought some lands (20ha/50acres) in the Quebec province.
Creating a new problem It all started because as a new &amp;ldquo;forest owner&amp;rdquo;, I wanted to do a tree survey.
There are some good mobile apps to recognize flowers and trees but nothing professionally graded I knew (except paying for human expert).
I also needed a solution that would work offline since the mobile network coverage is not good on the field.</description><author>Fabrice Aneche</author><pubDate>Wed, 29 Dec 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.nobugware.com/post/2021/tree-survey-using-machine-learning/</guid></item><item><title>I'll be home for Christmas: Quarantine Piano Edition</title><link>https://joshuarogers.net/articles/2021-12/ill-be-home-for-christmas-quarantine-piano-edition/</link><description>This year, despite generally taking precautions and having two shots of Moderna, Christmas found me in quarantine in my home office, hiding away from my family. As far as Christmas traditions go, &amp;ldquo;physical isolation&amp;rdquo; has to be one of the more unusual ways to celebrate a holiday whose traditions frequently involve getting together in larger groups.
It was Christmas day and I was alone, but it didn't really feel that way: my wife, daughter, and I spent Christmas lunch (and supper) talking via FaceTime.</description><author>Joshua Rogers</author><pubDate>Wed, 29 Dec 2021 14:00:00 GMT</pubDate><guid isPermaLink="true">https://joshuarogers.net/articles/2021-12/ill-be-home-for-christmas-quarantine-piano-edition/</guid></item><item><title>Less is More: How Degrowth Will Save the World</title><link>https://apurva-shukla.me/bookshelf/less-is-more/</link><description>⭐ ⭐ ⭐ ⭐ ⭐ This is one of those books that changes your theory of being.

I cannot recommend this enough, this book should be on everyone’s…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Wed, 29 Dec 2021 13:30:55 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/bookshelf/less-is-more/</guid></item><item><title>Book review - Foundations of Library and Information Science</title><link>https://river.me/blog/book-review-foundations-lis/</link><description>A review of &amp;ldquo;Foundations of Library and Information Science&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 29 Dec 2021 10:20:41 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-foundations-lis/</guid></item><item><title>Using Go on iOS in 2021</title><link>https://blog.nobugware.com/post/2021/using-go-on-ios-in-2021/</link><description>I&amp;rsquo;ve already posted about gomobile on this blog: &amp;ldquo;Using gomobile for real&amp;rdquo;.
For a lot of people it seems crazy to run Go on iOS and Android for some others it&amp;rsquo;s just routine, Zenly &amp;amp; Tailscale to name a few.
Since I&amp;rsquo;m reviving the idea for a geo database on iPhone here are some details.
I&amp;rsquo;ve started to port spatialite with the required dependencies to realize most of them were LGPL and incompatible with iOS distribution, so I went back to a simple solution using Go to display points on a map from a local database.</description><author>Fabrice Aneche</author><pubDate>Wed, 29 Dec 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.nobugware.com/post/2021/using-go-on-ios-in-2021/</guid></item><item><title>VR, VFIO and how latency ruined everything</title><link>https://ounapuu.ee/posts/2021/12/29/vr-vfio-latency/</link><description>Alternative title: "Honey, I completely changed my server setup again!"</description><author>./techtipsy</author><pubDate>Wed, 29 Dec 2021 06:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/12/29/vr-vfio-latency/</guid></item><item><title>The Lowest Common Denominator</title><link>https://jonpauluritis.com/articles/the-lowest-common-denominator/</link><description>&lt;p&gt;One of the dumbest things a person can ever say is: &lt;strong&gt;&amp;quot;People are dumb.&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No doubt you've heard it out there in a wild. I used to hear it all the time from sales reps, managers, ops people, etc. Now I hear it from software devs, PMs, technical writers, etc.&lt;/p&gt;
&lt;p&gt;They even have little cartoons for it, that they send around:&lt;/p&gt;
&lt;p&gt;&lt;img alt="users" src="https://jonpauluritis.com/img/users.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Everyone seems to think that these humans that they are working on behalf of are not capable... and that things need to be &amp;quot;dumbed down&amp;quot; for them. Brought down to a easier level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To the idea of &amp;quot;lowest common denominator,&amp;quot; that peope are dumb, I call bullshit.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;People are lazy, tired, and distracted. They are drunk, high, tripping, over/under caffeinated. They are are bombarded with requests for their focus/attention, their money, and their time. They have family, debt, pets, jobs, kids, coworkers, broken appliances, neigbhors, trees, and priests. They have racoons getting into their trash at night keeping them up, and robbers stealing rims in their neighborhood. They have a baby that got COVID-19 and a husband/wife with a medical condition that doctors don't understand. Their boss is getting a divorce and their kid is getting bullied at school.&lt;/p&gt;
&lt;p&gt;The thought that a &amp;quot;normal person&amp;quot; could be some sort of subject matter expert in whatever you're selling, or that they are able to operate in your world at the drop of a hat, no matter how &amp;quot;easy&amp;quot; it is, is laughable. &lt;strong&gt;People are actually pretty smart, they just have a lot of things in the way of them acting like it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now a decade ago I probably would have said the same thing as everyone else, but then something magical happened- I interacted with those &amp;quot;dumb&amp;quot; people that my coworkers talk about. Not just a handful either. From ~2009 to 2013 I talked to somewhere in the realm of ~13,000 to ~15,000 different people. And what I found: Most people are pretty smart. They aren't &amp;quot;Rhodes Scholar&amp;quot; smart (chances are neither are you) but they're smart enough.&lt;/p&gt;
&lt;p&gt;So whats all this business about serving the &amp;quot;lowest common denominator,&amp;quot; making sure you dumb things down??? In my book it's laziness.&lt;/p&gt;
&lt;p&gt;Here's what I'd recommend: instead of bringing the subject down to a place where even a corpse could understand you should try to bring people up to your level, or at least a place where they can operate. It isn't easy, and it's definitely a goal... but the world will be better for it. Your customers will thank you, and your users, clients, audience, whoever will thank you for it.&lt;/p&gt;
&lt;p&gt;I'm going to leave off with a quote from John Collison (cofounder of Stripe):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;That one of our principles when it comes to the marketing at Stripe is that we speak up to the reader. &lt;strong&gt;You’re not trying to dumb things down&lt;/strong&gt; for someone who isn’t familiar with something like this. You are speaking to an intelligent person who is busy, but knows what they’re talking about, knows what they’re doing. And it’s your job to kind of help educate them on this. And so we certainly made that a big part of the culture early on. And I think, I mean, like any culture, it’s self perpetuating. I think people for whom that’s important tend to be drawn to it.&lt;/p&gt;
&lt;/blockquote&gt;</description><author>JonPaulUritis.com</author><pubDate>Wed, 29 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://jonpauluritis.com/articles/the-lowest-common-denominator/</guid></item><item><title>4000 years</title><link>https://jonpauluritis.com/articles/4000years/</link><description>&lt;p&gt;My favorite Professor at KU, John Younger, was at Duke for about 25 years before coming to KU. He was their expert in classical studies (Greece, Rome, etc) and was considered a world expert. He left when they gave him a little clock to say thank you for the 25 years. For whatever reason he moved to Kansas. One time I was hanging out during office hours and he explained to me the difference between Duke and Kansas (paraphrased in my own words):&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;When I was at Duke everyone was very attentive, they were all on their way to be doctors and lawyers, etc they studied hard, but never stopped to look around at all the interesting things that they could learn... at Kansas I was sitting in my office during office hours and a random kid who grew up on a farm 200 miles away from any town I've ever heard of came in to ask me some questions about an ancient dialect of greek that hasn't been spoken in ~4000 years. The kid had taught himself how to read this language sitting on a farm in the middle of Kansas- making him one of maybe 100 people in the world that could read/write in this anchient language... I could have sat at Duke for 4000 years waiting for something like that to happen and it never would have. These things happen here...&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I think about that interaction every once in a while... there's a lot in there.&lt;/p&gt;</description><author>JonPaulUritis.com</author><pubDate>Wed, 29 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://jonpauluritis.com/articles/4000years/</guid></item><item><title>Go in Web Applications with WebAssembly</title><link>https://www.tderflinger.com/en/go-lang-in-javascript-web-applications-webassembly</link><description>With the advent of the new technology of WebAssembly, it is possible to leverage other languages than JavaScript in the browser. Go is one popular language that you can call from your JavaScript web application.
This article shows how to do that with TinyGo.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Wed, 29 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/go-lang-in-javascript-web-applications-webassembly</guid></item><item><title>Generative Art: Pen Plotting an Old Family Photo</title><link>https://www.csun.io/2021/12/29/plotting-old-pictures.html</link><description>Turning family history into art.</description><author>Cameron Sun</author><pubDate>Wed, 29 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.csun.io/2021/12/29/plotting-old-pictures.html</guid></item><item><title>Un petit retour sur l'AOC 2021</title><link>https://ache.one/articles/retour-sur-laoc-2021-semaine-1</link><description>L'Advent of Code 2021, le concept est simple. Chaque jour du calendrier de l'avent vous devez résoudre un exercice de programmation (dit des puzzles) qui comporte deux niveaux de difficulté. Tout ceci est mis en contexte puisque vous aidez les lutins du père Noël (ici des elfes, mais on France on parle de lutin !) à récupérer les clés du traîneau ... Oui le traîneau du père Noël a des clés, et un lutin les a fait tomber dans l'océan. Ne vous arrêtez pas déjà à la narration car ça va devenir encore plus improbable !</description><author>ache: Blog personnel</author><pubDate>Wed, 29 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ache.one/articles/retour-sur-laoc-2021-semaine-1</guid></item><item><title>Vim tip 2: indent/unindent lines</title><link>https://learnbyexample.github.io/tips/vim-tip-2/</link><description>&lt;p&gt;&lt;strong&gt;Normal mode&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;&amp;gt;&amp;gt;&lt;/kbd&gt; indent the current line&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;3&amp;gt;&amp;gt;&lt;/kbd&gt; indent the current line and two lines below (same as &lt;kbd&gt;2&amp;gt;j&lt;/kbd&gt;)&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;&amp;gt;k&lt;/kbd&gt; indent the current line and the line above (same as &lt;kbd&gt;1&amp;gt;k&lt;/kbd&gt; or &lt;kbd&gt;&amp;gt;1k&lt;/kbd&gt;)&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;&amp;lt;&amp;lt;&lt;/kbd&gt; unindent the current line&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;5&amp;lt;&amp;lt;&lt;/kbd&gt; unindent the current line and four lines below (same as &lt;kbd&gt;4&amp;lt;j&lt;/kbd&gt; or &lt;kbd&gt;&amp;lt;4j&lt;/kbd&gt;)&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;2&amp;lt;k&lt;/kbd&gt; unindent the current line and two lines above (same as &lt;kbd&gt;&amp;lt;2k&lt;/kbd&gt;)&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;=&lt;/kbd&gt; auto indent code, use motion commands to indicate the portion to be indented
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;=4j&lt;/kbd&gt; auto indents the current line and four lines below&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;=ip&lt;/kbd&gt; auto indents the current paragraph&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; You can use any motion command with &lt;kbd&gt;&amp;gt;&lt;/kbd&gt; and &lt;kbd&gt;&amp;lt;&lt;/kbd&gt;. For example, &lt;kbd&gt;&amp;gt;}&lt;/kbd&gt; indents till the end of the paragraph.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Visual mode&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;&amp;gt;&lt;/kbd&gt; indent the visually selected lines once&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;3&amp;gt;&lt;/kbd&gt; indent the visually selected lines three times&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;&amp;lt;&lt;/kbd&gt; unindent the visually selected lines once&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;=&lt;/kbd&gt; auto indent code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consider the following unindented code:&lt;/p&gt;
&lt;pre class="language-c " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-c"&gt;&lt;span style="color: #72ab00;"&gt;for&lt;/span&gt;&lt;span&gt;(i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;5&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;++&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;{
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;for&lt;/span&gt;&lt;span&gt;(j&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;=&lt;/span&gt;&lt;span&gt;i; j&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;10&lt;/span&gt;&lt;span&gt;; j&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;++&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;{
&lt;/span&gt;&lt;span&gt;statements
&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;span&gt;statements
&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's the result after applying &lt;kbd&gt;vip=&lt;/kbd&gt; (you can also use &lt;kbd&gt;=ip&lt;/kbd&gt; if you prefer Normal mode).&lt;/p&gt;
&lt;pre class="language-c " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-c"&gt;&lt;span style="color: #72ab00;"&gt;for&lt;/span&gt;&lt;span&gt;(i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;5&lt;/span&gt;&lt;span&gt;; i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;++&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;{
&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;for&lt;/span&gt;&lt;span&gt;(j&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;=&lt;/span&gt;&lt;span&gt;i; j&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;10&lt;/span&gt;&lt;span&gt;; j&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;++&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;span&gt;        statements
&lt;/span&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;span&gt;    statements
&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; Indentation depends on the &lt;code&gt;shiftwidth&lt;/code&gt; setting.&lt;/p&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; See &lt;a href="https://vimhelp.org/change.txt.html#shift-left-right"&gt;:h shift-left-right&lt;/a&gt;, &lt;a href="https://vimhelp.org/change.txt.html#%3D"&gt;:h =&lt;/a&gt; and &lt;a href="https://vimhelp.org/options.txt.html#%27shiftwidth%27"&gt;:h 'shiftwidth'&lt;/a&gt; for documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Video demo&lt;/strong&gt;:&lt;/p&gt;
&lt;p align="center"&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; See also my &lt;a href="https://github.com/learnbyexample/vim_reference"&gt;Vim Reference Guide&lt;/a&gt; and &lt;a href="https://learnbyexample.github.io/curated_resources/vim.html"&gt;curated list of resources for Vim&lt;/a&gt;.&lt;/p&gt;</description><author>learnbyexample</author><pubDate>Wed, 29 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/tips/vim-tip-2/</guid></item><item><title>Chat Server Protocol</title><link>https://mfashby.net/posts/2021-12-28-chat-server-protocol/</link><description/><author>Home on mfashby.net</author><pubDate>Wed, 29 Dec 2021 00:22:01 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-12-28-chat-server-protocol/</guid></item><item><title>My favourite books of 2021</title><link>https://gaganpreet.in/posts/books-of-2021/</link><description>&lt;p&gt;
&lt;img alt="Sunset from the Lighthouse of Genoa" src="https://gaganpreet.in/images/2021/books-of-2021-cover.jpg" /&gt;
&lt;/p&gt;
&lt;p&gt;This year I read more non-fiction than I usually do and I read &lt;a href="https://www.goodreads.com/user/year_in_books/2021/7515314" rel="noreferrer" target="_blank"&gt;32 books&lt;/a&gt; this year. I also wrote a similar &lt;a href="https://gaganpreet.in/posts/books-of-2020/" rel="noreferrer" target="_blank"&gt;list for 2020&lt;/a&gt;.&lt;/p&gt;</description><author>Gaganpreet Arora</author><pubDate>Tue, 28 Dec 2021 23:58:45 GMT</pubDate><guid isPermaLink="true">https://gaganpreet.in/posts/books-of-2021/</guid></item><item><title>Self-hosted email MUST remain "practical"</title><link>https://stop.zona-m.net/2021/12/self-hosted-email-must-remain-practical/</link><description>&lt;p&gt;You want your very own email server. Trust me on this.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 28 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/self-hosted-email-must-remain-practical/</guid></item><item><title>Devlog #6: async activities</title><link>https://domwillia.ms/devlog6</link><description>An overview of many months of churning the entity activity system to use Rust's async/await support, for a much nicer design.</description><author>Dom Williams</author><pubDate>Tue, 28 Dec 2021 14:00:00 GMT</pubDate><guid isPermaLink="true">https://domwillia.ms/devlog6</guid></item><item><title>Announcing rp2040_hal 1.0</title><link>https://synack.me/blog/announcing-rp2040_hal-1-0.html</link><description>rp2040_hal is a library of drivers for the Raspberry Pi RP2040 microcontroller written in Ada.</description><author>Jeremy Grosser</author><pubDate>Tue, 28 Dec 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://synack.me/blog/announcing-rp2040_hal-1-0.html</guid></item><item><title>Static HTTP share with S3 and Haskell</title><link>https://www.anardil.net/2021/static-http-share-with-s3-and-haskell.html</link><description>&lt;p&gt;Creating HTML index pages for a public S3 bucket with Haskell&lt;/p&gt;</description><author>Anardil</author><pubDate>Tue, 28 Dec 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://www.anardil.net/2021/static-http-share-with-s3-and-haskell.html</guid></item><item><title>Book Review: Consider the Lobster</title><link>https://nicolaiarocci.com/book-review-consider-the-lobster/</link><description>&lt;p&gt;I found a Consider the Lobster &lt;a href="https://www.goodreads.com/review/show/14143044"&gt;review&lt;/a&gt; on Goodreads that almost precisely
matches my thoughts on DFW and the book. Hence, given the lazy Christmas-break
mood I am in right now, I am conceding myself the right to copy-paste and edit
David&amp;rsquo;s review right away.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I know of nobody else who writes as thoughtfully and intelligently as DFW. That
he manages to write so informatively, with humor and genuine wit, on almost any
subject under the sun is mind-blowing – it&amp;rsquo;s also why I am willing to forgive
his occasional stylistic excesses. (Can you spell &amp;lsquo;footnote&amp;rsquo;?) You may not have
a strong interest in lobsters or pornography, but the essays in question are
terrific. The reporting on Ziegler and McCain is outstanding, heartbreakingly
so, because it makes the relative shallowness of most reporting painfully
evident.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 28 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-consider-the-lobster/</guid></item><item><title>useMousetrap()</title><link>https://maxleiter.com/notes/useMousetrap</link><description>&lt;p&gt;I've been messing around with a React implementation of The Lounge lately (for no real reason except that I can) and wanted a functional way to use &lt;a href="https://craig.is/killing/mice" rel="noopener noreferrer" target="_blank"&gt;Mousetrap&lt;/a&gt;, which TL already uses.  The below hook let me replace the Vue use of Mousetrap (which was pretty much vanilla JS) in a React-like fashion. For bonus typing, add `@types/mousetrap` to your project.&lt;/p&gt;
&lt;p&gt;Usage is pretty simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ts"&gt;useMousetrap(&amp;quot;escape&amp;quot;, (evt, combo) =&amp;gt; {
	evt.preventDefault
}, &amp;quot;keypress&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-ts"&gt;import mousetrap from &amp;quot;mousetrap&amp;quot;
import { useEffect, useRef } from &amp;quot;react&amp;quot;

const useMousetrap = (
	handlerKey: string | string[],
	handlerCallback: (evt: KeyboardEvent, combo: string) =&amp;gt; void,
	evtType?: &amp;quot;keypress&amp;quot; | &amp;quot;keydown&amp;quot; | &amp;quot;keyup&amp;quot;,
) =&amp;gt; {
	let actionRef = useRef(handlerCallback)

	useEffect(() =&amp;gt; {
		mousetrap.bind(
			handlerKey,
			(evt: KeyboardEvent, combo: string) =&amp;gt; {
				typeof actionRef.current === &amp;quot;function&amp;quot; &amp;amp;&amp;amp; actionRef.current(evt, combo)
			},
			evtType,
		)
		return () =&amp;gt; {
			mousetrap.unbind(handlerKey)
		}
	}, [evtType, handlerKey])
}

export default useMousetrap
&lt;/code&gt;&lt;/pre&gt;</description><author>Max Leiter</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://maxleiter.com/notes/useMousetrap</guid></item><item><title>2021 Year in Review</title><link>https://benovermyer.com/blog/2021/12/2021-year-in-review/</link><description>&lt;p&gt;2021 was an eventful year. The global pandemic was still a major problem, though vaccines finally showed up, and treatments started to become available at the end of the year. I quit my job at Solution Design Group after a golden opportunity presented itself, and took a 100% remote job with Apiture. Thus far, it has proven to be a fantastic job with a great group of people. The benefits are amazing.&lt;/p&gt;
&lt;p&gt;I think I finally gave up on the idea of resurrecting Silver Gryphon Games this year. Kevin doesn't need that distraction and my tendency to change focus for months at a time would make a company with that level of activity difficult to maintain on my own. It's possible that in the future I will consider starting a company with a smaller focus, but that's not likely to happen in 2022.&lt;/p&gt;
&lt;p&gt;Iron Arachne saw less development in the latter half of the year, but the first half was quite productive. I learned more about how concepts should be structured. In July, I rewrote the entire project in Typescript with Svelte. That was enlightening. I still don't care for Javascript generally, but Typescript at least makes it tolerable. I wish I didn't have to write the site in JS. However, the benefits of having a static site outweigh the drawbacks of writing in JS. I did end up building that planet generator I wrote about at the beginning of the year. I taught myself how to write GLSL shaders in the process.&lt;/p&gt;
&lt;p&gt;One of the biggest events of 2021 was that I got Lasik surgery in late September. While it has been fantastic to be able to see well without glasses, I'm still experiencing issues due either to healing or to side effects. If I focus on something close up for more than a few seconds, then I'll be unable to re-focus on things farther away for about half a minute. Also, when I'm tired, my distance vision is blurry enough that it's difficult to read signs. Still, absolutely worth it.&lt;/p&gt;
&lt;p&gt;We abandoned our idea of moving to Estonia for a variety of reasons. At first it was due to the difficulty involved in moving there with two cats. Over the course of the year, however, it became obvious that Russia's aggression in the area makes Estonia unsafe. Our new plan is much less disruptive: we're moving to North Carolina in the next couple months, and then in a few years, we'll move to Maine.&lt;/p&gt;
&lt;p&gt;I'm in worse shape physically at the end of 2021 than I was at the beginning, I think. I'm not sure because I stopped logging progress a couple years ago. That's changing now. I got an Apple TV for Christmas, and I intend to expand my usage of Fitness+ from occasional workouts using my iPhone to daily workouts using the TV. I'll talk more about that in my "looking forward" post in a few days.&lt;/p&gt;
&lt;p&gt;In the end, 2021 was a relatively positive year full of changes. The threat of Covid-19 still looms over everything, but I think there is light at the end of the tunnel now.&lt;/p&gt;</description><author>Ben Overmyer's Site</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://benovermyer.com/blog/2021/12/2021-year-in-review/</guid></item><item><title>Using {Blocks} in Rust &amp;amp; Go for Fun &amp;amp; Profit</title><link>https://taylor.town/code-blocks</link><description>&lt;p&gt;Did you know you can just whip out curly-braces all over the place in Rust and
Go?&lt;/p&gt;
&lt;pre&gt;&lt;code class="go language-go"&gt;a := 1
a = 2

{ b := 1 }
b := 2&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="rust language-rust"&gt;let mut a = 1;
a = 2;

{ let b = 1; };
let b = 2;

let c = {
  let mut d = 1;
  d = 2;
  d
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But why on Earth would you even want to do this?&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="creating-construction-zones"&gt;Creating "Construction-Zones"&lt;/h2&gt;
&lt;p&gt;When you're creating a variable that demands multiple initialization steps, it's
tider to tuck the details into a block:&lt;/p&gt;
&lt;pre&gt;&lt;code class="rust language-rust"&gt;// before
let person = Person(id);
person.authenticate(token).await;
let raw_email = person.fetchEmail().await;
let email = Email.parse(email);&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="rust language-rust"&gt;// after
let email = {
  let person = Person(id);
  person.authenticate(token).await;
  let raw_email = person.fetchEmail().await;
  Email.parse(email)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, it's obvious that the "end-goal" is &lt;code&gt;email&lt;/code&gt;. At a quick-glance,
you can see which variables are important, and which are intermediate ephemera.&lt;/p&gt;
&lt;h2 id="containing-side-effects-and-error-handling"&gt;Containing Side-Effects and Error-Handling&lt;/h2&gt;
&lt;p&gt;Every golang programmer knows the pain of juggling &lt;code&gt;result&lt;/code&gt; and &lt;code&gt;err&lt;/code&gt; variables.
With blocks, you can tidily handle each &lt;code&gt;err&lt;/code&gt; and other variables without
worrying about weird assignment problems with &lt;code&gt;:=&lt;/code&gt; and &lt;code&gt;=&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="go language-go"&gt;id := 123
firstName := "John"
lastName := "Smith"

{
  result, err := db.Exec(
    `UPDATE usr SET first_name = $2 WHERE id = $1`,
    id,
    firstName,
  )
  if result.RowsAffected() = 0 || err != nil {
    return err
  }
}

{
  result, err := db.Exec(
    `UPDATE usr SET last_name = $2 WHERE id = $1`,
    id,
    lastName,
  )
  if result.RowsAffected() = 0 || err != nil {
    return err
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="restricting-variable-lifetimes"&gt;Restricting Variable Lifetimes&lt;/h2&gt;
&lt;p&gt;I like creating tidy "workspaces" for each group of intermediate variables:&lt;/p&gt;
&lt;pre&gt;&lt;code class="rust language-rust"&gt;// define vars
// do stuff with vars
{
  // define intermediate vars
  // manipulate top-level vars
}
// do more stuff with vars
{
  // define intermediate vars
  // manipulate top-level vars
}
// do even more stuff with vars&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="go language-go"&gt;email := "j.smith@example.com"

usr := Usr{}
{
  var usrId int
  {
    err := db.QueryRow(
      `SELECT usr_id FROM usr_email WHERE email = $1`,
      email,
    ).Scan(&amp;amp;usrId)
    if err != nil {
      return err
    }
  }

  {
    err := db.QueryRow(
      `SELECT * FROM usr WHERE id = $1`,
      usrId,
    ).Scan(&amp;amp;usr)
    if err != nil {
      return err
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this very contrived example, you can easily see the purpose of everything
just by glancing at the general structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;email&lt;/code&gt; is at the beginning with an explicit value, so it's a given input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;usr&lt;/code&gt; is declared empty, so the block directly beneath it will define it&lt;/li&gt;
&lt;li&gt;&lt;code&gt;usrId&lt;/code&gt; is declared empty, so the block directly beneath it will define it&lt;/li&gt;
&lt;li&gt;&lt;code&gt;usr&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; are the only variables that we need to care about after this
section&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;{}&lt;/code&gt; blocks to prevent your variables from leaking everywhere.&lt;/p&gt;
&lt;p&gt;Your friends and coworkers will thank you.&lt;/p&gt;</description><author>taylor.town</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://taylor.town/code-blocks</guid></item><item><title>Why JavaScript's Secret Comma Operator is So Darn Useful</title><link>https://taylor.town/js-comma-operator</link><description>&lt;p&gt;Did you know you could do this in JS?&lt;/p&gt;
&lt;pre&gt;&lt;code class="js language-js"&gt;let n = (0, 1);

console.log(n); // 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this?&lt;/p&gt;
&lt;pre&gt;&lt;code class="js language-js"&gt;let n = 0;

n = (n++, n++, n);

console.log(n); // 2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this?&lt;/p&gt;
&lt;pre&gt;&lt;code class="js language-js"&gt;// 😒
if (x) {
  foo();
  return bar();
} else {
  return "baz";
}

// 😍
return x ? (foo(), bar()) : "baz";&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this?&lt;/p&gt;
&lt;pre&gt;&lt;code class="js language-js"&gt;// 😒
const f = x =&amp;gt; { x[0]=42; return x; };

// 😍
const g = x =&amp;gt; (x[0]=42, x);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, this seems pretty useless outside of &lt;a href="https://en.wikipedia.org/wiki/Code_golf"&gt;code golf&lt;/a&gt;. But I personally find it helpful for a few cases.&lt;/p&gt;
&lt;h2 id="quick-logging"&gt;Quick Logging&lt;/h2&gt;
&lt;p&gt;Sometimes I want to log something deep in the code without disturbing the general structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class="js language-js"&gt;// before
function f(x) {
  return x ? "left" : "right";
}

// after
function f(x) {
  return x ? (console.log("left", x), "left") : "right";
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without the comma-operator, we would've had to completely disassemble the ternary-statement into an equivalent if-statement.&lt;/p&gt;
&lt;h2 id="making-returnable-if-statements"&gt;Making Returnable If-Statements&lt;/h2&gt;
&lt;pre&gt;&lt;code class="js language-js"&gt;// before
let x;
if (n &amp;gt; 0) {
  f();
  x = "left";
} else {
  x = "right";
}

// after
const x = n &amp;gt; 0 
  ? (f(), "left") 
  : "right";&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's nice that the code is smaller, but the &lt;em&gt;real&lt;/em&gt; benefit of this example is that we don't need a mutable &lt;code&gt;x&lt;/code&gt; variable polluting our scope! I personally find the ternary much safer and easier to read here.&lt;/p&gt;</description><author>taylor.town</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://taylor.town/js-comma-operator</guid></item><item><title>A year in review - 123dev #52</title><link>https://justingarrison.com/123dev/2021-12-28-123dev-52/</link><description>It was not an easy year for many reasons. I have hopes 2022 will be better or at</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-12-28-123dev-52/</guid></item><item><title>V8 Heap pwn and /dev/memes - WebOS Root LPE</title><link>https://www.da.vidbuchanan.co.uk/blog/webos-wampage.html</link><author>David Buchanan's Blog</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.da.vidbuchanan.co.uk/blog/webos-wampage.html</guid></item><item><title>Writing a minimal Lua implementation with a virtual machine from scratch in Rust</title><link>http://notes.eatonphil.com/lua-in-rust.html</link><description>&lt;p&gt;By the end of this guide we'll have a minimal, working implementation
of a small part of Lua from scratch. It will be able to run the
following program (among others):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="kr"&gt;then&lt;/span&gt;
      &lt;span class="kr"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kr"&gt;end&lt;/span&gt;

   &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kr"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kr"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is my second project in Rust and only the third time I've
invented an instruction set so don't take my style as gospel. However,
I have found some Rust parsing tutorials overly complex so I'm hoping
you'll find this one simpler.&lt;/p&gt;
&lt;p&gt;All &lt;a href="https://github.com/eatonphil/lust"&gt;source code is available on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="entrypoint"&gt;Entrypoint&lt;/h3&gt;&lt;p&gt;Running &lt;code&gt;cargo init&lt;/code&gt; will give the boilerplate necessary. In
&lt;code&gt;src/main.rs&lt;/code&gt; we'll accept a file name from the command line, perform
lexical analysis to retrieve all tokens from the file, perform grammar
analysis on the tokens to retrieve a tree structure, compile the tree
to a linear set of virtual machine instructions, and finally interpret
the virtual machine instructions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;::&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;::&lt;span class="n"&gt;read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Could not read file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex&lt;/span&gt;::&lt;span class="n"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;::&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;::&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;eval&lt;/span&gt;::&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Easy peasy. Now let's implement &lt;code&gt;lex&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="lexical-analysis"&gt;Lexical analysis&lt;/h3&gt;&lt;p&gt;Lexical analysis drops whitespace (Lua is not whitespace
sensitive) and chunks all source code characters into their
smallest possible meaningful pieces like commas, numbers, identifiers,
keywords, etc.&lt;/p&gt;
&lt;p&gt;In order to have useful error messages, we'll keep track of state in
the file with a &lt;code&gt;Location&lt;/code&gt; struct that implements &lt;code&gt;increment&lt;/code&gt; and
&lt;code&gt;debug&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This goes in &lt;code&gt;src/lex.rs&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#[derive(Copy, Clone, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;increment&lt;/code&gt; function will update line and column numbers as well
as the current index in the file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the &lt;code&gt;debug&lt;/code&gt; function will dump the current line with a pointer in
text to the current column along with a message.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;Into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;[&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line_str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whole&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;discovering&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;line_str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;line_str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;space&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;{}\n\n{}\n{}^ Near here&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;space&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The smallest individual unit after lexical analysis is a token which
is either a keyword, number, identifier, operator, or syntax. (This
implementation is clearly skipping lots of real Lua syntax like
strings.)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#[derive(Debug, PartialEq, Eq, Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;TokenKind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug, Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;: &lt;span class="nc"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The top-level &lt;code&gt;lex&lt;/code&gt; function will iterate over the file and call a lex
helper for each kind of token, returning an array of all tokens on
success. In between lexing it will "eat whitespace".&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lexers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;lex_keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;lex_identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;lex_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;lex_syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;lex_operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="na"&gt;outer&lt;/span&gt;: &lt;span class="nc"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eat_whitespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lexers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;'outer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unrecognized character while lexing:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="whitespace"&gt;Whitespace&lt;/h4&gt;&lt;p&gt;Eating whitespace is just incrementing the location while we see a
space, tab, newline, etc.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;eat_whitespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\t'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\n'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="numbers"&gt;Numbers&lt;/h4&gt;&lt;p&gt;Lexing numbers iterates through the source starting at a position
until it stops seeing decimal digits (this implementation only
supports integers).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lex_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If there are no digits in the string then this is not a number.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="nc"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;: &lt;span class="nc"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;: &lt;span class="nc"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="identifiers"&gt;Identifiers&lt;/h4&gt;&lt;p&gt;Identifiers are any collection of alphabet characters, numbers, and
underscores.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lex_identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_alphanumeric&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'_'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But they cannot start with a number.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// First character must not be a digit&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;is_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="nc"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;: &lt;span class="nc"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;: &lt;span class="nc"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="keywords"&gt;Keywords&lt;/h4&gt;&lt;p&gt;Keywords are alphabetical like identifiers are but they cannot be
reused as variables by the user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lex_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;function&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;if&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;then&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;local&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;return&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="na"&gt;outer&lt;/span&gt;: &lt;span class="nc"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_alphanumeric&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'_'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;'outer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Not a complete match&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// If it got to this point it found a match, so exit early.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// We don't need a longest match.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Aside from matching a list of strings we have to make sure
there is a complete match. For example &lt;code&gt;function1&lt;/code&gt; is not a keyword,
it's a valid identifier. Whereas &lt;code&gt;function 1&lt;/code&gt; is a valid set of tokens
(the keyword &lt;code&gt;function&lt;/code&gt; and the number &lt;code&gt;1&lt;/code&gt;), even if it's not a valid
Lua grammar.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// If the next character would be part of a valid identifier, then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// this is not a keyword.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_alphanumeric&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'_'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="nc"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;: &lt;span class="nc"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;: &lt;span class="nc"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="syntax"&gt;Syntax&lt;/h4&gt;&lt;p&gt;Syntax (in this context) is just language junk that isn't
operators. Things like commas, parenthesis, etc.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lex_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// TODO: this won't work with multiple-character syntax bits like &amp;gt;= or ==&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="nc"&gt;possible_syntax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;: &lt;span class="nc"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;: &lt;span class="nc"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="operators"&gt;Operators&lt;/h4&gt;&lt;p&gt;Operators are things like plus, minus, and less than
symbols. Operators are syntax but it helps us later on to break these
out into a seperate type of token.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lex_operator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;: &lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;operators&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;+&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;operators&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// TODO: this won't work with multiple-character operators like &amp;gt;= or ==&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;possible_syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="nc"&gt;possible_syntax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;: &lt;span class="nc"&gt;initial_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;: &lt;span class="nc"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;next_loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And now we're all done lexing!&lt;/p&gt;
&lt;h3 id="grammar-analysis"&gt;Grammar analysis&lt;/h3&gt;&lt;p&gt;Parsing finds grammatical (tree) patterns in a flat list of
tokens. This is called a syntax tree or abstract syntax tree (AST).&lt;/p&gt;
&lt;p&gt;The boring part is defining the tree. Generally speaking (and
specifically for this project), the syntax tree is a list of
statements. Statements can be function definitions or expression
statements or if statements or return statements or local
declarations.&lt;/p&gt;
&lt;p&gt;This goes in &lt;code&gt;src/parse.rs&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Statement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Ast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There's almost nothing special at all about the rest of the tree
definitions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Literal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;FunctionCall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;: &lt;span class="nc"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;BinaryOperation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;: &lt;span class="nc"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;FunctionCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionCall&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;BinaryOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BinaryOperation&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;: &lt;span class="nc"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;: &lt;span class="nc"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;: &lt;span class="nc"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;: &lt;span class="nc"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;: &lt;span class="nc"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's it for the AST!&lt;/p&gt;
&lt;h4 id="some-helpers"&gt;Some helpers&lt;/h4&gt;&lt;p&gt;Lastly before the fun part, we'll define a few helpers for validating
each kind of token.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expect_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Keyword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expect_identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now on to the fun part, actually detecting these trees!&lt;/p&gt;
&lt;h4 id="top-level-parse"&gt;Top-level parse&lt;/h4&gt;&lt;p&gt;The top-level &lt;code&gt;parse&lt;/code&gt; function and it's major helper,
&lt;code&gt;parse_statement&lt;/code&gt;, dispatch very similarly to the top-level lex
function. For each statement in the file we look for function
declarations, if statements, return statements, local declarations,
and expression statements.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parse_if&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parse_expression_statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parse_return&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parse_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parse_local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ntokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Invalid token while parsing:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="expression-statements"&gt;Expression statements&lt;/h4&gt;&lt;p&gt;Expression statements are just a wrapper for the Rust type
system. They call &lt;code&gt;parse_expression&lt;/code&gt; (which we'll define shortly),
expect a semicolon afterward, and wrap the expression in a statement.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_expression_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected semicolon after expression:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past semicolon&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="expressions"&gt;Expressions&lt;/h4&gt;&lt;p&gt;Expressions in this minimal Lua are only one of function calls,
literals (numbers, identifiers), or binary operations. To keep things
very simple, binary operations cannot be combined. So instead of &lt;code&gt;1 +
2 + 3&lt;/code&gt; we'd need to do &lt;code&gt;local tmp1 = 1 + 2; local tmp2 = tmp1 + 3;&lt;/code&gt;
and so on.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;::&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If what follows the first literal is an open parenthesis then we try
to parse a function call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past open paren&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Function call&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We need to call &lt;code&gt;parse_expression&lt;/code&gt; recursively for every possible
argument passed to the function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected comma between function call arguments:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past comma&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid expression in function call arguments:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past closing paren&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;FunctionCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionCall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;: &lt;span class="nc"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Otherwise if there isn't an opening parenthesis then we could be
parsing either a literal expression or a binary operation. If the
token that follows is an operator token then we know it's a binary
operation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Might be a literal expression&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Operator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Otherwise is a binary operation&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past op&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid right hand side binary operand:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rtoken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is at this point that we &lt;em&gt;could&lt;/em&gt; (but won't) call
&lt;code&gt;parse_expression&lt;/code&gt; recursively. I don't want to deal with operator
precedence right now so we'll just require that the right hand side is
another literal.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rtoken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;::&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rtoken&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TokenKind&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rtoken&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;rtoken&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid right hand side binary operand:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past right hand operand&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;BinaryOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BinaryOperation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;: &lt;span class="nb"&gt;Box&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;: &lt;span class="nc"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And now we're done parsing expressions!&lt;/p&gt;
&lt;h4 id="function-declarations"&gt;Function declarations&lt;/h4&gt;&lt;p&gt;Functions start with the &lt;code&gt;function&lt;/code&gt; keyword, and an identifier token follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;function&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid identifier for function name:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After the function name comes the argument list that can be empty or a
comma separated list of identifiers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past name&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected open parenthesis in function declaration:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past open paren&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected comma or close parenthesis after parameter in function declaration:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past comma&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past param&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past close paren&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we parse all statements in the function body until we find the
&lt;code&gt;end&lt;/code&gt; keyword.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;statements&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid statement in function declaration:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past end&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;: &lt;span class="nc"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Phew! We're halfway through the parser.&lt;/p&gt;
&lt;h4 id="return-statements"&gt;Return statements&lt;/h4&gt;&lt;p&gt;Return statements just check for the &lt;code&gt;return&lt;/code&gt; keyword, an expression,
and a semicolon.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;return&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past return&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_none&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid expression in return statement:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected semicolon in return statement:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past semicolon&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;: &lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="local-declarations"&gt;Local declarations&lt;/h4&gt;&lt;p&gt;Local declarations start with the &lt;code&gt;local&lt;/code&gt; keyword, then the local
name, then an equal sign, then an expression, and then a semicolon.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;local&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past local&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid identifier for local name:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past name&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected = syntax after local name:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past =&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_none&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected valid expression in local declaration:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_next_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;expect_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected semicolon in return statement:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Skip past semicolon&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;: &lt;span class="nc"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;next_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="if-statements"&gt;If statements&lt;/h4&gt;&lt;p&gt;This implementation of Lua doesn't support &lt;code&gt;elseif&lt;/code&gt; so parsing &lt;code&gt;if&lt;/code&gt;
just checks for the &lt;code&gt;if&lt;/code&gt; keyword followed by a test expression, then
the &lt;code&gt;else&lt;/code&gt; keyword, then the if body (a list of statements), and then the
&lt;code&gt;end&lt;/code&gt; keyword.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="sx"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;parse_if(raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;&amp;amp;[char],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;&amp;amp;[Token],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;usize)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;Option&amp;lt;(Statement,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;usize)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;!expect_keyword(tokens,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;index,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;if&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;None;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;1;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;Skip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;past&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;if&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;parse_expression(raw,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;tokens,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_index);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;res.is_none()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;println!(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sx"&gt;tokens[next_index]&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="sx"&gt;.loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="sx"&gt;.debug(raw,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Expected valid expression for if test:&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;None;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;(test,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_next_index)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;res.unwrap();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_next_index;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;!expect_keyword(tokens,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_index,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;then&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;None;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;+&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;1;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;Skip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;past&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;then&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;Vec&amp;lt;Statement&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;vec![];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;!expect_keyword(tokens,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_index,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;end&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;parse_statement(raw,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;tokens,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_index);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;Some((stmt,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_next_index))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;res&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sx"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;next_next_index;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sx"&gt;statements.push(stmt);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sx"&gt;println!(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="sx"&gt;tokens[next_index]&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="sx"&gt;.loc&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="sx"&gt;.debug(raw,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Expected valid statement in if body:&amp;quot;&lt;/span&gt;&lt;span class="sx"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sx"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;None;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;next_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;+&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;1;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;Skip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;past&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;end&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;Some((&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nl"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nl"&gt;If(If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sx"&gt;test,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sx"&gt;statements,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;}),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sx"&gt;next_index,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sx"&gt;))&lt;/span&gt;
&lt;span class="sx"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And goshdarnit we're done parsing.&lt;/p&gt;
&lt;h3 id="compiling-to-a-made-up-virtual-machine"&gt;Compiling to a made up virtual machine&lt;/h3&gt;&lt;p&gt;This virtual machine will be entirely stack-based other than the stack
pointer and program counter.&lt;/p&gt;
&lt;p&gt;The calling convention is that the caller will put arguments on the
stack followed by the frame pointer, the program counter, and then the
number of arguments (for cleanup). Then it will alter the program
counter and frame pointer. Then the caller will allocate space on the
stack for all arguments and locals within the function.&lt;/p&gt;
&lt;p&gt;For simplicity in addressing modes, the function declaration once
jumped to will copy the arguments from before the frame pointer to in
front of it (yes I know, I know, this is silly).&lt;/p&gt;
&lt;p&gt;The virtual machine will support add, subtract, less than operations
as well as jump, jump-if-not-zero, return, and call. It will support a
few more memory-specific instructions for loading literals, loading
identifiers, and managing arguments.&lt;/p&gt;
&lt;p&gt;I'll explain the non-obvious instructions as we implement them.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;::&lt;span class="n"&gt;parse&lt;/span&gt;::&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;collections&lt;/span&gt;::&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Instruction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;DupPlusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;MoveMinusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;MovePlusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;JumpIfNotZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Subtract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;LessThan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result of compiling will be a &lt;code&gt;Program&lt;/code&gt; instance. This instance
will contain symbol information and the actual instructions to run.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;: &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;: &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Compiling, similar to parsing, just calls the helper
&lt;code&gt;compile_statement&lt;/code&gt; for each statement in the AST.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;: &lt;span class="nc"&gt;Ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;: &lt;span class="nc"&gt;HashMap&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;compile_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;code&gt;compile_statement&lt;/code&gt; dispatches to additional helpers based on the
kind of statement.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;: &lt;span class="nc"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compile_declaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compile_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compile_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compile_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;::&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="function-declarations"&gt;Function declarations&lt;/h4&gt;&lt;p&gt;Let's do the hard one first. First off, function declarations will
include an unconditional guard around them so that we can evaluate
from the 0th instruction at the top-level and have only
non-function-declaration statements be evaluated.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_declaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;: &lt;span class="nc"&gt;FunctionDeclaration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Jump to end of function to guard top-level&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;done_label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;function_done_{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we'll add another limitation/simplification that local variables
are only accessible within the current function scope.&lt;/p&gt;
&lt;p&gt;For each parameter, we'll copy the parameter on the stack before the
frame pointer to a place in front of the frame pointer. This gets
around addressing mode limitations in our virtual machine.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_locals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;function_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;MoveMinusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;new_locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we compile the body.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;compile_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once the body is compiled we know the total number of locals so we can
fill out the symbol table correctly. The location is importantly
already stored because it is the location of the instruction where the
function started.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;: &lt;span class="nc"&gt;function_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;: &lt;span class="nc"&gt;new_locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally we add a symbol linking the done label for the function to
the position of the end of the function. Again, this allows us to skip
past the function declaration when evaluating instructions from 0 to
N.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;done_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;: &lt;span class="nc"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ok that wasn't so bad. And the rest are simpler.&lt;/p&gt;
&lt;h4 id="local-declarations"&gt;Local declarations&lt;/h4&gt;&lt;p&gt;The expression for the local is compiled and then the local name is
stored in a locals table mapped to the current number of locals
(including arguments). This allows the compiler to turn &lt;code&gt;identifier&lt;/code&gt;
token lookups into simply an offset from the frame pointer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;: &lt;span class="nc"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;MovePlusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And specifically, the instruction pattern is to evaluate the
expression and then copy it back into a relative position in the
stack.&lt;/p&gt;
&lt;h4 id="literals"&gt;Literals&lt;/h4&gt;&lt;p&gt;Number literals use the &lt;code&gt;store&lt;/code&gt; instruction for pushing a number onto
the stack. Identifier literals are copied to the top of the stack from
their position relative to the frame pointer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;lit&lt;/span&gt;: &lt;span class="nc"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;::&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;::&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Literal&lt;/span&gt;::&lt;span class="n"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;DupPlusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="function-calls"&gt;Function calls&lt;/h4&gt;&lt;p&gt;Pretty simple: just compile all the arguments and then issue a call
instruction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_function_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt;: &lt;span class="nc"&gt;FunctionCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="binary-operations"&gt;Binary operations&lt;/h4&gt;&lt;p&gt;Binary operations compile the left, then the right, and then issue an
instruction based on the operator. All the operators are builtin and
act on the top two elements on the stack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_binary_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;: &lt;span class="nc"&gt;BinaryOperation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;+&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Subtract&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;LessThan&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unable to compile binary operation:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="expressions"&gt;Expressions&lt;/h4&gt;&lt;p&gt;Compiling expressions just dispatches to a compile helper based on the
type of expression. We've already written those three helpers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;: &lt;span class="nc"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;BinaryOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;compile_binary_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;FunctionCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;compile_function_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;::&lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;compile_literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="if"&gt;If&lt;/h4&gt;&lt;p&gt;First we compile the conditional test and then we jump to after the if
the test result is not zero.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;if_&lt;/span&gt;: &lt;span class="nc"&gt;If&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;done_label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;if_else_{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;JumpIfNotZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;done_label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we compile the body.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;if_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;compile_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally make sure we insert the &lt;code&gt;done&lt;/code&gt; symbol in the right place after the if.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;done_label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;: &lt;span class="nc"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="return"&gt;Return&lt;/h4&gt;&lt;p&gt;The final statement type is return. We simply compile the return
expression and issue a return instruction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;: &lt;span class="nc"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;compile_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it for the compiler! Now the trickiest part. I lost a few hours
debugging and iterating on the next bit.&lt;/p&gt;
&lt;h3 id="the-virtual-machine"&gt;The virtual machine&lt;/h3&gt;&lt;p&gt;Ok so the easy part is that there are only two registers, a program
counter and a frame pointer. There's also a data stack. The frame
pointer points to the location on the data stack where each function
can start storing its locals.&lt;/p&gt;
&lt;p&gt;Evaluation starts from 0 and goes until the last instruction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;: &lt;span class="nc"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;: &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each instruction will be responsible for incrementing the program
counter or having it jump around.&lt;/p&gt;
&lt;h4 id="addition,-subtraction,-less-than"&gt;Addition, subtraction, less than&lt;/h4&gt;&lt;p&gt;The easiest ones are the math operators. We just pop off the data
stack, perform the operation, and store the result.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Subtract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;LessThan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;store&lt;/code&gt; instruction is another easy one. It just pushes a literal
number onto the stack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="jump-variants"&gt;Jump variants&lt;/h4&gt;&lt;p&gt;The jump variants are easy too. Just grab the location and change the
program counter. If it's a conditional jump then test the condition
first.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;JumpIfNotZero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="loading-from-a-variable"&gt;Loading from a variable&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;MovePlusFP&lt;/code&gt; instruction copies a value from the stack (offset the
frame pointer) onto the top of the stack. This is for references to
arguments and locals.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;MovePlusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Accounts for top-level locals&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="storing-locals"&gt;Storing locals&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;DupPlusFP&lt;/code&gt; instruction is used by &lt;code&gt;compile_locals&lt;/code&gt; to store a
local once compiled onto the stack in the relative position from the
frame pointer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;DupPlusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="duplicating-arguments"&gt;Duplicating arguments&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;MoveMinusFP&lt;/code&gt; instruction is, again, a hack to work around limited
addressing modes in this minimal virtual machine. It copies
arguments from behind the frame pointer to in front of the frame
pointer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;MoveMinusFP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fp_offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local_offset&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp_offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we're down to the last two instructions: call and return.&lt;/p&gt;
&lt;h4 id="call"&gt;Call&lt;/h4&gt;&lt;p&gt;Call has a special dispatch for builtin functions (the only one that
exists being &lt;code&gt;print&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Handle builtin functions&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;print&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..*&lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="fm"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="fm"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Otherwise it pushes the current frame pointer, then the program
counter, and finally the number of arguments (not locals) onto the
stack for preservation. Then it sets up the new program counter and
frame pointer and creates space for all locals and arguments after the
new frame pointer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Set up space for all arguments/locals&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pgrm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;syms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;nlocals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="return"&gt;Return&lt;/h4&gt;&lt;p&gt;The return instructions pops the return value from the stack. Then it
pops off all locals and arguments. Then it restores the program
counter and frame pointer, and pops off the arguments before the frame
pointer. Finally it adds the return value back onto the stack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Instruction&lt;/span&gt;::&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Clean up the local stack&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Restore pc and fp&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Clean up arguments&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;narguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// Add back return value&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And yes, this implementation would be more efficient if instead of
literally pushing and popping we just incremented/decremented a
stack pointer.&lt;/p&gt;
&lt;p&gt;And that's it! We're completely done a basic parser, compiler, and
virtual machine for a subet of Lua. Is it janky? Yeah. Is it simple?
Kind of? Does it work? It seems to!&lt;/p&gt;
&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;&lt;p&gt;Ok we've got &amp;lt;1200 lines of Rust enough to run some decent Lua
programs. We run this fib program against this implementation and
against Lua 5.4.3 (which isn't LuaJIT) and what do we see?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cargo&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--release
$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;test/fib.lua
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;end

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;n1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;n-1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;n2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;n-2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n1&lt;span class="w"&gt; &lt;/span&gt;+&lt;span class="w"&gt; &lt;/span&gt;n2&lt;span class="p"&gt;;&lt;/span&gt;
end

print&lt;span class="o"&gt;(&lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./target/release/lust&lt;span class="w"&gt; &lt;/span&gt;test/fib.lua
&lt;span class="m"&gt;832040&lt;/span&gt;
./target/release/lust&lt;span class="w"&gt; &lt;/span&gt;test/fib.lua&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.29s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.00s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.293&lt;span class="w"&gt; &lt;/span&gt;total
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;lua&lt;span class="w"&gt; &lt;/span&gt;test/fib.lua
&lt;span class="m"&gt;832040&lt;/span&gt;
lua&lt;span class="w"&gt; &lt;/span&gt;test/fib.lua&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.06s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.00s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.063&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This implementation is a bit slower! Time to do some profiling and maybe
revisit some of those aforementioned inefficiencies.&lt;/p&gt;
&lt;p class="note"&gt;
  Big thanks to &lt;a href="https://twitter.com/christianfscott/status/1475832498663792640"&gt;Christian
  Scott on Twitter&lt;/a&gt; for pointing out I should not be benchmarking
  with debug builds!
  &lt;br /&gt;&lt;br /&gt;
  And thanks
  to &lt;a href="https://www.reddit.com/r/rust/comments/rqgm8t/comment/hqbwgwj/"&gt;reddit123123123123
  on Reddit&lt;/a&gt; for suggesting I use &lt;code&gt;cargo clippy&lt;/code&gt; to
  clean up my code.
  &lt;br /&gt;&lt;br /&gt;
  Thanks to &lt;a href="https://github.com/eatonphil/lust/issues/1"&gt;GiffE
  on Github&lt;/a&gt; for pointing out some key inconsistencies between this
  implementation and Lua. I won't modify anything because a perfect
  Lua subset wasn't the goal, but I'm sharing because it was good
  analysis and criticism of this implementation.
&lt;/p&gt;&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;I wrote a new blog post on parsing, compiling, and virtual machine evaluation for a super minimal Lua implementation written from scratch in Rust!&lt;a href="https://t.co/8qFviEecJo"&gt;https://t.co/8qFviEecJo&lt;/a&gt; &lt;a href="https://t.co/d1MGArlErR"&gt;pic.twitter.com/d1MGArlErR&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1475828516835008513?ref_src=twsrc%5Etfw"&gt;December 28, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Tue, 28 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/lua-in-rust.html</guid></item><item><title>Drones, bringing MAD to every neighborhood</title><link>https://stop.zona-m.net/2021/12/drones-bringing-mad-to-every-neighborhood/</link><description>&lt;p&gt;and the only protection may be to bring sanity to the same neighborhoods.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 27 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/drones-bringing-mad-to-every-neighborhood/</guid></item><item><title>Why TurboRepo Will Be The First Big Trend of 2022</title><link>https://www.swyx.io/turborepo-why</link><description>TurboRepo is a big deal for the JS community because it addresses the monorepo problem head on, bringing 85% faster build speeds and great architecture/docs/marketing.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 27 Dec 2021 18:08:03 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/turborepo-why</guid></item><item><title>Update to Portals</title><link>https://rjp.is/blogging/posts/2021/12/update-to-portals/</link><description>In which we update our portal finding.</description><author>infrequent oscillations</author><pubDate>Mon, 27 Dec 2021 11:50:49 GMT</pubDate><guid isPermaLink="true">https://rjp.is/blogging/posts/2021/12/update-to-portals/</guid></item><item><title>System Shock is Back Home</title><link>https://nicolaiarocci.com/system-shock-is-back-home/</link><description>&lt;p&gt;When The Digital Antiquarian released his &lt;a href="https://www.filfre.net/2021/03/system-shock/"&gt;System Shock retrospective&lt;/a&gt;
earlier this year, I was in awe. System Shock was one of my favorite games back
in the day, and yes, in the quarrel between id Software&amp;rsquo;s DOOM and Looking
Glass&amp;rsquo;s System Shock, I was siding with the latter. I was so much more for
immersion and storyline than shoot &amp;rsquo;em-ups. The Antiquarian article is
excellent. If you&amp;rsquo;re into gaming history or, really, into computer&amp;rsquo;s history,
I urge you to read it all.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 27 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/system-shock-is-back-home/</guid></item><item><title>Brute-Forcing 22 Trillion Parameters</title><link>https://mattkeeter.com/blog/2021-12-27-brute</link><description/><author>Matt Keeter</author><pubDate>Mon, 27 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://mattkeeter.com/blog/2021-12-27-brute</guid></item><item><title>Getting started with Arch on a Macbook pro 2012 (9,2)</title><link>https://xnacly.me/posts/2021/macbookpro_guide/</link><description>A guide to wifi, touchpad support and more on your macbook pro 2012 using arch</description><author>xnacly - blog</author><pubDate>Mon, 27 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://xnacly.me/posts/2021/macbookpro_guide/</guid></item><item><title>Thinking out loud</title><link>https://grady.io/thinking-out-loud/</link><description>Thinking out loud is useful, but why?</description><author>Grady Simon RSS Feed</author><pubDate>Mon, 27 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://grady.io/thinking-out-loud/</guid></item><item><title>Lessons learned from 123dev newsletter</title><link>https://justingarrison.com/blog/2021-12-27-lessons-learned-from-123dev/</link><description>One year of failures and tips for new and existing newsletters</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 27 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/blog/2021-12-27-lessons-learned-from-123dev/</guid></item><item><title>Dangerous MVPs</title><link>https://www.danstroot.com/posts/2021-12-27-dangerous-mvps</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/sosa.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;When building a new product, we all know we should remain agile and launch something small and simple to start. Then, over time, we can make that product more functional by listening to our customers and adding more features. Unfortunately, the same process wreaks havoc when it comes to modernizing an existing system. At first, the reason why is not intuitive. In theory, the same principles should apply.&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-12-27-dangerous-mvps"&gt;Dangerous MVPs&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Mon, 27 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-12-27-dangerous-mvps</guid></item><item><title>Using Datasette for an ELT Personal Twitter Data Warehouse</title><link>https://www.swyx.io/datasette-twitter</link><description>EDA tools like Datasette dramatically lower the cost of data analysis, with a surprisingly simple ELT contract - You handle the Extract phase, it handles the Load, and exposes a standard UI for you to do dynamic Transforms.</description><author>swyx's site RSS Feed</author><pubDate>Sun, 26 Dec 2021 22:52:14 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/datasette-twitter</guid></item><item><title>The best food delivery programs for the digital age...</title><link>https://stop.zona-m.net/2021/12/the-best-food-delivery-programs-for-the-digital-age.../</link><description>&lt;p&gt;Are the LEAST digital of all.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 26 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/the-best-food-delivery-programs-for-the-digital-age.../</guid></item><item><title>The Matrix Resurrections</title><link>https://olshansky.info/movie/the_matrix_resurrections/</link><description>Olshansky's review of The Matrix Resurrections</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 26 Dec 2021 15:42:16 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/the_matrix_resurrections/</guid></item><item><title>Book Review: About the Meaning of Life</title><link>https://nicolaiarocci.com/book-review-about-the-meaning-of-life/</link><description>&lt;p&gt;I&amp;rsquo;m not a regular philosophy reader, much less of self-improvement guides. I&amp;rsquo;m
wary of the latter and too ignorant for the former. Yet, theologian Vito
Mancuso has intrigued me for some time. I followed his podcast on the &amp;ldquo;Four
Masters of Life&amp;rdquo;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; and found it excellent. In it, Mancuso discusses his four
tutelary deities: &amp;ldquo;Socrates, the educator. Buddha, the physician. Confucius,
the politician. Jesus, the prophet.&amp;rdquo; I also listened to some TV interviews
where I always found him fascinating. In short, there was enough to make me
decide to read some of his work. From the back cover of the book:&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 26 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-about-the-meaning-of-life/</guid></item><item><title>Loyal Citizens Being Blown into the Sky</title><link>https://nonhuman.party/post/naas_would_save_people_from_being_blown_into_sky/</link><description>Tornadoes are big and scary; and they’re beyond the control of ordinary civilians, so it’s the perfect scenario for helpful government intervention. It&amp;rsquo;s existential threats like these that remind us why we need governments; but perhaps too it serves as a prompt to consider if we could replace governments with something else.</description><author>Non-Human Party</author><pubDate>Sun, 26 Dec 2021 05:59:05 GMT</pubDate><guid isPermaLink="true">https://nonhuman.party/post/naas_would_save_people_from_being_blown_into_sky/</guid></item><item><title>Yamaha DX7 Firmware ROM Disassembly</title><link>https://ajxs.me/blog/Yamaha_DX7_Firmware_ROM_Disassembly.html</link><description>A brief update regarding the reverse-engineering of the Yamaha DX7's firmware ROM.</description><author>ajxs.me</author><pubDate>Sat, 25 Dec 2021 03:40:31 GMT</pubDate><guid isPermaLink="true">https://ajxs.me/blog/Yamaha_DX7_Firmware_ROM_Disassembly.html</guid></item><item><title>My 2021 reads</title><link>https://www.alexanderlolis.com/my-2021-reads</link><description>On A Life Well Spent: Cicero's De Senectute</description><author>Alexander's Blog Blog</author><pubDate>Sat, 25 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.alexanderlolis.com/my-2021-reads</guid></item><item><title>Actually Creating An Idempotency Keys Gem</title><link>http://live.julik.nl/2021/12/actually-creating-an-idempotency-keys-gem</link><description/><author>julik live</author><pubDate>Sat, 25 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://live.julik.nl/2021/12/actually-creating-an-idempotency-keys-gem</guid></item><item><title>Things I worked on in 2021</title><link>https://gilmi.me/post/2021/12/25/my-2021</link><description>Writing, streaming and OSS - A recap of the things I worked on in 2021.</description><author>λm.me</author><pubDate>Sat, 25 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://gilmi.me/post/2021/12/25/my-2021</guid></item><item><title>Christmas 2021</title><link>https://lambdaland.org/posts/2021-12-25_christmas/</link><description>&lt;p&gt;It’s my first Christmas being a dad. The end of the school semester was unusually stressful; I didn’t have much time or energy to anticipate Christmas. But I’ve felt a marked lack of eagerness for &lt;em&gt;getting&lt;/em&gt; presents. That feeling wanes each year—something I’m grateful for—but this year I only felt an eagerness for a time of peace and celebration with my family. Peaceful it has been.&lt;/p&gt;
&lt;p&gt;My family isn’t perfect. (My baby daughter is about as perfect as they come, though!) The time I’ve gotten to spend so far and the time that I will spend with them is precious to me. My wife and I have focused so much on celebrating the birth of Jesus Christ that this commercialized junk we put up with has been all but absent from our home. We have a few books about Santa Claus that we read to our daughter, but we’ve listened to strictly Christ-centered music. (&lt;em&gt;A Charlie Brown Christmas&lt;/em&gt; counts, though!) There’s been a spirit in our home that has brought me comfort and peace in this tumultuous year—more than any before it.&lt;/p&gt;
&lt;p&gt;Merry Christmas—may the peace of God be with you.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sat, 25 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-12-25_christmas/</guid></item><item><title>Probabilistic spin glass - Part III</title><link>https://bytepawn.com/probabilistic-spin-glass-part-iii.html</link><description>&lt;p&gt;I run simulations to understand the dynamic probabilistic evolution of these toy models. &lt;br /&gt;&lt;br /&gt;&lt;img alt="." src="/images/prob_spin_glass_p3_fractions_curve.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 25 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/probabilistic-spin-glass-part-iii.html</guid></item><item><title>Sherlock Holmes would love rationing</title><link>https://stop.zona-m.net/2021/12/sherlock-holmes-would-love-rationing/</link><description>&lt;p&gt;As he said, when you have eliminated all which is impossible&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 24 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/sherlock-holmes-would-love-rationing/</guid></item><item><title>Spiral Again?</title><link>https://blog.herlein.com/post/spiral-again/</link><description>&lt;p&gt;Merry Christmas Eve!  Playing with the Beaglebone sends me into another thought spiral.&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Fri, 24 Dec 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/spiral-again/</guid></item><item><title>Data cooperatives would not give EVERYBODY more power</title><link>https://stop.zona-m.net/2021/12/data-cooperatives-would-not-give-everybody-more-power/</link><description>&lt;p&gt;Just ask Kim Kardashian.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 23 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/data-cooperatives-would-not-give-everybody-more-power/</guid></item><item><title>Eggnog &amp;amp; Cola</title><link>https://dustin.lammiman.ca/icecream/eggnong-coke/</link><description>&lt;h2 id="eggnog-ice-cream" tabindex="-1"&gt;Eggnog Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/eggnong-coke/#eggnog-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/eggnong-coke/#ingredients"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1/2 Cup Granulated Sugar&lt;/li&gt;
&lt;li&gt;2 Tbsp Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;1 Tsp Ground Nutmeg&lt;/li&gt;
&lt;li&gt;1 Tbsp Imitation Rum Extract&lt;/li&gt;
&lt;li&gt;1 1/3 Cups Milk (I used 2%)&lt;/li&gt;
&lt;li&gt;2 Tbsp Light Corn Syrup&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;li&gt;3 Large Egg Yolks&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/eggnong-coke/#instructions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;In a bowl whisk together sugar, milk powder, nutmeg, and xanthan gum.&lt;/li&gt;
&lt;li&gt;In a pot add milk, whipping cream, and corn syrup.&lt;/li&gt;
&lt;li&gt;Whisk dry ingredients into the pot of liquid ingredients. Be sure to mix well.&lt;/li&gt;
&lt;li&gt;Heat ingredients on medium/high heat, stirring well, until it comes to a boil. Remove from heat.&lt;/li&gt;
&lt;li&gt;Temper the yolks by placing them in a bowl and slowly adding ~1/2 cup of the hot mixture while whisking well. Pour the yolks back into the main pot while continuing to whisk well.&lt;/li&gt;
&lt;li&gt;Heat the mixture until it begins to thicken or it reaches a temperature of 180 F.&lt;/li&gt;
&lt;li&gt;Remove from heat and allow mixture to cool for before placing in an air-tight container.&lt;/li&gt;
&lt;li&gt;Refridgerate for 12+ hours. Then churn according to your ice cream maker's instructions.&lt;/li&gt;
&lt;li&gt;Split the ice cream into two containers, each ~1L in size. You now have Eggnog ice cream. To make it eggnog and cola ice cream, make cola ice cream (below) and layer it on top!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="cola-ice-cream" tabindex="-1"&gt;Cola Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/eggnong-coke/#cola-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Note - this one didn't really turn out like I hoped. Not a lot of Cola flavour. I'd like to try finding a Cola syrup for my next attempt.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="ingredients-1" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/eggnong-coke/#ingredients-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;5 Cups Cola&lt;/li&gt;
&lt;li&gt;1/3 Cup Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;2 Tbsp Corn Syrup&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions-1" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/eggnong-coke/#instructions-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Place Cola in a medium pot and boil for ~30-45 minutes, until you have about 1 1/2 Cups of liquid remaining.&lt;/li&gt;
&lt;li&gt;Meanwhile, in a large bowl whisk together the milk powder and xanthan gum.&lt;/li&gt;
&lt;li&gt;Add cream and dry ingredients to the reduced cola, whisking well as you add them.&lt;/li&gt;
&lt;li&gt;Bring the mixture to a simmer, long enough to allow the sugars to completely dissolve into the mixture.&lt;/li&gt;
&lt;li&gt;Allow to cool, then place mixture in a sealed contained and refridgerate for 12+ hours.&lt;/li&gt;
&lt;li&gt;Churn according to your ice cream maker's instructions.&lt;/li&gt;
&lt;li&gt;Split the ice cream into two containers, each ~1L in size. You now have Cola ice cream. To make it eggnog and cola ice cream, layer on top of your eggnog ice cream!&lt;/li&gt;
&lt;/ol&gt;</description><author>Dustin.Lammiman</author><pubDate>Thu, 23 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/icecream/eggnong-coke/</guid></item><item><title>Autonomous cars will kill the car industry</title><link>https://stop.zona-m.net/2021/12/autonomous-cars-will-kill-the-car-industry/</link><description>&lt;p&gt;Stay away from their stocks. Seriously.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 22 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/autonomous-cars-will-kill-the-car-industry/</guid></item><item><title>2021 in Review</title><link>https://jasoneckert.github.io/myblog/2021-in-review/</link><description>&lt;p&gt;While 2020 introduced a slew of changes to everyone&amp;rsquo;s personal and professional lives, these changes were considerably less pronounced for those of us working in tech fields. After all, most software engineering and server/cloud configuration was already performed remotely before the pandemic using long-standing processes/tools. Adapting to online tech teaching in the college was a welcome challenge, and also more relevant for the industry.&lt;/p&gt;
&lt;p&gt;As 2020 was ending, we all knew that 2021 would bring vaccine rollouts but not a complete return to normalcy. I was very interested to see how the pandemic shakeup would transform the workplace and our use of technology in 2021, but also knew that 2021 would be more like season 2 of 2020. Consequently, I decided to make the most out of the more delightful aspects of 2020, including work-from-home, online tech conferences, and local outdoor activities.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Wed, 22 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/2021-in-review/</guid></item><item><title>The lazy webmaster's way of providing a RSS feed</title><link>https://holzer.online/articles/lazy-way-of-providing-a-feed/index.html</link><description>&lt;p&gt;
    I consciously setup my website as minimal as possible. It's liberating to have a the content immediately
    in a representation that renders &lt;a href="/articles/benefits-of-classless-css/"&gt;good enough&lt;/a&gt;. Yet
    I'd like to offer is a RSS feed. I could of course write it manually, but that would become a chore
    rather soon. A small tool chain could also work, but I would lose the feeling of immediacy.
&lt;/p&gt;
&lt;p&gt;
    Having a regular markup structure made it possible to an &lt;a href="https://createfeed.fivefilters.org"&gt;external service&lt;/a&gt; derive the feed automatically. So,
    &lt;strike&gt;here you go&lt;/strike&gt;.
&lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Wed, 22 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/lazy-way-of-providing-a-feed/index.html</guid></item><item><title>How I Almost Cheesed the EVM</title><link>https://zefram.xyz/posts/how-i-almost-cheesed-the-evm/</link><description>&lt;h2 id="genesis"&gt;Genesis&lt;/h2&gt;
&lt;p&gt;Using Ethereum is expensive. Us smart contract developers are eternally doomed to the fate of locking ourselves in dank dark basements every day to optimize gas usage, so that our fancy DeFi protocols won’t make our users bankrupt.&lt;/p&gt;
&lt;p&gt;Of all the operations done by smart contracts, storage writes &amp;amp; reads usually take up most of the gas usage. EVM storage is made intentionally expensive, because it’s expensive for Ethereum nodes to store data for the rest of time.&lt;/p&gt;</description><author>Zefram's Blog</author><pubDate>Wed, 22 Dec 2021 00:13:31 GMT</pubDate><guid isPermaLink="true">https://zefram.xyz/posts/how-i-almost-cheesed-the-evm/</guid></item><item><title>Announcing a Full-Stack Web Development cohort-based course</title><link>https://www.mootoday.com/blog/announcing-a-full-stack-web-development-cohort-based-course</link><description>Today, I am excited to announce a hands-on, live, project-based course with a small, highly motivated group of professional software engineers.</description><author>www.mootoday.com - RSS Feed</author><pubDate>Tue, 21 Dec 2021 19:47:18 GMT</pubDate><guid isPermaLink="true">https://www.mootoday.com/blog/announcing-a-full-stack-web-development-cohort-based-course</guid></item><item><title>Announcing the First Release of kcctl</title><link>https://www.morling.dev/blog/announcing-first-release-of-kcctl/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_discussion_and_outlook"&gt;Discussion and Outlook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;🧸 &lt;em&gt;It’s Casey. Casey Cuddle.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I am very happy to announce the first stable release of &lt;a href="https://github.com/kcctl/kcctl"&gt;kcctl&lt;/a&gt;,
a modern and intuitive command line client for &lt;a href="https://kafka.apache.org/documentation/#connect"&gt;Apache Kafka Connect&lt;/a&gt;!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Forget about having to memorize and type the right REST API paths and curl flags;
with kcctl, managing your Kafka connectors is done via concise and logically structured commands,
modeled after the semantics of the kubectl tool known from Kubernetes.&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Tue, 21 Dec 2021 18:48:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/announcing-first-release-of-kcctl/</guid></item><item><title>Would you like to have just one monthly bill... but like THIS?</title><link>https://stop.zona-m.net/2021/12/would-you-like-to-have-just-one-monthly-bill...-but-like-this/</link><description>&lt;p&gt;Me, I think it would be dangerous.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 21 Dec 2021 13:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/would-you-like-to-have-just-one-monthly-bill...-but-like-this/</guid></item><item><title>Apple to Apple Comparison: M1 Max vs Intel 🍏</title><link>https://ashvardanian.com/posts/apple-m1/</link><description>M1 Max with DDR5 challenges 64-core server workloads in hash-table benchmarks. Memory bandwidth matters more than core count for many real-world tasks.</description><author>Ash's Blog</author><pubDate>Tue, 21 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ashvardanian.com/posts/apple-m1/</guid></item><item><title>The Value Of Not Having To Be Right</title><link>http://live.julik.nl/2021/12/the-value-of-not-having-to-be-right</link><description/><author>julik live</author><pubDate>Tue, 21 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://live.julik.nl/2021/12/the-value-of-not-having-to-be-right</guid></item><item><title>Aten: Price Convergence 2021 in CWE region</title><link>https://boerman.dev/posts/aten/price-convergence-cwe-2021/</link><description>&lt;figure&gt;&lt;img alt="Bar chart of price convergence per day in 2021 for CWE region (click to enlarge)" src="https://boerman.dev/posts/aten/price-convergence-cwe-2021/price-convergence-CWE-2021.png" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Bar chart of price convergence per day in 2021 for CWE region (click to enlarge)&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="what-is-price-convergence"&gt;What is price convergence?&lt;/h3&gt;
&lt;p&gt;The electricity market in Europe is split into so-called bidding zones. The rule is that within these zones you can trade without transport restrictions, as if the entire grid there is one big conducting plate. This also means that for the day ahead marketi, there is one price per hour per bidding zone. If neighbouring bidding zones have enough interconnection capacity available to satisfy the market direction, then the price in those zones will converge: it will be the same. So, price convergence can be seen as a measure on how much transport restriction the market has run into between bidding zones.&lt;/p&gt;</description><author>Blog Frank Boerman</author><pubDate>Tue, 21 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://boerman.dev/posts/aten/price-convergence-cwe-2021/</guid></item><item><title>A bit on web3, community, and the purpose of life - 123dev #51</title><link>https://justingarrison.com/123dev/2021-12-21-123dev-51/</link><description>It's kind of fun to do the impossible. – Walt Disney</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 21 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-12-21-123dev-51/</guid></item><item><title>Avoid collecting without acting</title><link>https://holzer.online/articles/avoid-collecting-without-acting/index.html</link><description>&lt;p&gt;
The amount of information which is thrust upon you is orders of magnitude larger than the capacity to absorb and make use of it. This can lead to a destructive habit, called the &lt;a href="https://zettelkasten.de/posts/collectors-fallacy/"&gt;collector's fallacy&lt;/a&gt;. This fallacy is a form of self-deception. It makes you feel like you are progressing where you are actually stalling. It leads to wasting your precious time on &lt;a href="/articles/is-it-worth-my-time/"&gt;something that's not worth it&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
To avoid falling prey to the collector's fallacy, you need to work actively with the collection you have. Introduce some form of rate limiting: Don't buy a new book before you finished one. Don't bookmark another article before you haven't read (or at least deleted) one of those you have bookmarked.
&lt;/p&gt;

&lt;p&gt;
If it would have been important, you would have already read it. Its content likely won't be lost. There are search engines and even in case the of &lt;a href="https://www.w3.org/Provider/Style/URI"&gt;uncool urls&lt;/a&gt; there's still the &lt;a href="https://archive.org/web/"&gt;wayback machine&lt;/a&gt;.
&lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Tue, 21 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/avoid-collecting-without-acting/index.html</guid></item><item><title>Why Google is rapidly losing publishers' trust</title><link>https://stop.zona-m.net/2021/12/why-google-is-rapidly-losing-publishers-trust/</link><description>&lt;p&gt;&amp;ldquo;Don&amp;rsquo;t be evil&amp;rdquo; my foot.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 20 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/why-google-is-rapidly-losing-publishers-trust/</guid></item><item><title>It's bingo time (AoC 2021 Day 4)</title><link>https://geekmonkey.org/aoc2021-day4/</link><description>&lt;p&gt;Our underwater journey continues. It can get dark underwater, and today is no exception. The only thing we can see apparently is a giant squid that has attached itself to the outside of our submarine. &lt;/p&gt;&lt;p&gt;It&amp;apos;s only logical that we decide to play Bingo with the squid. Hey,&lt;/p&gt;</description><author>geekmonkey</author><pubDate>Mon, 20 Dec 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/aoc2021-day4/</guid></item><item><title>Self arrest on the Slippery Slope of Addiction</title><link>http://dimitarsimeonov.com/2021/12/20/self-arrest-on-the-slippery-slope-of-addiction</link><description>&lt;blockquote&gt;
  &lt;p&gt;…distraction is just another way our brains attempt to deal with pain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Nir Eyal, Indistractable
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Welcome to part 9 of the series on “How to be a game changer”. You can find the previous posts here &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;1&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;2&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer"&gt;3&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources"&gt;4&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/21/exiting-artificial-scarcity"&gt;5&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/28/climb-your-own-status-everest"&gt;6&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/12/06/the-sorting-hat-of-sales-mindset"&gt;7&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/12/12/opt-out-of-cynicism"&gt;8&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are games which can be win-win when played a limited amount, but become overall negative if you play them excessively. For example, unhealthy addictions can feel like a slippery slope - starting with a little bit, and then becoming a regular user, who is more and more attracted to the game. With such slippery slope games, one way to opt out is to define and maintain a stopping point - a.k.a. Shelling - a point beyond which we don’t venture, as the slope is too slippery for us, and we’re going to slide away. I highly recommend reading &lt;a href="https://www.lesswrong.com/posts/Kbm6QnJv9dgWsPHQP/schelling-fences-on-slippery-slopes"&gt;this article from Less Wrong&lt;/a&gt;, as my article largely applies its ideas, and illustrates them with stories from my life. I will share two stories of slippery slope games - substances and entertainment.&lt;/p&gt;

&lt;p&gt;Defining the point a-priori allows us to &lt;a href="https://www.youtube.com/watch?v=94QFImjdEAo"&gt;self-arrest&lt;/a&gt; the way a mountaineer would be able to stop themselves from falling on an actual icy slippery slope.&lt;/p&gt;

&lt;p&gt;&lt;img alt="the slippery slope" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/09slope.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Addictive Substances&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I used to drink a lot, but today I don’t. Here’s what happened. I think Alcohol can be a very effective way to quickly relax after a stressful day, and in the right social environment, can help people who drink it to open up to social connections, be more empathetic, and foster relationships. I used to drink regularly, and I found it hard to control how much I drank. When I was buzzed, I wanted to drink more and more.&lt;/p&gt;

&lt;p&gt;Fifteen years ago, when I was in college I used to drink a lot. I enjoyed and craved drinking. But one day, someone close called me an alcoholic. I didn’t identify as alcoholic and this criticism made me feel painful, and in doubt of myself. After another binge, and a really bad hangover the next day, I decided to take a break from alcohol, and didn’t drink for a month. I wrote more about the story &lt;a href="https://dimitarsimeonov.com/top/2013/11/09/overthrowing-alcohol"&gt;here&lt;/a&gt;. I kept the practice of no-alcohol month every year for 12 years, and I gradually started noticing that after an alcohol fast, I would start back drinking slowly, one drink a week or so, but gradually the volume would increase. It’s a slippery slope! Few years ago I started adding a second month of no alcohol, which lead to adding a third month the year after, which easily transformed to my current relationship to alcohol. Today, I’d only have a drink on a special occasion, and I’m in a near permanent alcohol fast.&lt;/p&gt;

&lt;p&gt;For me, the Shelling point of alcohol has moved very close to the no-alcohol zone, and I think of my relationship to alcohol as healthy at that level. Some people can maintain a personal restriction of never going more than 1-2 drinks per night, and that can also be a sustainable relationship to it. People who are past alcoholics, or others who just went cold turkey are staying at completely zero alcohol in their life. That’s where they put their boundary and it’s OK. They stay in self-arrest all of the time, as they wouldn’t be able to stop if they slide even a little bit. If I didn’t take 10 years to gradually wean off it my boundary would be different. I think I can self-arrest if I notice myself sliding, so I allow myself a drink on special occasions.&lt;/p&gt;

&lt;p&gt;&lt;img alt="drinks decrease" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/09drink.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Other substances may be more addictive than alcohol. I’ve never had heroin, which is known to be extremely addictive, and I cannot speak about it. But I’ve had another extremely addictive drug, which 90% of US population is currently addicted to - caffeine. It started with black tea, and in college I upgraded to coffee. Caffeine is a reasonably healthy drug, unlike alcohol which is toxic. Caffeine can also boost mental focus for brief periods, and a lot of our economy runs on coffee and tea, perhaps as much as it runs on gasoline.&lt;/p&gt;

&lt;p&gt;For me, caffeine’s addiction slope was less slippery than alcohol - I could easily maintain a certain amount of coffee per day for a long time, without needing to increase - it felt more like a staircase, than slope. The reason I decided to opt-out of caffeine is that I thought was abusing it to force myself to do work I didn’t actually want to do. I also had the hypothesis that the human body should be fine without it, as we’ve had most of our biological evolution without it. A wolf doesn’t drink an espresso before chasing a deer, after all. I didn’t find good long term research on caffeine addiction - it is hard to find subjects for a control study when nearly everyone uses, and who the hell wants to give up coffee and suffer. Whatever research I found seemed to only focus on short term, and I was curious about the long term effects of caffeine.&lt;/p&gt;

&lt;p&gt;So, on March 14th 2018 (pi day!), after weaning out caffeine for a few weeks, I quit it. The self-arrest shook me up, and caffeine withdrawal felt much worse than alcohol withdrawal. For a few weeks, I had headaches, exhaustion and other withdrawal symptoms. I was between jobs, and I had the privilege to afford the space for the withdrawal, and to realize that I can be productive without caffeine, perhaps even more productive over the long term. Since then, I’ve learned to recognize how music, exercise, cold showers and pomodoro timers can induce the same level of focus. And I’ve learned to appreciate how I can be more motivated by having skin in the game.&lt;/p&gt;

&lt;p&gt;Today, I will only have caffeine in “emergency” or if I am climbing a high elevation. I also occasionally have small amounts of dark chocolate, which has caffeine too. My Shelling point is very close to zero, and honestly, caffeine doesn’t feel that good to me any more, after the first few minutes of focus. I feel grateful for having reduced my dependency on it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Information slipping into entertainment&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;YouTube contains a lot of useful videos. It has helped me do a few minor repairs, and taught about a bunch of technologies through instructional videos. But the YouTube’s video recommendations can cause me to stick around for much longer. I come for the information, but stay for the entertainment. The information is valuable, it makes my life objectively better if I learn how to repair a light fixture. And some entertainment is also valuable as helps me relax. But as I get entertained I want more, more, more. I find excessive entertainment is not only not helpful - it is not that enjoyable either! Excessive use of such media is mostly a lose-lose game. We, as users waste our time, and society loses our potentially valuable contributions. And even YouTube may lose in the long term, as its users churn cold turkey when they realize it is a problem. The only people who win in the short term are advertisers, which buy our time for pennies per hour.&lt;/p&gt;

&lt;p&gt;Other media outlets like TV(CNN, Fox News, etc.), Facebook, Instagram, Twitter, and so on, can be just as slippery. They may seem valuable, but beyond providing some basic information, these media outlets optimize for sheepish behavior, and turn their consumers into sheep. They add more and more addictive entertainment to their content. Even if that entertainment takes the form of making us angry or scared. Such entertainment is empty and it doesn’t make our lives better.&lt;/p&gt;

&lt;p&gt;I also strongly suspect it affects our body chemistry - we get a dopamine boost from every funny video we watch. Opting out of this game is hard, as our bodies will literally go into withdrawal, and we’ll try to replace our dose of dopamine with something else. We may think that what happens in our minds stays in our mind, but it doesn’t. It has physical effects on us, and we’re literally poisoning ourselves with our screens. As of writing this, I still have an excessive scrolling habit. I have not yet been able to redefine my relationship to it.&lt;/p&gt;

&lt;p&gt;And I’m still experimenting with ways to self-arrest and opt out of it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Mon, 20 Dec 2021 06:01:11 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/12/20/self-arrest-on-the-slippery-slope-of-addiction</guid></item><item><title>Upgrade tolerant way of setting the path to Oracle SQLcl with Homebrew</title><link>https://hartenfeller.dev/blog/sqlcl-homebrew-macos</link><description>With two lines in your zsh config file, Oracle SQLcl will still work after an upgrade with Homebrew.</description><author>Philipp Hartenfeller Blog RSS Feed</author><pubDate>Mon, 20 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://hartenfeller.dev/blog/sqlcl-homebrew-macos</guid></item><item><title>End of Year Reading 2021</title><link>https://rohitjha.com/blog/end-of-year-reading-2021/</link><description>Top book recommendations for end of year reading in 2021</description><author>THINK@RJ</author><pubDate>Sun, 19 Dec 2021 17:40:32 GMT</pubDate><guid isPermaLink="true">https://rohitjha.com/blog/end-of-year-reading-2021/</guid></item><item><title>Getting started with Hakyll on Fedora</title><link>https://blog.marbu.eu/posts/2021-03-28-hakyll-fedora</link><description>&lt;article&gt;
&lt;section class="article-header"&gt;
Posted on 28 March 2021

&lt;/section&gt;
&lt;section class="article-tags"&gt;
Tags: &lt;a href="/tags/Fedora.html" rel="tag" title="All pages tagged 'Fedora'."&gt;Fedora&lt;/a&gt;, &lt;a href="/tags/Haskell.html" rel="tag" title="All pages tagged 'Haskell'."&gt;Haskell&lt;/a&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;div class="article-toc"&gt;Table of Contents:
&lt;ul&gt;
&lt;li&gt;&lt;a href="#installing-hakyll-on-fedora" id="toc-installing-hakyll-on-fedora"&gt;Installing Hakyll on Fedora&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#initializing-hakyll-project" id="toc-initializing-hakyll-project"&gt;Initializing Hakyll project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#building-the-example-site" id="toc-building-the-example-site"&gt;Building the example site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#next-steps" id="toc-next-steps"&gt;Next steps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Upstream &lt;a href="https://jaspervdj.be/hakyll/tutorials/01-installation.html"&gt;installation tutorial of Hakyll static site
generator&lt;/a&gt; shows
how to use cabal or stack to get hakyll installed and build example site with
it. And while the tutorial also mentions an option to use distribution
packages on Debian or Fedora, the rest of the tutorial doesn’t explore this
option further. In this post I provide a quick overview of installation and
usage when Hakyll is installed from rpm packages on Fedora 33.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;a href="https://jaspervdj.be/hakyll/"&gt;Hakyll&lt;/a&gt; is a &lt;em&gt;static-site generator&lt;/em&gt; written in
haskell, inspired by other such tools like
&lt;a href="https://en.wikipedia.org/wiki/Jekyll_(software)"&gt;Jekyll&lt;/a&gt;,
&lt;a href="https://nanoc.ws/"&gt;nanoc&lt;/a&gt; or &lt;a href="https://github.com/jgm/yst"&gt;yst&lt;/a&gt;. It uses
haskell DSL for configuration in a similar way how
&lt;a href="https://xmonad.org/"&gt;xmonad&lt;/a&gt; does, and for html building, it relies on
versatile &lt;a href="https://pandoc.org/"&gt;pandoc&lt;/a&gt; document converter.&lt;/p&gt;
&lt;p&gt;Reasons I use Hakyll to build my personal blog include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I simply prefer static-site generator approach for this use case, it allows
me to write posts in a simple plain text format and then store them in a git
repository.&lt;/li&gt;
&lt;li&gt;I already use pandoc for various tasks (converting between wiki formats,
generating epub files, exporting my markdown or org mode notes …) because
it can convert between lot of markup and document formats. Possibility to use
pandoc features in hakyll makes all these familiar possibilities available,
which is a plus.&lt;/li&gt;
&lt;li&gt;I’m little familiar with haskell and as xmonad user, I find xmonad’s approach
to configuration flexible and powerful.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installing-hakyll-on-fedora"&gt;&lt;a class="headerLink" href="#installing-hakyll-on-fedora" title="Installing Hakyll on Fedora"&gt;Installing Hakyll on Fedora&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fortunately Hakyll is already packaged in Fedora, so we don’t need to build it
themselves via &lt;code&gt;cabal&lt;/code&gt; or &lt;code&gt;stack&lt;/code&gt; just to have it installed if we don’t have
other reason to do so (eg. using latest version of hakyll for testing or
development of hakyll itself).&lt;/p&gt;
&lt;p&gt;The package we need to install is &lt;code&gt;ghc-hakyll-devel&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# dnf install ghc-hakyll-devel&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And everything else will be installed as a dependency. Installing the devel
package is necessary because Hakyll’s reconfiguration process includes haskell
compilation.&lt;/p&gt;
&lt;h2 id="initializing-hakyll-project"&gt;&lt;a class="headerLink" href="#initializing-hakyll-project" title="Initializing Hakyll project"&gt;Initializing Hakyll project&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First of all, we are going to use &lt;code&gt;hakyll-init&lt;/code&gt; tool to create new directory
&lt;code&gt;example-site&lt;/code&gt; with both content and configuraion files of the example project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hakyll-init example-site
Creating example-site/templates/post-list.html
Creating example-site/templates/post.html
Creating example-site/templates/default.html
Creating example-site/templates/archive.html
Creating example-site/index.html
Creating example-site/images/haskell-logo.png
Creating example-site/site.hs
Creating example-site/contact.markdown
Creating example-site/css/default.css
Creating example-site/about.rst
Creating example-site/posts/2015-10-07-rosa-rosa-rosam.markdown
Creating example-site/posts/2015-08-12-spqr.markdown
Creating example-site/posts/2015-11-28-carpe-diem.markdown
Creating example-site/posts/2015-12-07-tu-quoque.markdown
Creating example-site/example-site.cabal
$ cd example-site&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to compile &lt;code&gt;site.hs&lt;/code&gt;, which is haskell source code file with hakyll
configuration for the site, into &lt;code&gt;site&lt;/code&gt; binary file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ghc --make site.hs
[1 of 1] Compiling Main             ( site.hs, site.o )
Linking site ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could also use &lt;code&gt;cabal&lt;/code&gt; instead as explained in upstream &lt;a href="https://jaspervdj.be/hakyll/tutorials/01-installation.html"&gt;hakyll installation
tutorial&lt;/a&gt;, but
since we have Hakyll installed from the rpm package, we already have all the
devel dependencies installed as well, and there is no need to use cabal just
for building of the &lt;code&gt;site&lt;/code&gt; executable.&lt;/p&gt;
&lt;p&gt;The resulting &lt;code&gt;site&lt;/code&gt; tool then provides all hakyll’s functionality:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./site -h
Usage: site [-v|--verbose] COMMAND
  site - Static site compiler created with Hakyll

Available options:
  -h,--help                Show this help text
  -v,--verbose             Run in verbose mode

Available commands:
  build                    Generate the site
  check                    Validate the site output
  clean                    Clean up and remove cache
  deploy                   Upload/deploy your site
  preview                  [DEPRECATED] Please use the watch command
  rebuild                  Clean and build again
  server                   Start a preview server
  watch                    Autocompile on changes and start a preview server.
                           You can watch and recompile without running a server
                           with --no-server.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we check the size of the executable, we see that it’s quite large. That is
because like golang, haskell compiler uses static linking by default.
From the system perspective, the executable is dynamically linked though. If
we stripe the binary, we can save about 25% of the executable size.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh site
-rwxrwxr-x. 1 martin martin 148M Mar 28 19:13 site
$ strip site
$ ls -lh site
-rwxrwxr-x. 1 martin martin 110M Mar 28 19:47 site&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But when the size is really bothering us, we can tell ghc to use dynamic
linking instead. This will link the executable with system haskell libraries
making the binary file orders of magnitude smaller.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ghc -dynamic --make site.hs
[1 of 1] Compiling Main             ( site.hs, site.o )
Linking site ...
$ ls -lh site
-rwxrwxr-x. 1 martin martin 78K Mar 28 19:52 site&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="building-the-example-site"&gt;&lt;a class="headerLink" href="#building-the-example-site" title="Building the example site"&gt;Building the example site&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So now we can generate html files of the site via:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./site build
Initialising...
  Creating store...
  Creating provider...
  Running rules...
Checking for out-of-date items
Compiling
  updated templates/default.html
  updated about.rst
  updated templates/post.html
  updated posts/2015-08-12-spqr.markdown
  updated posts/2015-10-07-rosa-rosa-rosam.markdown
  updated posts/2015-11-28-carpe-diem.markdown
  updated posts/2015-12-07-tu-quoque.markdown
  updated templates/archive.html
  updated templates/post-list.html
  updated archive.html
  updated contact.markdown
  updated css/default.css
  updated images/haskell-logo.png
  updated index.html
Success&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And check the result in &lt;code&gt;_site/&lt;/code&gt; directory. We can also start hakyll preview
http server which will present the site on &lt;a class="uri" href="http://127.0.0.1:8000"&gt;http://127.0.0.1:8000&lt;/a&gt; and auto
recompile html files when it’s source files changes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./site watch
Listening on http://127.0.0.1:8000
Initialising...
  Creating store...
  Creating provider...
  Running rules...
Checking for out-of-date items
Compiling
Success&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="next-steps"&gt;&lt;a class="headerLink" href="#next-steps" title="Next steps"&gt;Next steps&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As noted above, the configuration of a hakyll project is done via &lt;code&gt;site.hs&lt;/code&gt;
file, which contains haskell source code.
And as you have most likely already guessed, when you update configuration in
&lt;code&gt;site.hs&lt;/code&gt; file, you need to recompile &lt;code&gt;site&lt;/code&gt; executable.&lt;/p&gt;
&lt;p&gt;Domain specific language approach
combined with good upstream
&lt;a href="https://jaspervdj.be/hakyll/tutorials.html"&gt;tutorials&lt;/a&gt; and &lt;a href="https://jaspervdj.be/hakyll/examples.html"&gt;real world
examples&lt;/a&gt;
makes
changing hakyll configuration possible even without fully understanding
haskell language, but having some haskell knowledge will definitely help a
lot. That said I have to admit that if you don’t like idea of learning a bit
of haskell (or finding an excuse for that), you won’t be very happy with
hakyll.&lt;/p&gt;

&lt;/section&gt;
&lt;/article&gt;

&lt;div class="post-end-note"&gt;
&lt;p&gt;Do you have a comment or question? You can &lt;a href="/about.html"&gt;contact me&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;</description><author>blog.marbu.eu</author><pubDate>Sun, 19 Dec 2021 13:53:10 GMT</pubDate><guid isPermaLink="true">https://blog.marbu.eu/posts/2021-03-28-hakyll-fedora</guid></item><item><title>Consuming Articles Offline</title><link>https://blog.nawaz.org/posts/2021/Dec/consuming-articles-offline/</link><description>&lt;p&gt;My desktop is my portal to the Internet. &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; Lately, I&amp;#8217;ve needed to
spend significant amounts of time away from my machine. So I took to
printing articles and taking them with&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;The experience has been&amp;nbsp;transformative.&lt;/p&gt;
&lt;p&gt;As an example, consider this
&lt;a class="reference external" href="https://www.theguardian.com/us-news/2021/sep/16/geofence-warrants-reverse-search-warrants-police-google"&gt;article&lt;/a&gt;:&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://blog.nawaz.org/images/print-articles/guard01.png"&gt;
&lt;img alt="Screenshot of the article in Google Chrome" src="https://blog.nawaz.org/images/print-articles/guard01.png" /&gt;
&lt;/a&gt;
&lt;p&gt;And as I scroll further …&lt;/p&gt;</description><author>Beetle Space</author><pubDate>Sun, 19 Dec 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.nawaz.org/posts/2021/Dec/consuming-articles-offline/</guid></item><item><title>My Favorite Board Games and How I Play Them</title><link>https://www.jimwestergren.com/favorite-board-games</link><description>Board games is one of my passions. Both as a player and now also as an aspiring game designer. And I can sometimes get obsessed in trying to improve rules of games to better suit the tastes of me and those I play with (usually my wife, son and brother-in-law). I normally play as a &amp;#91;...&amp;#93;</description><author>Jim Westergren</author><pubDate>Sat, 18 Dec 2021 23:04:00 GMT</pubDate><guid isPermaLink="true">https://www.jimwestergren.com/favorite-board-games</guid></item><item><title>Digital transformation in Eastern Europe</title><link>https://stop.zona-m.net/2021/12/digital-transformation-in-eastern-europe/</link><description>&lt;p&gt;Tell me more about it!&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 18 Dec 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/digital-transformation-in-eastern-europe/</guid></item><item><title>Tick, Tick... Boom!</title><link>https://olshansky.info/movie/tick_tick..._boom/</link><description>Olshansky's review of Tick, Tick... Boom!</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 18 Dec 2021 13:50:33 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/tick_tick..._boom/</guid></item><item><title>TypeScript &amp;lt;3</title><link>https://liza.io/typescript-3/</link><description>&lt;p&gt;I&amp;rsquo;ve started playing around with TypeScript for the first time over the last couple of weeks and it is excellent. I&amp;rsquo;ve made a few hobby JS games in the past as you can see from the &lt;a href="../games"&gt;games&lt;/a&gt; page. JavaScript was always fun for me, but after years of statically typed languages I have grown to prefer them.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Sat, 18 Dec 2021 11:45:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/typescript-3/</guid></item><item><title>Class Management Reviews: Fall 2021</title><link>https://lambdaland.org/posts/2021-12-18_classroom_management_reviews/</link><description>&lt;p&gt;A collection of what worked well and what didn’t in classes that I took this semester. This is partially for me to record what things reduced friction for me as a student so that one day, should I become a professor, I’ll be able to run the lowest-friction class ever!&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sat, 18 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-12-18_classroom_management_reviews/</guid></item><item><title>Probabilistic spin glass - Part II</title><link>https://bytepawn.com/probabilistic-spin-glass-part-ii.html</link><description>&lt;p&gt;This is a continuation of the previous article on probabilistic spin glasses, with improvements to the simulation code and improved entropy computation. &lt;br /&gt;&lt;br /&gt;&lt;img alt="." src="/images/prob_spin_glass_p2_5.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 18 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/probabilistic-spin-glass-part-ii.html</guid></item><item><title>Update to Game of Ur Background</title><link>https://ztoz.blog/posts/ur-background/</link><description>&lt;p&gt;I&amp;rsquo;ve made an update to the &lt;a href="https://ztoz.blog/posts/game-of-ur-background/"&gt;Game of Ur - Background&lt;/a&gt; post. A reader took up my challenge to find a closed form solution for the number of valid game placements and &lt;a href="https://distracted-jackson-c5763c.netlify.app/math/ur.html"&gt;found a solution&lt;/a&gt;.&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Fri, 17 Dec 2021 20:16:48 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/ur-background/</guid></item><item><title>Overthinking TypeScript Types</title><link>http://www.nuke24.net/plog/32.html</link><author>TOGoS's Project Log</author><pubDate>Fri, 17 Dec 2021 20:13:37 GMT</pubDate><guid isPermaLink="true">http://www.nuke24.net/plog/32.html</guid></item><item><title>All those millions of online meetings are WASTED...</title><link>https://stop.zona-m.net/2021/12/all-those-millions-of-online-meetings-are-wasted.../</link><description>&lt;p&gt;&amp;hellip;if they don&amp;rsquo;t bring about LASTING changes in HUMANS.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 17 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/all-those-millions-of-online-meetings-are-wasted.../</guid></item><item><title>Iterating on Club Leaderboards</title><link>https://medium.com/strava-engineering/iterating-on-club-leaderboards-f67862cef178</link><description>&lt;p&gt;Iteration is an important part of my development workflow, and it’s an important
part of the way we work at Strava. Over the course of my own career, I’ve
learned to really value the process of incremental development. By shipping
relatively small changes quickly, we can gather feedback, observe important
metrics, and continue the cycle with targeted improvements. Ultimately, this
helps us continually deliver athlete value on a rapid timeline.&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Fri, 17 Dec 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://medium.com/strava-engineering/iterating-on-club-leaderboards-f67862cef178</guid></item><item><title>Creating Fastmail Masked Emails without 1Password anytime and everywhere</title><link>https://david.coffee/creating-fastmail-masked-emails-without-1password-anytime-and-everywhere/</link><description>&lt;p&gt;I love Masked Emails, Fastmails take on one-time-use email addresses. I&amp;rsquo;ve been doing something similar myself for years now, but it is a bit of a shame that it&amp;rsquo;s so tightly coupled to 1Password. Sure you &lt;em&gt;can&lt;/em&gt; create them through the webinterface now, but it&amp;rsquo;s still kind of a pain to use and not very convenient (unless you use 1Password).&lt;/p&gt;
&lt;p&gt;You can&amp;rsquo;t even manually trigger creation of one within 1Password, so when the extension doesn&amp;rsquo;t detect an email signup field, you&amp;rsquo;re sorta out of luck.&lt;/p&gt;
&lt;p&gt;What I want is something like this:&lt;/p&gt;
&lt;p&gt;
&lt;img alt="showcase" class="img-fluid" src="./masked-email-showcase.gif" /&gt;

&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get it working.&lt;/p&gt;
&lt;p&gt;The first thing I noticed is that the request to create a new masked email (through 1Password) isn&amp;rsquo;t actually coming from the 1Password servers like I first thought, it goes directly from the client to api.fastmail.com, like seen on a reverse-proxy like Proxyman:&lt;/p&gt;
&lt;p&gt;
&lt;img alt="Network screenshot" class="img-fluid" src="./masked-email-proxyman.jpg" /&gt;

&lt;/p&gt;
&lt;p&gt;By the way, Fastmail also stated they put this functionality right into their open-source JMAP project &lt;a href="https://blog.1password.com/making-masked-email-with-jmap/"&gt;in a blogpost&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After a bit of reverse-engineering the request format to the API server and replicating it in Golang:&lt;/p&gt;
&lt;p&gt;
&lt;img alt="Requests" class="img-fluid" src="./masked-email-request.jpg" /&gt;

&lt;/p&gt;
&lt;p&gt;I present you: &lt;a href="https://github.com/dvcrn/maskedemail-cli"&gt;https://github.com/dvcrn/maskedemail-cli&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;maskedemail-cli -accountid xxxx -token abcdef12345 create facebook.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;hellip; and you get a new masked email for facebook.com, right in your shell!&lt;/p&gt;
&lt;h2 id="glueing-the-cli-to-shortcuts-with-keyboardmaestro"&gt;Glueing the CLI to shortcuts with KeyboardMaestro&lt;/h2&gt;
&lt;p&gt;I wanted this to be available everywhere whenever I need to. A full extension for a product that isn&amp;rsquo;t mine would have been too much work, and I wanted it to work outside browsers as well. With something like KeyboardMaestro this is trivial to do&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the macro from above:&lt;/p&gt;
&lt;p&gt;
&lt;img alt="KeyboardMaestro Macro" class="img-fluid" src="./masked-email-keyboardmaestro.png" /&gt;

&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Insert &amp;ldquo;&amp;hellip;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Retrieve the URL from the current browser window. I had to use some glue applescript here because Firefox has no scripting interface&lt;/li&gt;
&lt;li&gt;Pipe it into the maskedemail-cli&lt;/li&gt;
&lt;li&gt;Remove the &amp;ldquo;&amp;hellip;&amp;rdquo; and paste the newly created email&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="other-thoughts"&gt;Other thoughts&lt;/h2&gt;
&lt;p&gt;I am sure this will become more available in the near future. Fastmail likely has some kind of exclusivity thing with 1Password for now, but it&amp;rsquo;s great to see that we can hack ontop of it so easily. It&amp;rsquo;s also very cool that this is built right into JMAP. I hope we&amp;rsquo;ll see more email providers implementing it!&lt;/p&gt;</description><author>David Mohl</author><pubDate>Fri, 17 Dec 2021 17:39:23 GMT</pubDate><guid isPermaLink="true">https://david.coffee/creating-fastmail-masked-emails-without-1password-anytime-and-everywhere/</guid></item><item><title>Advent of Code 2021 - Day 3</title><link>https://geekmonkey.org/aoc2021-day3/</link><description>&lt;p&gt;Deep under the water, we find ourselves hearing creaking noises. Our submarine handily gives us a diagnostics report. Sadly, as is oft too common in AoC, the report format is complete garbage, to put it mildly.&lt;/p&gt;&lt;p&gt;Our puzzle input comes in the form of binary numbers. The task is to&lt;/p&gt;</description><author>geekmonkey</author><pubDate>Fri, 17 Dec 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/aoc2021-day3/</guid></item><item><title>Monte Carlo Options Pricing</title><link>https://blog.skz.dev/monte-carlo-options-pricing</link><description>&lt;h4 id="the-options-casino"&gt;&lt;em&gt;The options casino&lt;/em&gt;&lt;/h4&gt;

&lt;p&gt;Predicting the movement of stock prices is an alluring challenge with the promise of riches. Unfortunately, predicting future stock prices consistently and reliably is generally considered impossible. However, we can use models to make useful predictions, manage risk, and profit &lt;em&gt;probalistically&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id="geometric-brownian-motion"&gt;Geometric Brownian Motion&lt;/h1&gt;
&lt;p&gt;One such commonly used model is geometric Brownian motion (&lt;a href="https://stats.libretexts.org/Bookshelves/Probability_Theory/Probability_Mathematical_Statistics_and_Stochastic_Processes_(Siegrist)/18%3A_Brownian_Motion/18.04%3A_Geometric_Brownian_Motion"&gt;1&lt;/a&gt;, &lt;a href="http://www.columbia.edu/~ks20/FE-Notes/4700-07-Notes-GBM.pdf"&gt;2&lt;/a&gt;) - in fact, the famous &lt;a href="https://en.wikipedia.org/wiki/Black%E2%80%93Scholes_model"&gt;Black-Scholes options pricing formula&lt;/a&gt; assumes this model as well. This has the form&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
 S(t) = S(0)\exp{((\mu-\frac{\sigma^2}{2})t + \sigma B(t))} \tag{0}  \label{eq:gbm}
\end{align}

    $$
&lt;/div&gt;

&lt;p&gt;where \(B(t)\) is standard Brownian motion. If we let \(S(0)\) to be the initial stock price, \(\mu\) to be the mean &lt;a href="https://en.wikipedia.org/wiki/Rate_of_return#Logarithmic_or_continuously_compounded_return"&gt;compounding return&lt;/a&gt;, and \(\sigma\) to be the &lt;a href="https://en.wikipedia.org/wiki/Volatility_(finance)"&gt;volatility&lt;/a&gt; of a stock, then we can generate very realistic price paths.&lt;/p&gt;

&lt;div id="plot-0"&gt;&lt;/div&gt;
&lt;p&gt;&lt;span class="slider-container"&gt;
    &lt;span class="slider-label"&gt;\(\mu\)&lt;/span&gt;
    &lt;span&gt;
        &lt;input class="slider" id="mean-slider" max="1" min="-2.5" step="0.01" type="range" value="0" /&gt;
    &lt;/span&gt;
    &lt;span class="slider-value" id="mean-slider-value"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="slider-container"&gt;
    &lt;span class="slider-label"&gt;\(\sigma\)&lt;/span&gt;
    &lt;span&gt;
        &lt;input class="slider" id="sigma-slider" max="1" min="0" step="0.01" type="range" value="0" /&gt;
    &lt;/span&gt;
    &lt;span class="slider-value" id="sigma-slider-value"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;button class="regenerate-button"&gt;Regenerate&lt;/button&gt;&lt;/p&gt;

&lt;div id="mean-0" style="display: none;"&gt;0&lt;/div&gt;
&lt;div id="sigma-0" style="display: none;"&gt;0&lt;/div&gt;
&lt;div id="option-mean-0" style="display: none;"&gt;0&lt;/div&gt;

&lt;p&gt;So, what does this mean, and why is this a good model for security prices?&lt;/p&gt;

&lt;h2 id="logarithmic-returns"&gt;Logarithmic Returns&lt;/h2&gt;
&lt;p&gt;In quantitative finance it is common to deal with continuously compounded returns rather than simple returns. What that means is that instead of examining a simple return between trading periods \(1 + R_t = \frac{S(t)}{S(t-1)}\) we will examine the compounding return \(r_t = \ln{(1 + R_t)} = \ln{\frac{S(t)}{S(t-1))}}\). This has the very nice property that one can recover the price of a stock by simply summing up the logarithmic returns as:&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
 S(t) = S(0)\exp{(r_1 + r_2 + ... + r_{t-1})} \tag{1}  \label{eq:rr}
\end{align}

    $$
&lt;/div&gt;

&lt;p&gt;This is also convenient because a common assumption in finance is that the logarithmic returns of a stock are independent and normally distributed with mean \(\mu\) and variance \(\sigma^2\). Note that for small returns, simple returns \(R_t\) approximate logarthmic returns \(r_t\) since \(r_t = \ln{(1 + R_t)} \approx R_t\), so the difference between the two returns is not substantial, and thus one can assume both simple and logarathmic returns are normally distributed. Next, if the logarithmic returns are normally distributed, then this in turn implies the stock price is &lt;em&gt;lognormally&lt;/em&gt; distributed. This has been observed empirically to some extent, although in real life stock prices tend to have “fatter tails” - meaning rare events happen more often than a normal distribution would predict. Here are the monthly returns for IBM stock showing this behaviour.&lt;/p&gt;

&lt;p&gt;&lt;img alt="IBM monthly returns" src="/assets/img/monte-carlo-stocks/IBM-returns.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Monthly simple and logarithmic returns of IBM stock with fitted normal distributions. We observe more returns at the extremities of the distributions than the normal distribution would predict. We call these 'fat tails' or 'excess kurtosis'. This image has been taken from 'Analysis of Financial Time Series, 3rd Ed.' by Ruey S. Tsay.
&lt;/p&gt;

&lt;p&gt;This model also works well because it only permits positive security prices. Securities tend to only have positive prices (although in some cases they can go negative - see the &lt;a href="https://assets.weforum.org/editor/kE8TpkO7bzo3dLFWEh9UDvgqRuZPOmURhnh0FfwOVYc.png"&gt;oil crash of 2020&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So, we want a process that generates a series of normally distributed returns \(r_t\) - which invites Brownian motion as a natural choice.&lt;/p&gt;

&lt;h1 id="brownian-motion"&gt;Brownian motion&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://stats.libretexts.org/Bookshelves/Probability_Theory/Probability_Mathematical_Statistics_and_Stochastic_Processes_(Siegrist)/18%3A_Brownian_Motion/18.01%3A_Standard_Brownian_Motion"&gt;Brownian motion&lt;/a&gt; describes the motion of a particle in a fluid or gas. Such a particle bounces around “randomly” within the fluid surrounding it. The path it creates appears quite noisy and random. It can essentially be considered a limiting form of the random walk where both the time between steps and the step length approach zero (with some caveats: namely, if the time step is \(t\), the step length must be \(\sqrt{t}\) - this produces a process whose variance scales exactly with time). Consider flipping a coin to determine whether to take a step forward or backward. Now let the time between steps approach zero. You would trace a path that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img alt="1-D Brownian Motion" src="/assets/img/monte-carlo-stocks/wiener-process-zoom.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
1 dimensional [arithmetic] Brownian motion. Geometric Brownian motion is the exponential of this process. &lt;a href="http://creativecommons.org/licenses/by-sa/3.0/" target="_blank" title="Creative Commons Attribution-Share Alike 3.0"&gt;CC BY-SA 3.0&lt;/a&gt;, &lt;a href="https://commons.wikimedia.org/w/index.php?curid=1426987" target="_blank"&gt;Source&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Brownian motion has the very nice property that the trajectories are normally distributed, which is exactly what we are looking for. We can simulate a Brownian process with discrete steps by sampling from a normal distribution (although if you only cared about the terminal value of a time series, you could sample from any distribution with unit variance; due to the central limit theorem the sum of random variables would approach a Gaussian distribution in the long term anyway).&lt;/p&gt;

&lt;p&gt;Hence, Brownian motion provides us with the series of normally distributed logarathmic returns \(r_t\) as required in \(\eqref{eq:rr}\):&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
 r(t) = (\mu-\frac{\sigma^2}{2})t + \sigma B(t) \tag{2}  \label{eq:bm}
\end{align}

    $$
&lt;/div&gt;

&lt;p&gt;Taking the exponential of this process, we’ve recovered an equation for modelling stock price movement.&lt;/p&gt;

&lt;p&gt;Of particular interest here is the drift rate \(\mu - \frac{\sigma^2}{2}\) which causes the process to drift linearly, as the name suggests. \(\mu\) is obvious - it’s the mean continuosly compounding return for the stock, and in the absence of noise (or volatility), the stock price would only grow (or decrease) by this compounding amount. But what about \(\frac{\sigma^2}{2}\)? Well, it turns out that the expected value of a lognormally distributed variable is \(\exp(\mu + \sigma^2/2)\). Since the stock price is &lt;em&gt;lognormally distributed&lt;/em&gt; (while returns are &lt;em&gt;normally distributed&lt;/em&gt;), and since we require that the expected value of the stock price after continuous compounding to be \(S(0)\exp(\mu t)\), we must apply the correction term of \(- \frac{\sigma^2}{2}\) to the process.&lt;/p&gt;

&lt;p&gt;There’s also a philsophical argument to be made for choosing Brownian motion as the basis for this model. A common assumption made in finance is that markets are efficient, so there should be no arbitrage opportunities. Hence, the market should be unpredictable - if an actor could predict it, that would be an arbitrage opportunity that would be exploited away. Hence, random noise (which is what Brownian motion is, essentially) is a good choice for modelling markets.&lt;/p&gt;

&lt;h1 id="option-pricing"&gt;Option Pricing&lt;/h1&gt;

&lt;p&gt;Monte-Carlo simulation is a statistical technique inspired by the casinos of Monaco. Much like gamblers resigning their fates to probability, we hand over the results of statistical analysis to chance. By running enough trials, we can make conclusions with statistical significance.&lt;/p&gt;

&lt;p&gt;Consider evaluating a call option with a strike prices of $105 for this stock. What would be the expected value of the option at expiry, given a geometric Brownian model for the stock’s movement? We can estimate this by generating a series of price paths, each of which can be considered a trial. The terminal value of each trial is related to the intrinsic value of the option via the relation \(V = \max(0, S - K)\) where \(S\) is the stock price and \(K\) is the strike price. The mean terminal value provides us with an estimate of the option’s value at expiry, while the standard deviation of terminal values provides us with a measure of how certain we are that the option will expire close to this value.&lt;/p&gt;

&lt;div id="plot-1"&gt;&lt;/div&gt;
&lt;p&gt;&lt;button class="regenerate-button"&gt;Regenerate&lt;/button&gt;&lt;/p&gt;
&lt;div id="mean-1"&gt;0&lt;/div&gt;
&lt;div id="sigma-1"&gt;0&lt;/div&gt;
&lt;div id="option-mean-1"&gt;0&lt;/div&gt;

&lt;p&gt;We can increase the number of trials to increase the statistical certainty of the estimate. Re-running the simulation (by clicking “Regenerate”) on a large number of trials (below) yields a much smaller discrepency between experiments than on a small number of trials (above).&lt;/p&gt;

&lt;div id="plot-2"&gt;&lt;/div&gt;
&lt;p&gt;&lt;button class="regenerate-button"&gt;Regenerate&lt;/button&gt;&lt;/p&gt;
&lt;div id="mean-2"&gt;0&lt;/div&gt;
&lt;div id="sigma-2"&gt;0&lt;/div&gt;
&lt;div id="option-mean-2"&gt;0&lt;/div&gt;

&lt;h1 id="more-advanced-scenarios"&gt;More Advanced Scenarios&lt;/h1&gt;
&lt;p&gt;Monte-Carlo simulation of terminal values is a relatively simple simulation, and one that is not too useful as this analysis can probably be completed analytically. The true power of Monte-Carlo simulation is unlocked when analysing scenarios that are more difficult to solve analytically, if not impossible. For example, if we wanted to analyse an American-style option, which can be exercised anytime, we might want to count the probability of a stock price exceeding the strike price at any time during the lifetime of that option. We could do this by counting how many trials cross the strike price boundry, which is easy to do with a Monte-Carlo simulation.&lt;/p&gt;</description><author>Sasha Kuznetsov's Blog</author><pubDate>Fri, 17 Dec 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.skz.dev/monte-carlo-options-pricing</guid></item><item><title>My Family Minecraft Survival Multiplayer Server built on AWS</title><link>https://zdwolfe.medium.com/my-family-minecraft-survival-multiplayer-server-built-on-aws-db392b61df77?source=rss-65d40a7904f------2</link><description>&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C8huuCcs1s9QGPJlDfug_A.png" /&gt;&lt;figcaption&gt;The armada of family and friends off to defeat the Ender Dragon&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Last month (November 18, 2021) marked the one-year birthday of my family Minecraft Survival Multiplayer server.&lt;/p&gt;&lt;p&gt;After over 1,500 hours of combined play time and 11 players at peak, I thought I’d share details on the setup and what I’ve learned so far hosting an active Minecraft server on AWS.&lt;/p&gt;&lt;p&gt;Some technologies I use include AWS EC2, ECS, S3, EFS, CDK, CloudWatch, Docker, Typescript, and PaperMC. One day I hope to release these AWS CDK Constructs to Github, so look out for that!&lt;/p&gt;&lt;h3&gt;Background&lt;/h3&gt;&lt;p&gt;In the winter of 2020, like many other people in our lives, my wife and I were in a rut of depressing news, pandemic anxiety, work stress, and local unrest in Seattle. Cut off from typical face-to-face social interactions at work and being unable to hang out with friends on the weekends, life was lonely and weird.&lt;/p&gt;&lt;p&gt;Enter: Familycraft 2020!&lt;/p&gt;&lt;p&gt;Looking for an escape and an excuse to hang out with loved ones for a dose of normalcy, I asked close friends and family members if they wanted to play Minecraft together over Thanksgiving holiday break.&lt;/p&gt;&lt;p&gt;As a software engineer familiar with hosting Java applications professionally, I also took it as an opportunity for a fun side project to host the Java-based Minecraft Server software myself, with a “backup plan” of renting a server from &lt;a href="https://apexminecrafthosting.com/"&gt;Apex&lt;/a&gt; Minecraft Hosting or similar if it didn’t work out or I lost enthusiasm.&lt;/p&gt;&lt;h3&gt;Self-Hosted Minecraft: Requirements&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dx9ibRvUzUSGw_BB1SzX7w.png" /&gt;&lt;/figure&gt;&lt;p&gt;Having hosted two Minecraft servers prior, a private world with my wife and a Happy Hour server with co-workers, both on a single cheap AWS EC2 instance, I had learned a set of requirements if I was going to host a world that others would cherish.&lt;/p&gt;&lt;p&gt;For “Version 2” of my attempts of self-hosting Minecraft, I wanted the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Automatic, frequent, durable backups of the world in case of disaster&lt;/li&gt;&lt;li&gt;Lots of RAM, a fast CPU, with 4 or more cores for minimal lag when up to 8 players connected&lt;/li&gt;&lt;li&gt;Fast networking (players were scattered around the US)&lt;/li&gt;&lt;li&gt;A Minecraft server version optimized for many players that receives frequent updates&lt;/li&gt;&lt;li&gt;Hosting infrastructure that was “one-click” simple to tweak&lt;/li&gt;&lt;li&gt;A locally-reproducible method of running the server so I could test new Minecraft versions, plugins and configuration changes on my PC before pushing them to “production”&lt;/li&gt;&lt;li&gt;An easy way to instantly “pull the plug” to save on hardware costs if people stopped playing that wouldn’t interfere with my other side projects&lt;/li&gt;&lt;li&gt;A voice channel and chatroom to rouse anyone interested in ‘crafting (Discord)&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;AWS: Go With What You Know&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tmNDy2UgtWdQNfKAh1NKKA.png" /&gt;&lt;figcaption&gt;hangin’ out at the in-game tavern with cousins across the country&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Being familiar with AWS professionally, I opted to go with what I know best. I knew EC2 would offer instances that could achieve the low network latency I needed for cheap enough, and S3 was an obvious world backup solution.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/682/1*AQc6YI8rAVMYk9kMq6EOPw.png" /&gt;&lt;/figure&gt;&lt;h3&gt;Docker: Packaged, Reproducible Runtime&lt;/h3&gt;&lt;p&gt;To run, the Minecraft server software needs a Java installation, a .jar file, and a writable directory for the world files, which seems easy enough.&lt;/p&gt;&lt;p&gt;To achieve my goal of being able to quickly run a carbon copy of the Minecraft world on my PC to test out new plugins, Minecraft versions, new EC2 instance type, or do any world file surgery, I chose Docker so the server was runnable as one neatly wrapped package.&lt;/p&gt;&lt;p&gt;After getting half-way through building my own Docker image, I found an outstanding open source version that did everything I wanted and more. The &lt;a href="https://github.com/itzg/docker-minecraft-server"&gt;itzg/docker-minecraft-server&lt;/a&gt; image acts as a configurable wrapper around the Minecraft server software, using environment variables to configure plugins, mods, admins, seeds, and version. Kudos to itzg for the awesome work.&lt;/p&gt;&lt;p&gt;I also found a configurable cronjob-PUT-to-S3 Docker image that would suit my needs for regular backups.&lt;/p&gt;&lt;h3&gt;AWS CDK: One-line Infrastructure Updates&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lnWxy7eUS-40HTY65Abamg.png" /&gt;&lt;figcaption&gt;the “amber Waynes of grain”&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;A challenge I had with a previously hosted Minecraft server was it was hard to change the instance, instance type, and JVM parameters powering the world.&lt;/p&gt;&lt;p&gt;This meant as the world got bigger and wanted an instance type with more RAM to run without lag, I needed to manually zip and scp files around, manually reconfigure firewall settings, reconfigure backup cronjobs, etc.&lt;/p&gt;&lt;p&gt;To avoid that headache this time around, I decided to spend the time up-front and to use &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS Cloud Development Kit&lt;/a&gt; (CDK) to define all of my Minecraft server resources as code and check them in to Git. This makes it simple to tweak things like instance type, make a “carbon copy” of the server, test out new instance types or versions, or roll back if something broke.&lt;/p&gt;&lt;p&gt;After getting a messy version to work, I broke up the stack into subcomponents, called CDK Constructs, to allow me to run multiple Minecraft worlds on a unique cluster or shared cluster, unique filesystem or shared, depending on what I wanted to do, and also to allow me to reason about components individually.&lt;/p&gt;&lt;h3&gt;CDK Constructs: Logical Pieces of Minecraft Infrastructure&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1001/1*e2dTCh9yEwL31W9dekCw3g.png" /&gt;&lt;/figure&gt;&lt;p&gt;Logically, I was thinking about the infrastructure in the following pieces:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;WorldCluster:&lt;/strong&gt; a collection of EC2 compute resources to run the Minecraft server instance&lt;/li&gt;&lt;li&gt;&lt;strong&gt;WorldPersistence:&lt;/strong&gt; a collection of runtime storage resources&lt;/li&gt;&lt;li&gt;&lt;strong&gt;WorldService:&lt;/strong&gt; the Minecraft game runtime&lt;/li&gt;&lt;li&gt;&lt;strong&gt;WorldBackup:&lt;/strong&gt; a component that periodically backs up the WorldService’s game files&lt;/li&gt;&lt;li&gt;&lt;strong&gt;WorldConfig:&lt;/strong&gt; a collection of per-instance configurations that are unique to the game — eg: the list of Admins, the Minecraft version (1.17, 1.18.1, etc), a list of plugins/mods, EC2 instance type, RAM&lt;/li&gt;&lt;li&gt;&lt;strong&gt;World:&lt;/strong&gt; Wraps all the above into one package, allowing me to easily add more server instances with a few lines of code.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Stack: &lt;/strong&gt;A collection of &lt;strong&gt;Worlds&lt;/strong&gt; (maybe a Universe would be a better name?)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;A World ends up looking something like this:&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/561/1*aOm8S7WBFClTMlQsC9LPmA.png" /&gt;&lt;/figure&gt;&lt;a href="https://medium.com/media/d53d14871cd583aa8aed090c0709c5bd/href"&gt;https://medium.com/media/d53d14871cd583aa8aed090c0709c5bd/href&lt;/a&gt;&lt;p&gt;..and if I ever want to add another server for testing, another group of people, playing with a new Minecraft update, etc, it’s a one-ish-liner to the stack:&lt;/p&gt;&lt;a href="https://medium.com/media/ea5a7e379add60b747495608807a48e6/href"&gt;https://medium.com/media/ea5a7e379add60b747495608807a48e6/href&lt;/a&gt;&lt;p&gt;..and a one-liner to update the stack&lt;/p&gt;&lt;a href="https://medium.com/media/78adc805c0cc2260d2c723891df713aa/href"&gt;https://medium.com/media/78adc805c0cc2260d2c723891df713aa/href&lt;/a&gt;&lt;p&gt;Looking under the hood, each logical component has several AWS resources that interact with each other.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/763/1*XfPLzsgfOEOcUakDrsDTdQ.png" /&gt;&lt;/figure&gt;&lt;a href="https://medium.com/media/57899c9645d93892cabb570859e609f4/href"&gt;https://medium.com/media/57899c9645d93892cabb570859e609f4/href&lt;/a&gt;&lt;h3&gt;Making Changes: CDK Shines&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_qJj6IauuZadAfyB.png" /&gt;&lt;figcaption&gt;“the colonies”&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;A few months into Familycraft we decided to defeat the Ender Dragon. By that time, our server had gained a few new members, and we wanted everyone to be online together for the big boss fight.&lt;/p&gt;&lt;p&gt;To prepare for that event, I bumped up the EC2 instance type from a t3.medium with 1GB RAM dedicated to the game to a more powerful c5a.large with more CPU cores, a dedicated processor, 2GB RAM and faster networking, which was as simple as making this small diff then rerunning cdk deploy:&lt;/p&gt;&lt;a href="https://medium.com/media/ab57541ff369fd6ce2d978f199168856/href"&gt;https://medium.com/media/ab57541ff369fd6ce2d978f199168856/href&lt;/a&gt;&lt;p&gt;It turns out that this was a good idea! During the event, in the hour-ish that everyone was in the same few chunks during The End Dragon fight, the CPU spiked up to &amp;gt; 47%, which might have overwhelmed the t3.medium.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*syLr6OV68_bGPB6xYmEnUg.png" /&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dRgZ8N2ANj0SiK2pQobmZw.png" /&gt;&lt;figcaption&gt;defeating the Ender Dragon&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Minecraft Server version updates were almost as simple, bumping the “VERSION” parameter from 1.16.4 to 1.17.1, and utilizing the hard work itzg put into getting the right Java versions set up.&lt;/p&gt;&lt;a href="https://medium.com/media/faf2c76944ca7c7dd32868a108faa52f/href"&gt;https://medium.com/media/faf2c76944ca7c7dd32868a108faa52f/href&lt;/a&gt;&lt;h3&gt;In Case Of Emergency: World Backups&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Praf-afV_aG805Qg_7nrg.png" /&gt;&lt;figcaption&gt;she can fly!&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;To prevent against any in-game disasters, glitches, etc, or my mistakes when hosting the server, I needed a ‘fire-and-forget’ backup system.&lt;/p&gt;&lt;p&gt;To achieve this, the Docker image &lt;em&gt;peterrus/s3-cron-backup&lt;/em&gt; runs as a sidecar to the Minecraft Server Docker image, peeking at the Minecraft World’s runtime EFS volume with read-only permissions, throwing them in a zip, and pushing to S3.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/308/1*KjNiAhTZve9LZawdAf1MKw.png" /&gt;&lt;/figure&gt;&lt;p&gt;The image operates on a cronjob of every other day at 7pm which balances storage costs with recovering in-game projects. I’ll also occasionally manually trigger a backup after someone completes something epic. Again, CDK really shines here, allowing me to configure the backup image and schedule using auto-generated resource names.&lt;/p&gt;&lt;a href="https://medium.com/media/c23c8fcdac61659d76f1558109bd6f1c/href"&gt;https://medium.com/media/c23c8fcdac61659d76f1558109bd6f1c/href&lt;/a&gt;&lt;h3&gt;Optimized For Multiplayer: PaperMC&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qfC5onGFTBdBPjE7.png" /&gt;&lt;/figure&gt;&lt;p&gt;In previous worlds I had used the vanilla Minecraft server jar &lt;a href="https://www.minecraft.net/en-us/download/server"&gt;directly from Mojang&lt;/a&gt;. These ran “okay”, but I saw frequent micro-stutters, rubber banding, and ghost blocks (where the client thinks there’s only air in a voxel but the server has a block), and after some Googling I found a few different versions of optimized server instances that claimed to have better performance when multiple people were online.&lt;/p&gt;&lt;p&gt;There’s a hilarious number of choices to pick from, including Spigot, PaperMC, Forge, Fabric, Airplane, Purpur, etc, etc, and I didn’t want to try them all. I read an &lt;a href="https://madelinemiller.dev/blog/paper-vs-spigot/"&gt;article&lt;/a&gt; by Maddy Miller suggesting PaperMC was the best, and after making some tweaks (re-enabling TNT duping), it definitely runs more smoothly than vanilla.&lt;/p&gt;&lt;p&gt;This doesn’t come without a cost though. For example, Minecraft 1.18: Caves and Cliffs was officially released Nov 30, 2021, however as of writing (Dec 16), PaperMC &lt;a href="https://github.com/PaperMC/Paper/issues/6995"&gt;has blocking bugs&lt;/a&gt;, which was a disappointment to have to wait.&lt;/p&gt;&lt;h3&gt;Docker Run: Testing Dangerous Changes Locally&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*75OnGTsgUSURw-DCoDmScA.png" /&gt;&lt;/figure&gt;&lt;p&gt;When Minecraft releases a new version, or there’s a new plugin/tool we want to try, it’s extremely useful to run the server locally on my PC before pushing those changes out to everyone that plays on the server.&lt;/p&gt;&lt;p&gt;To do this, I download a recent automatic update and run a few commands to start the server&lt;/p&gt;&lt;a href="https://medium.com/media/da95df052408d9617e3e021c5992a443/href"&gt;https://medium.com/media/da95df052408d9617e3e021c5992a443/href&lt;/a&gt;&lt;p&gt;and then configure the client to connect to localhost&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/488/1*q9emToUKNqe9ZGRCGl6xiw.png" /&gt;&lt;/figure&gt;&lt;p&gt;..then I run around the world, which is both served by and running on my laptop, and play around with the important farms and areas of the world.&lt;/p&gt;&lt;p&gt;This was crucial recently, where I discovered some breaking bugs in the 1.18 PaperMC update that would have griefed everyone.&lt;/p&gt;&lt;h3&gt;Craftin’ Tonight? Player Join Notifications&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/0*o6Ng70ZVRXORAdUM.png" /&gt;&lt;figcaption&gt;creeper farm + perimeter — beacon for scale&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;One kind-of awkward part of playing with a group was getting people to join around the same time to play together.&lt;/p&gt;&lt;p&gt;We use Discord for voice and video calls while we’re playing, so I spent a few hours writing an open source Spigot/Bukkit/Paper plugin, &lt;a href="https://github.com/zdwolfe/minecraft-player-notification-plugin"&gt;minecraft-player-notification&lt;/a&gt;, that sends a message to a Discord channel whenever a player joins a Minecraft server.&lt;/p&gt;&lt;p&gt;This nudges folks that someone’s around, and to pop in and say “hey” in-game.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/406/0*xHN3P-MHHizBKVCf.png" /&gt;&lt;/figure&gt;&lt;p&gt;I built this with all intentions of it only ever being used by our server, but to my delight it’s been used by something like 30 others as well!&lt;/p&gt;&lt;p&gt;The plugin works by intercepting PlayerJoinEvents, and using &lt;a href="https://github.com/zdwolfe/minecraft-player-notification-plugin/blob/master/src/main/java/io/github/zdwolfe/mc/playernotification/notification/http/HttpWebhookMessageSender.java#L19"&gt;HTTP POST Webhooks&lt;/a&gt; to notify a Discord channel.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/561/1*lDToRcVFICU3bbKE6JbslA.png" /&gt;&lt;/figure&gt;&lt;a href="https://medium.com/media/c2825a20c85eb5327a45ad4ff486f872/href"&gt;https://medium.com/media/c2825a20c85eb5327a45ad4ff486f872/href&lt;/a&gt;&lt;p&gt;It has a few configuration knobs, changed with a config file like this, and it would be easily extended to other notification types, like rich Discord messages, SMSes, etc.&lt;/p&gt;&lt;a href="https://medium.com/media/1f0760cdc0da17710ee39e20a380c559/href"&gt;https://medium.com/media/1f0760cdc0da17710ee39e20a380c559/href&lt;/a&gt;&lt;h3&gt;Fiddly Bits: ECS + EFS = :(&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hqEeqLbb1Fd4TZCQH4xWGw.png" /&gt;&lt;figcaption&gt;rare blue axolotyl!&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The most annoying component to get set up was the runtime world persistence. Getting EC2, Docker, ECS, and EFS to play nice took the most time of anything, and required a lot of trial and error.&lt;/p&gt;&lt;p&gt;The difficulty was in getting the Docker Container to allow connections to the EFS service backend, which required BRIDGE network mode and a few in/out ports punched in the EC2 Security Group.&lt;/p&gt;&lt;p&gt;I wasn’t able to find much documentation or prior art of how to achieve this in CDK, so this took a long time to figure out.&lt;/p&gt;&lt;a href="https://medium.com/media/7c2dca3e782c4911c0c659de3ab3e8b4/href"&gt;https://medium.com/media/7c2dca3e782c4911c0c659de3ab3e8b4/href&lt;/a&gt;&lt;h3&gt;What’s next?&lt;/h3&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aE0WDGAykO18dds_tNkF3Q.png" /&gt;&lt;/figure&gt;&lt;p&gt;One thing I want to add to the setup is a CloudWatch alarm-based auto-sleep system to save a few bucks per month on hosting. Whenever there’s a period of X hours with nobody connected, the ECS service should be scaled down and the EC2 instance paused. The only reason I haven’t done that yet is to make sure the server is available for players when they want it, so I also want to build a simple web portal that players can use to turn it on without my help.&lt;/p&gt;&lt;p&gt;Another thing I’d like to add is DNS. Occasionally when making updates to infrastructure, the EC2 Autoscaling Group decides it needs a fresh EC2 instance, which rotates the IPv4 address. To support that, I’ll eventually get around to a Route53 DNS address and subdomain for the Minecraft server.&lt;/p&gt;&lt;h3&gt;Appendix&lt;/h3&gt;&lt;h4&gt;Familycraft name&lt;/h4&gt;&lt;p&gt;We call our family Minecraft server Familycraft, and were unaware of “Familycraft Dad” at the time. We’re not associated with him in any way, but he seems like a cool guy.&lt;/p&gt;&lt;img alt="" height="1" src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=db392b61df77" width="1" /&gt;</description><author>Stories by Zach Wolfe on Medium</author><pubDate>Fri, 17 Dec 2021 08:47:19 GMT</pubDate><guid isPermaLink="true">https://zdwolfe.medium.com/my-family-minecraft-survival-multiplayer-server-built-on-aws-db392b61df77?source=rss-65d40a7904f------2</guid></item><item><title>List of Mormon History Association "Best Book" award books</title><link>https://www.davidschlachter.com/misc/mha-best-books</link><description>The MHA's list of their "Best Book" award winners only lists authors, not the book titles. Here's an easier list to browse.</description><author>David Schlachter</author><pubDate>Fri, 17 Dec 2021 02:36:00 GMT</pubDate><guid isPermaLink="true">https://www.davidschlachter.com/misc/mha-best-books</guid></item><item><title>Finding Sierpiński in the Oddest Places</title><link>https://ztoz.blog/posts/finding-sierpinski/</link><description>&lt;p&gt;This is a tale of finding fractals in an unexpected place and why the appearance makes sense in hindsight.&lt;/p&gt;
&lt;p&gt;I was looking for patterns in valid board game configurations, specifically the middle contesting row in the Game of Ur. In the game, there are eight slots either of the two players may occupy, but a slot may only be occupied by at most one player. I represented the available slots as two bit strings, with each bit representing a single slot. A valid placement is one where the two corresponding bits are both false or only one is true, but never both are true. Formally, this is equivalent to the bitwise formula \(\neg (p \land q)\) or \(p \uparrow q\).&lt;/p&gt;
&lt;p&gt;Plotting the p and q bitstrings as axes within a matrix, I saw:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Sierpinski Triangle in 256x256 matrix form" src="sierpinski256.png" /&gt;&lt;/p&gt;
&lt;p&gt;I recognized this set of repeated triangles as a Sierpiński Triangle (or Gasket). Described by Wacław Franciszek Sierpiński in 1915 (mathematically; there may have been artistic representations in the 13th century by the Cosmati&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;), the Gasket is an iterative structure with infinite scaling. The Triangle is considered a fractal (and one of the first discussed in any primer), but the discovery predates the field of fractal geometry. A typical way to describe the construction&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; is to inscribe a triangle with coordinates (0,0), (0,1), and (1,0). Then, inscribe three triangles via the transformations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;\((x,y) \rightarrow (\frac{1}{2}x, \frac{1}{2}y) \)&lt;/li&gt;
&lt;li&gt;\((x,y) \rightarrow (\frac{1}{2}x + \frac{1}{2}, \frac{1}{2}y) \)&lt;/li&gt;
&lt;li&gt;\((x,y) \rightarrow (\frac{1}{2}x, \frac{1}{2}y + \frac{1}{2}) \)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Animating this construction (note that this figure has been rotated versus the matrix orientation):&lt;/p&gt;
&lt;p&gt;&lt;img alt="Construction of a Sierpinski Triangle" src="https://www.wolframcloud.com/obj/85625c5a-11ad-47cd-9b03-755d36b4c3ea" title="Construction of Sierpinski Triangle" /&gt;&lt;/p&gt;
&lt;p&gt;Of course, the Triangle&amp;rsquo;s representation can also be rotated and projected without impacting the structure. Equilateral constructions are very popular.&lt;/p&gt;
&lt;p&gt;There are many ways to construct the Triangle, including many that aren&amp;rsquo;t graphical. The Triangle is registered as &lt;a href="https://oeis.org/A001317"&gt;OEIS A001317 Sierpiński&amp;rsquo;s triangle (Pascal&amp;rsquo;s triangle mod 2) converted to decimal&lt;/a&gt;. The catalog entry contains 16 formulas	and 17 links. Due to the link to Pascal&amp;rsquo;s Triangle, which has a tremendous number of constructions, this is a small subset of the known set.&lt;/p&gt;
&lt;p&gt;Looking through the catalog, I tried to find one that corresponded to how I was generating the Triangle but didn&amp;rsquo;t find one. The exclusive-or method &amp;mdash; \(a_{n+1} = a_n \otimes 2*a_n\) &amp;mdash; seemed like a good candidate, but I did not find an an algebraic equivalence.&lt;/p&gt;
&lt;p&gt;Since my formulation wasn&amp;rsquo;t generating integers but rather a binary matrix, I found a different formulation that was defined for binary matrices:&lt;/p&gt;
&lt;p&gt;$$a_{i=1,j=*}=1$$&lt;/p&gt;
&lt;p&gt;$$a_{i=*,j=1}=1$$&lt;/p&gt;
&lt;p&gt;$$a_{i,j} = a_{i-1,j} \otimes a_{i,j-1}$$&lt;/p&gt;
&lt;p&gt;This formulation can be easier to see in smaller matrices:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Sierpinski Triangle in 8x8 matrix form" src="sierpinski8.png" /&gt;&lt;/p&gt;
&lt;p&gt;I was still having difficulty finding an equivalence between this formula and mine, although the above seemed closer. In particular, I started thinking of the representations of binary numbers and how my ordering incorporated binary operations. Thinking that I might have discovered a novel representation, I expanded my research and found that, yes, someone had discussed this construction before (and they didn&amp;rsquo;t even treat it as novel). Alas.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Fractal Images of Formal Systems&lt;/em&gt;&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; explores the intersection of formal systems (e.g. propositional calculus) and fractals via visualizations. (Academic works tend to be what they say on the tin.) In one of the examples, the authors explore computing NAND (&lt;em&gt;not and&lt;/em&gt;; often depicted via the Sheffer stroke \(\uparrow\)) of two sentences of propositions. They found that ordering these sentences into ascending order and plotting the results yielded a Sierpinski Triangle. This approach is analogous to my application of NAND&amp;rsquo;ing two binary strings.&lt;/p&gt;
&lt;p&gt;So, why is the Triangle generated by the NAND of two bitstrings, projected onto a two-dimensional matrix? Drawing upon the discussion in &lt;a href="http://pgrim.org/fractal/Sierpins.html"&gt;&lt;em&gt;Fractal Images&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;First, we can consider one of the classic constructions (similar to before): For any given triangle, the interior points can be divided into two classes. The first class of points are ones where, if we measure the distance from them to the closest vertex of the surrounding triangle, a doubling of that distance would be outside the triangle. The second class of points are the ones where the doubling of the distance would be inside the triangle. The first class will form an empty inverted triangle in the center. Of the points in the second class, we can repeat the process by separating points that would be outside the triangle by twice doubling the distance versus ones that would be inside. A Sierpiński Triangle is formed via the infinite repetition of this process.&lt;/p&gt;
&lt;p&gt;How can we map the above construction of the Triangle into our NAND matrix world? Consider the NAND logic inscribed on a unit square, where the true values are connected to form a triangle:&lt;/p&gt;
&lt;p&gt;&lt;img alt="NAND Logic within a Unit Square" src="sierpinski-unit.png" /&gt;&lt;/p&gt;
&lt;p&gt;If we implement the doubling logic within this unit square, the logic acts as:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Closest Vertex&lt;/th&gt;
&lt;th&gt;Translation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A (Origin)&lt;/td&gt;
&lt;td&gt;\( (x,y) \rightarrow (2x, 2y) \)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B (1,0)&lt;/td&gt;
&lt;td&gt;\( (x,y) \rightarrow (1 - 2(1-x), 2y) \rightarrow (2x - 1, 2y) \)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C (0,1)&lt;/td&gt;
&lt;td&gt;\( (x,y) \rightarrow (2x, 1 - 2(1-y)) \rightarrow (2x, 2y - 1) \)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;(It may be helpful to think of an inward pointing vector from the vertex.)&lt;/p&gt;
&lt;p&gt;What does doubling mean in terms of numbers encoded as binary strings? Multiplying a binary number by 2 is equivalent to a left shift (and halving is equivalent to a right shift). Subtracting a one will invert the lowest bit (among other potential changes).&lt;/p&gt;
&lt;p&gt;Shifts either insert a zero at the lowest bit or remove the lowest bit from the string. A pair of zeros will not change the result of a NAND computation, so if two strings are valid, we can left shift them infinitely with no change. Similarly, if two strings are valid under NAND, stripping the lowest bits will not change the result of the computation.&lt;/p&gt;
&lt;p&gt;Subtracting the one will eliminate any one-bits on the lowest bit. If the lowest bit is not set, this leads to an arithmetic subtraction that may invert a number of bits depending on the original string (borrowing). How does borrowing avoid violating the NAND rules? For the two original strings, there are three possibilities for the lowest bits: x=0 y=0, x=0 y=1, and x=1 y=0. (The case of x=1 y=1 would be invalid per NAND.) For the second and third case, the subtraction will borrow from a placement of 1 leading to a valid string. For the first case, rule A does not require borrowing. Thus, the recursive nature of the construction remains valid under the rules of NAND and the Triangle is approximated by NANDing binary numbers.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Brunori P., Magrone P., Lalli L.T. (2019) Imperial Porphiry and Golden Leaf: Sierpinski Triangle in a Medieval Roman Cloister. In: Cocchiarella L. (eds) ICGG 2018 - Proceedings of the 18th International Conference on Geometry and Graphics. ICGG 2018. Advances in Intelligent Systems and Computing, vol 809. Springer, Cham. &lt;a href="http://www.formulas.it/sito/wp-content/uploads/2021/03/B-M-TedeschiniLalli_authorversion.pdf"&gt;Author PDF&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Falconer, Kenneth. &lt;em&gt;Fractals: A very short introduction&lt;/em&gt;. OUP Oxford, 2013.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Denis, Paul St, and Patrick Grim. &amp;ldquo;Fractal images of formal systems.&amp;rdquo; Journal of philosophical logic 26.2 (1997): 181-222. &lt;a href="https://philpapers.org/archive/DENFIO.pdf"&gt;PDF&lt;/a&gt; &lt;a href="http://pgrim.org/fractal/index.html"&gt;HTML&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>ℤ→ℤ</author><pubDate>Fri, 17 Dec 2021 02:23:08 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/finding-sierpinski/</guid></item><item><title>Millionaire's Shortbread</title><link>https://dustin.lammiman.ca/icecream/millionaire-shortbread/</link><description>&lt;h2 id="millionaire-s-shortbread-ice-cream" tabindex="-1"&gt;Millionaire's Shortbread Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/millionaire-shortbread/#millionaire-s-shortbread-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/millionaire-shortbread/#ingredients"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1/2 Cup Granulated Sugar&lt;/li&gt;
&lt;li&gt;2 Tbsp Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;3 Tbsp Cocoa Powder&lt;/li&gt;
&lt;li&gt;1 1/3 Cups Milk (I used 2%)&lt;/li&gt;
&lt;li&gt;3 Tbsp Water&lt;/li&gt;
&lt;li&gt;2 Tbsp Light Corn Syrup&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;li&gt;Shortbread Cookies&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/millionaire-shortbread/#instructions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;In a bowl whisk together sugar, milk powder, cocoa powder, and xanthan gum.&lt;/li&gt;
&lt;li&gt;In a pot add milk and corn syrup.&lt;/li&gt;
&lt;li&gt;Whisk dry ingredients into the pot of liquid ingredients. Be sure to mix well.&lt;/li&gt;
&lt;li&gt;Heat ingredients for ~5 minutes on medium heat, stirring well, until sugar is disolved.&lt;/li&gt;
&lt;li&gt;Remove from heat and add remaining cream and allow mixture to cool for a few minutes.&lt;/li&gt;
&lt;li&gt;Refridgerate for 12+ hours. Then churn according to your ice cream maker's instructions.&lt;/li&gt;
&lt;li&gt;When ice cream is almost finished churning, cut shortbread cookies into small pieces and add to the ice cream dasher.&lt;/li&gt;
&lt;li&gt;Once it is well mixed, move your ice cream into an airtight container. Layer half the ice cream, then caramel sauce, then the rest of the ice cream.&lt;/li&gt;
&lt;li&gt;Freeze the ice cream for 4+ hours before serving.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="caramel-sauce" tabindex="-1"&gt;Caramel Sauce &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/millionaire-shortbread/#caramel-sauce"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients-1" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/millionaire-shortbread/#ingredients-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;3/8 Cup Salted Butter (start with 1/2 cup of butter, and use 3/4 of that)&lt;/li&gt;
&lt;li&gt;3/4 Cup Brown Sugar (packed)&lt;/li&gt;
&lt;li&gt;1 Tbsp Water&lt;/li&gt;
&lt;li&gt;Pinch of Salt&lt;/li&gt;
&lt;li&gt;1/4 Cup Evaporated Milk (I know this is a small amount from a large can - every Christmas I make a batch of &lt;a href="https://www.myfoodandfamily.com/recipe/051833/fantasy-fudge"&gt;&lt;em&gt;fantasy fudge&lt;/em&gt;&lt;/a&gt; and so I'm always looking for recipes that will use the excess evaporate milk. If you don't already have an open can, there are plenty of great caramel recipes that use heavy cream that should work fine instead.)&lt;/li&gt;
&lt;li&gt;1 Tsp Vanilla Extract&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions-1" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/millionaire-shortbread/#instructions-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Melt butter, sugar, water, and salt together in small pan.&lt;/li&gt;
&lt;li&gt;Bring to a boil, then reduce to a high simmer for about 5 minutes.&lt;/li&gt;
&lt;li&gt;Remove from heat and add evaporated milk and vanilla.&lt;/li&gt;
&lt;li&gt;Mix well, then move to an airtight container and allow to cool completely in the refridgerator before adding to your ice cream.&lt;/li&gt;
&lt;/ol&gt;</description><author>Dustin.Lammiman</author><pubDate>Fri, 17 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/icecream/millionaire-shortbread/</guid></item><item><title>On digital gardening</title><link>https://holzer.online/articles/on-digital-gardening/index.html</link><description>&lt;p&gt;
On content-focused websites the prevalent pattern for the organizing content is the blog format: a list  of articles/posts presented in reverse-chronologically order. Amy Hoy wrote &lt;a href="https://stackingthebricks.com/how-blogs-broke-the-web/"&gt;a criticism of this organizational pattern&lt;/a&gt;, which found some resonance. So nowadays - as everything old is new again - a bunch of personal websites started to conscientiously style and organize themselves as &lt;a href="https://joelhooks.com/digital-garden"&gt;digital gardens&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Now, there is no copyright to that term, but one overarching concept I see and very much like is, that articles are not seen as something that is written once and then finished after the publication, but as a seed which might grow into something that eventually becomes evergreen, more akin to a well-organized public (read-only) wiki.
&lt;/p&gt;
&lt;p&gt;
I haven't yet figured out how I will this page in the long run, currently it is much more a blog than a garden, yet I find the idea to go back to existing article and enhance them over time very intriguing.
&lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Fri, 17 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/on-digital-gardening/index.html</guid></item><item><title>Device neutrality coming to Europe?</title><link>https://stop.zona-m.net/2021/12/device-neutrality-coming-to-europe/</link><description>&lt;p&gt;Yay. Partially, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 16 Dec 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/device-neutrality-coming-to-europe/</guid></item><item><title>Strangers from the internet paid for my MacBook Air: on my 4th year of indiehacking</title><link>https://maxrozen.com/2021-strangers-paid-my-macbook</link><description>In which I finally make money from strangers off the internet.</description><author>Max Rozen</author><pubDate>Thu, 16 Dec 2021 07:52:00 GMT</pubDate><guid isPermaLink="true">https://maxrozen.com/2021-strangers-paid-my-macbook</guid></item><item><title>Static Pods in Kubernetes</title><link>https://blog.yelinaung.com/posts/static-pods-16th-dec-2021/</link><description>&lt;p&gt;After taking the &lt;a href="https://blog.yelinaung.com/posts/ckad-experience/"&gt;CKAD&lt;/a&gt; exam, I am planning to take the (harder) &lt;a href="https://www.cncf.io/certification/cka/"&gt;CKA&lt;/a&gt; exam.
As I was studying for it, I stumbled upon &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/#static-pods"&gt;Static Pods&lt;/a&gt; in Kubernetes.
It is slightly different from the regular Pods.&lt;/p&gt;
&lt;p&gt;This is the normal Kubernetes cluster components&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kubernetes Constructs Concepts and Architecture" src="https://platform9.com/wp-content/uploads/2019/05/kubernetes-constructs-concepts-architecture.jpg" /&gt;
From &lt;a href="https://platform9.com/blog/kubernetes-enterprise-chapter-2-kubernetes-architecture-concepts/"&gt;Cloud9 Kubernetes Concepts and Architecture&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Learn more about the Control plane components &lt;a href="https://kubernetes.io/docs/concepts/overview/components/#control-plane-components"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s imagine there is a Kubernetes cluster with no Schedular or Controller or even no Master Node.
There are worker nodes. On each worker node, there is &lt;a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/"&gt;kubelet&lt;/a&gt; running.
Now, how can we tell &lt;code&gt;kubelet&lt;/code&gt; to run a Pod itself?&lt;/p&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Thu, 16 Dec 2021 06:23:13 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/posts/static-pods-16th-dec-2021/</guid></item><item><title>The Intermediate Engineer Syndrome</title><link>https://www.pseudofreedom.com/p/the-intermediate-engineer-syndrome</link><description>Graduating with a Computer Science degree from Duke University, Wen followed some of his friends into a well respected tech company. Wen worked hard, consistently hit his goals at work, and gained a great deal of technical competency in the first 18 months. A timely promotion to Software Engineer II came just in time for a confidence booster.</description><author>PseudoFreedom</author><pubDate>Thu, 16 Dec 2021 03:39:25 GMT</pubDate><guid isPermaLink="true">https://www.pseudofreedom.com/p/the-intermediate-engineer-syndrome</guid></item><item><title>Kubernetes Quickstart</title><link>https://jasoneckert.github.io/myblog/kubernetes-quickstart/</link><description>&lt;p&gt;Over the past two decades, we&amp;rsquo;ve shifted from running servers bare metal to running them almost exclusively within virtual machines (VMs). More recently we&amp;rsquo;ve seen another shift to running servers within containers. Like VMs, containers are servers that contain a unique filesystem, IP stack and software. But unlike VMs, containers do not contain a full operating system or operating system kernel. Instead they rely on an underlying operating system that runs the container using a &lt;em&gt;&lt;strong&gt;container runtime&lt;/strong&gt;&lt;/em&gt;, such as Docker. Consequently, containers are much smaller than VMs and ideal for running software within cloud environments where resource utilization must be kept to a minimum to control costs.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Thu, 16 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/kubernetes-quickstart/</guid></item><item><title>Running SQL Server in a container on Github Actions</title><link>http://notes.eatonphil.com/sqlserver-in-github-actions.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-12-16-sqlserver-in-github-actions.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Thu, 16 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/sqlserver-in-github-actions.html</guid></item><item><title>Why you want labels for software, just like for food</title><link>https://stop.zona-m.net/2021/12/why-you-want-labels-for-software-just-like-for-food/</link><description>&lt;p&gt;Even more, possibly.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 15 Dec 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/why-you-want-labels-for-software-just-like-for-food/</guid></item><item><title>TIL how to create Github repos with Github</title><link>https://www.zufallsheld.de/2021/12/15/til-how-to-create-github-repos-with-github/</link><description>&lt;p&gt;I needed to create multiple Github repositories and grant access to them for different users. After creating the first repository by hand and granting access to it, I had enough of it. There are just too many damn&amp;nbsp;clicks!&lt;/p&gt;
&lt;p&gt;So I used the &lt;span class="caps"&gt;CLI&lt;/span&gt;. Github has a nice &lt;a href="https://cli.github.com/"&gt;cli-program&lt;/a&gt; called …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Wed, 15 Dec 2021 15:55:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/12/15/til-how-to-create-github-repos-with-github/</guid></item><item><title>Exploring the Nature of a PC</title><link>https://one.mikro2nd.net/pages/an-os-for-thinking/</link><description>In which I speculate about some of the attributes I&amp;rsquo;d want to see in a Personal Computer that takes seriously its job of uplifting my ability to think, understand, explore and organise information.
An Upsight:
When it comes to taking notes during meetings and reviews I reach for pencil and paper. Not the keyboard (though others seem comfortable doing so). Is it just that other folk are more fluent at the keyboard than I?</description><author>one mikro2nd</author><pubDate>Wed, 15 Dec 2021 14:37:10 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/an-os-for-thinking/</guid></item><item><title>New York, 2021</title><link>https://faingezicht.com/photos/2021/12/15/nyc2021/</link><description>After a weekend in &lt;a href="/photos/2021/12/11/philly/"&gt;Philly&lt;/a&gt;, our Thanksgiving trip continued with my family in New York City. My mom flew in from Costa Rica, while Max and Einat drove from Boston with Rollo in the back. Hopefully next time Danny, Caro and the kids will get to join us.

I also brought my film camera, so expect a few stragglers to come from that in a second post later on.</description><author>Avy Faingezicht</author><pubDate>Wed, 15 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/12/15/nyc2021/</guid></item><item><title>How not to learn Rust</title><link>https://dystroy.org/blog/how-not-to-learn-rust/</link><description>&lt;p&gt;I've seen too many good programmers struggle learning Rust, or even give up.&lt;/p&gt;
&lt;p&gt;Here are the mistakes I've seen which may make you fail at learning Rust.
I hope this list will help you avoid them.&lt;/p&gt;</description><author>dystroy|Canop / blog</author><pubDate>Wed, 15 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://dystroy.org/blog/how-not-to-learn-rust/</guid></item><item><title>The Foolproof Scones</title><link>https://blog.cetinich.net/content/food/2021-scones/</link><description>These scones take max 30 mins to prep and cook and are very simple and
enjoyable.</description><author>Brent's Blog</author><pubDate>Wed, 15 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.cetinich.net/content/food/2021-scones/</guid></item><item><title>Certain "fact checks" are nothing more than opinion</title><link>https://stop.zona-m.net/2021/12/certain-fact-checks-are-nothing-more-than-opinion/</link><description>&lt;p&gt;At least on Facebook, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 15 Dec 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/certain-fact-checks-are-nothing-more-than-opinion/</guid></item><item><title>How to find a namespace number</title><link>https://river.me/blog/find-namespace-number/</link><description>Here&amp;rsquo;s how you can find the number of a particular namespace</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 14 Dec 2021 17:28:29 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/find-namespace-number/</guid></item><item><title>Advent of Code 2021 - Day 2</title><link>https://geekmonkey.org/aoc2021-day2/</link><description>The advent of code is an annual coding challenge in the month of December. This article covers the second day's puzzle with a solution written in Go.</description><author>geekmonkey</author><pubDate>Tue, 14 Dec 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/aoc2021-day2/</guid></item><item><title>Converting my init.vim to lua</title><link>https://blog.imraniqbal.org/neovim-lua/</link><description>&lt;p&gt;I use neovim as my main driver for all my coding and text editing needs.
They released version 0.5 (up to 0.6 now) &lt;a href="https://github.com/neovim/neovim/pull/12235" rel="external"&gt;which supports having your vim configuration in lua (5.1 via LuaJIT) instead of in vimscript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've never used lua before but it's a nice small language. LuaJIT is also crazy fast.
You can find most of what you need to know with either &lt;code&gt;:h lua&lt;/code&gt; and some &lt;a href="https://learnxinyminutes.com/docs/lua/" rel="external"&gt;syntax reference&lt;/a&gt;.
One of the nicest changes is the ability to use maps for storing configuration for plugins. For example the following &lt;a href="https://github.com/dense-analysis/ale" rel="external"&gt;ale&lt;/a&gt; configuration:&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span style="color: #FFA659;"&gt;let&lt;/span&gt;&lt;span&gt; g:ale_fixers&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   '*'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'remove_trailing_lines'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt; 'trim_whitespace'&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   'python'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'autopep8'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt; 'black'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt; 'isort'&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   'javascript'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'eslint'&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   'vue'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'eslint'&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   'dhall'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'dhall-format'&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   'go'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'gofmt'&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;   'rust'&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;'rustfmt'&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #F29E74;"&gt;\&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Becomes this:&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span&gt;vim.g.ale_fixers&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span style="color: #F29E74;"&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;remove_trailing_lines&amp;quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;trim_whitespace&amp;quot; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    python&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;autopep8&amp;quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;black&amp;quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;isort&amp;quot; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    javascript&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;eslint&amp;quot; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    vue&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;eslint&amp;quot; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    dhall&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;dhall-format&amp;quot; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    go&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;gofmt&amp;quot; &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;    rust&lt;/span&gt;&lt;span style="color: #F29E74;"&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span style="color: #D5FF80;"&gt;&amp;quot;rustfmt&amp;quot; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some rough spots still exist (e.g. auto commands) but there is a nice &lt;code&gt;vim.cmd&lt;/code&gt; escape hatch that can still take regular vimscript. Super excited to see what else is in store for this project!&lt;/p&gt;</description><author>Imran's Blog</author><pubDate>Tue, 14 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.imraniqbal.org/neovim-lua/</guid></item><item><title>Hide and Seek by Fiona Benson</title><link>https://ho.dges.online/words/commonplace/hide-and-seek-by-fiona-benson/</link><description>&lt;p&gt;*This poem really affected me when I read it.&lt;/p&gt;
&lt;p&gt;After her swim I wrap my child warm&lt;br /&gt;
and take her to the changing room&lt;br /&gt;
and lay her down to dry. She holds the corners&lt;br /&gt;
of the towel up over her face&lt;br /&gt;
like a soft, turquoise tent and yells&lt;br /&gt;
‘Hide and seek! Hide and seek!’&lt;br /&gt;
I life an edge and shout ‘Boo!’&lt;br /&gt;
and she shrieks with laughter -&lt;br /&gt;
I can feel the heat rising from her body&lt;br /&gt;
and smell the chlorine - she hides again,&lt;br /&gt;
and again. I peek under and she’s beside herself&lt;br /&gt;
with happiness - she’s at an age where she thinks&lt;br /&gt;
that if she just stands still in the middle of the lawn&lt;br /&gt;
I will not see her, that somehow she is gone -&lt;br /&gt;
but always, in the pockets behind this game,&lt;br /&gt;
there is this residue, this constriction,&lt;br /&gt;
families squeezed behind false walls&lt;br /&gt;
or hidden under the floor. I think of the soldier&lt;br /&gt;
sensing the hollow under his sole&lt;br /&gt;
and prying up the board on all those cramped&lt;br /&gt;
and flinching humans; but mostly I think&lt;br /&gt;
of the mothers, their hearts jumping out of their mouths&lt;br /&gt;
trying to shush their children - my first-born now,&lt;br /&gt;
who’s never been able to do as she’s told,&lt;br /&gt;
how she’d have writhed and screamed and bitten like a cat&lt;br /&gt;
if I’d tried to hold her quiet, how I’d have hurt her,&lt;br /&gt;
clamping her mouth, trying to keep her still.&lt;br /&gt;
The trapdoor is always opening, the women and children&lt;br /&gt;
are herded into the yard - and I ask myself if,&lt;br /&gt;
when my daughters were pulled from me,&lt;br /&gt;
I would fight and scream to keep them,&lt;br /&gt;
or let them go gently, know&lt;br /&gt;
there was nothing to be done?&lt;br /&gt;
If we were pushed into the showers&lt;br /&gt;
would I pretend it was only time to get them clean?&lt;br /&gt;
We are not meant to write of the Sho’ah,&lt;br /&gt;
we who were not there, but on bad days it’s all I can think of,&lt;br /&gt;
the mothers trying to shield their children with their bodies&lt;br /&gt;
under the showers, screaming for mercy, begging for rain.&lt;br /&gt;
And it’s never over - here are the children&lt;br /&gt;
riding to the border in fridges as the air becomes hot and thin,&lt;br /&gt;
their tiny bodies glowing like bright sardines&lt;br /&gt;
on the customs officer’s hand-held scan;&lt;br /&gt;
and here is the tribesman carrying your husband’s genitals&lt;br /&gt;
and a bloody machete, and you are a mother&lt;br /&gt;
running for your life with a baby tied to you back&lt;br /&gt;
and two children by the hand&lt;br /&gt;
but one small son is falling behind;&lt;br /&gt;
Jesus fucking Christ, I don’t know who&lt;br /&gt;
I’m teaching you to hide from, but look&lt;br /&gt;
how eagerly you learn.&lt;/p&gt;</description><author>ho.dges.online</author><pubDate>Tue, 14 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/hide-and-seek-by-fiona-benson/</guid></item><item><title>Intentional weakness, getting a job in tech, and OSS funding - 123dev #50</title><link>https://justingarrison.com/123dev/2021-12-14-123dev-50/</link><description>I'm back to custom issue titles. I'll add a small intro here too. Welcome all th</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 14 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-12-14-123dev-50/</guid></item><item><title>On Satisficing</title><link>https://holzer.online/articles/satisficing/index.html</link><description>&lt;p&gt;
        Every day we need to decide many things and many of those decisions are of minor importance. Lots of
        trivial decisions to make can create neural fatigue and lead to unproductivity and loss of focus.
    &lt;/p&gt;
    &lt;p&gt;
        Herbert Simon coined the word &lt;i&gt;satisficing&lt;/i&gt; for a strategy that concentrates on getting an option
        that is good enough, but possibly not the very best. Satisficing is one of the foundations of productive
        human behaviour because it avoids wasting time on finding improvements that are not going to make a
        significant difference.
    &lt;/p&gt;
    &lt;p&gt;
        Yet, satisficing should not be applied to very high-priority endeavours where much is at stake. There
        pursuing excellence might actually be the better strategy. But those are few and far between. To have
        the capacity to make them happen, satisficing is an imperative.
    &lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Tue, 14 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/satisficing/index.html</guid></item><item><title>WhatsApp and the domestication of users</title><link>https://seirdy.one/posts/2021/01/27/whatsapp-and-the-domestication-of-users/</link><description>WhatsApp's rise and recent PR efforts highlight a class of business models that I call "user domestication".</description><author>All content on Seirdy’s Home</author><pubDate>Tue, 14 Dec 2021 01:26:03 GMT</pubDate><guid isPermaLink="true">https://seirdy.one/posts/2021/01/27/whatsapp-and-the-domestication-of-users/</guid></item><item><title>Setting up Katex - Maths in Hugo</title><link>https://yasha.solutions/posts/setting-up-katex-maths-in-hugo/</link><description>So, i had this planned for a while to add some more math content to this blog.
Mostly for the content I do in french, where I do a lot of school related content for the kids but also for here, in english, where I will likely need it later when adding content about recommender system and other ML topics.
Anyway, figured out that Latex is the way to go usually when you want to do some maths in a paper, and well, turns out there is a library for this name Katex and it&amp;rsquo;s super simple to integrate in Hugo.</description><author>Yasha Solutions</author><pubDate>Tue, 14 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/setting-up-katex-maths-in-hugo/</guid></item><item><title>Will my new blood pressure monitor be the thing to get me to finally meditate?</title><link>https://liza.io/will-my-new-blood-pressure-monitor-be-the-thing-to-get-me-to-finally-meditate/</link><description>&lt;p&gt;I know meditation is &lt;em&gt;good&lt;/em&gt; for me, but I&amp;rsquo;ve never been able to stick with the darn stuff. I just&amp;hellip;don&amp;rsquo;t do it. I do something else  - &lt;em&gt;anything else&lt;/em&gt; - instead. I&amp;rsquo;m always too busy, or too anxious or excited to move on to other things.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Mon, 13 Dec 2021 21:02:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/will-my-new-blood-pressure-monitor-be-the-thing-to-get-me-to-finally-meditate/</guid></item><item><title>Link rot, explained with just one post</title><link>https://stop.zona-m.net/2021/12/link-rot-explained-with-just-one-post/</link><description>&lt;p&gt;One post, and 45 images. Missing images.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 13 Dec 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/link-rot-explained-with-just-one-post/</guid></item><item><title>Guide to 3D OSM Building Models - Milan Edition</title><link>https://geo.rocks/post/guide-to-3d-osm-building-models/</link><description>&lt;p&gt;&lt;img alt="Milano Tre Torri" src="https://geo.rocks/img/3d-tre-torri.png" title="Milano Tre Torri" /&gt;&lt;/p&gt;
&lt;p&gt;OSM data offers a vast amount of information about our cities. In this guide I focus on working and styling OSM building data for 3D-modeling in Aerialod for close-to-reality models.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tl;dr: Use OSM building levels, emulate roofs with shapeburst effect and use Sketchfab for interactive 3D models&lt;/strong&gt;&lt;/p&gt;</description><author>Geography &amp;amp; Coding</author><pubDate>Mon, 13 Dec 2021 16:54:06 GMT</pubDate><guid isPermaLink="true">https://geo.rocks/post/guide-to-3d-osm-building-models/</guid></item><item><title>There and Back Again: GraphQL at strongDM</title><link>https://etodd.io/2021/12/13/there-and-back-again-graphql-at-strongdm/</link><description>&lt;p&gt;
&lt;em&gt;This post originally appeared on the &lt;a href="https://www.strongdm.com/blog/graphql-at-strongdm"&gt;strongDM engineering blog&lt;/a&gt;.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
Once upon a time, strongDM had no dedicated frontend engineers.
We backend engineers dipped our toes in the React frontend as infrequently as possible.
It relied on dusty, bespoke, private REST endpoints that returned schema-less JSON blobs.
We let these languish while we built a shiny new public API with code-generated SDKs in five languages.
Occasionally I was forced to write a new private endpoint for the frontend.
I did so with shame, dreaming of a future when the whole frontend would only talk to the public API.
Then one day, we hired a frontend engineer, and everything changed.
&lt;/p&gt;</description><author>Evan Todd</author><pubDate>Mon, 13 Dec 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://etodd.io/2021/12/13/there-and-back-again-graphql-at-strongdm/</guid></item><item><title>ZFS RAIDZ2 - Achieving 157 GB/s</title><link>https://neil.computer/notes/zfs-raidz2/</link><description>&lt;hr /&gt;&lt;!--kg-card-begin: html--&gt;&lt;p class="text-bold dark-red"&gt;Update: &lt;a href="https://neil.computer/notes/zfs-raidz2/#note-5"&gt;See Note 5 below &lt;/a&gt;. 157 GB/s is a misleading bandwidth due to the way fio lib handles the --filename option. Actual bandwidth is approximately 22 GB/s, which is still mighty impressive.&lt;/p&gt;&lt;!--kg-card-end: html--&gt;&lt;hr /&gt;&lt;p&gt;I built a new server that&amp;apos;s also going to serve as a NAS. It&lt;/p&gt;</description><author>Neil Panchal</author><pubDate>Mon, 13 Dec 2021 05:31:34 GMT</pubDate><guid isPermaLink="true">https://neil.computer/notes/zfs-raidz2/</guid></item><item><title>Aten: introduction and the first plot</title><link>https://boerman.dev/posts/aten/price-volatility-2021/</link><description>&lt;p&gt;As those that I have spoken with personally recently are well aware by now, I am working at TenneT at the System Operation Transport division, mainly on flowbased capacity calculation and bidding zone review topics. I have chosen TenneT because I wanted to play a part in something that both fascinates my and in which I hope to make a small difference for good: the energytransition and the energy market.&lt;br /&gt;
The more I work on it the more I become fascinated by all the intricate details and endless possibilities to analyse it, encouraged by my helpful co-workers. From this fascination a new hobby was born: project Aten. I like to name my projects after greek-roman, old norse or ancient egyptian mythology and this one is named after the &lt;a href="https://en.wikipedia.org/wiki/Aten"&gt;egyptian sungod Aten&lt;/a&gt;.&lt;/p&gt;</description><author>Blog Frank Boerman</author><pubDate>Mon, 13 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://boerman.dev/posts/aten/price-volatility-2021/</guid></item><item><title>The benefits of a classless approach to CSS</title><link>https://holzer.online/articles/benefits-of-classless-css/index.html</link><description>&lt;p&gt;
I first experienced the web in the mid-nineties. At that time CSS wasn't really around and even if the first browsers supported it, many pages were just text-heavy unstyled documents. Today you would consider such an approach as luddite, yet since the H in HTML stands for hyper&lt;i&gt;text&lt;/i&gt; you could argue that keeping styling concerns out of markup is the purest form of authoring a document for the web.
&lt;/p&gt;
&lt;p&gt;
Today's default choices are quite distinguishably different from that. A minified version of Bootstrap 4 ships 117kB. As a comparison: I would have to write about 60 to 80 additional articles on this site to match that weight with an equal portion of content. And while at it, I'd have to clutter my markup with classes over classes. There are of course slimmer css frameworks than Bootstrap, but nonetheless, my main motivation is in the writing down useful things, therefore I decided to approach the styling of this site quite radically with classless CSS. All styling decisions are taken by CSS which is targeted by element selectors only. If I'd bother to minify it, that would clock in little over half a kilobyte, and even unminified it is less than 1% of bootstrap.
&lt;/p&gt;
&lt;p&gt;
But page weight isn't the most important thing. A classless CSS approach enables me to author content without intermediate representations (e.g. the Markdown flavour du jour in combination with a static site generator) directly in HTML while still allowing me to focus on the content.
&lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Mon, 13 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/benefits-of-classless-css/index.html</guid></item><item><title>Opt Out of Cynicism</title><link>http://dimitarsimeonov.com/2021/12/12/opt-out-of-cynicism</link><description>&lt;blockquote&gt;
  &lt;p&gt;A cynic is a man who knows the price of everything, and the value of nothing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Oscar Wilde
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Welcome to part 8 of the series on “How to be a game changer”. You can find the previous posts here &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;1&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;2&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer"&gt;3&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources"&gt;4&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/21/exiting-artificial-scarcity"&gt;5&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/28/climb-your-own-status-everest"&gt;6&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/12/06/the-sorting-hat-of-sales-mindset"&gt;7&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Growing up in post-socialist-turned-cowboy-capitalist Bulgaria, I grew up around a lot of cynical behavior and absorbed it deep into me. It was the &lt;a href="https://www.youtube.com/watch?v=8CrOL-ydFMI"&gt;water&lt;/a&gt; I was swimming in, and I knew no better. There was always this feeling that attempts at improvements are futile. If anyone tried to improve the system in any way, they will face a great opposition, and any value they bring forward will be immediately vultured away. This made it obvious for me to see how any changes will be abused and rendred futile. I also became good at rationalizing the existing status quo. There’s this example which stuck with me, that if someone created a coin operated parking meter, another one will quickly figure out how to steal the coins out of it. Thus, the attempt to bring order will fail, and the rationalization is that we are a motivated but backstabbing people which get in our own interest.&lt;/p&gt;

&lt;p&gt;And today, I still don’t know much better, and sometimes view the world through cynical eyes. A cynicism frame and mindset that turns a lot of my life into a zero-sum game. When I recently realized that I’ve been carrying this cynical mindset for a long time, my legs felt soft, and my body started shaking. I was reckoning all the mistakes I’ve made with this mindset.&lt;/p&gt;

&lt;p&gt;But what do I mean by being cynical? When you’re conversing with someone, it is often easy to detect the frame through which they view the world and the interaction with you. Cynics think that the world is never changing on a fundamental level, but it only changes cosmetically, and superficially. Cynics may look at some new trend and call it “lipstick on a pig”. According to cynics, the world operates in a certain way, the elites are always the same, and will always be the same. The cynics think we are always playing the same game that has always been played. For example, a cynic may think that media’s only purpose is to sell your attention to advertisers - like I did &lt;a href="https://dimitarsimeonov.com/2017/10/27/the-shifting-sweet-spot-for-media"&gt;here&lt;/a&gt;. And often they might be right.&lt;/p&gt;

&lt;p&gt;We never try to be cynical. At least not consciously. Instead, we try to make models of the world. And we often simplify those models by ignoring the fact that the world today is different than the world ten years ago, and alien to the world a hundred years ago. We take a mental shortcut, but we end up in a dead end.&lt;/p&gt;

&lt;p&gt;&lt;img alt="cynicism leads to a dead end" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/08end.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Optimists, and even sometimes pessimists, see the world changing, for better or worse, and with it, the games we play change as well. They’re also both right to some degree. Cynics have fixed mindset, whereas optimists have growth mindset. The opposite of an optimist isn’t a pessimist, as they both realize that the world is changing and time moves forward. A pessimist also has a growth mindset, except they have a negative sign in front of it. The opposite of an optimist is the cynic.&lt;/p&gt;

&lt;p&gt;We are more likely to detect cynicism in others than in ourselves. For example if you approach a friend suggesting you both bake a pie together, they start talking about dividing the pie - they have the cynical fixed mindset. Or if a friend complains a lot, that’s also a cynical mindset - they don’t see a way to change the world, or their environment, and all they have left is complaining.&lt;/p&gt;

&lt;p&gt;When we detect in others, we’ll be more likely to detect in ourselves as well, and not only filter cynics out of our lives, but sometimes re-align them to a growth mindset, and attract other partners with growth mindset. Growth mindset says “yes and…” instead of “yes, but…”, or “no, but…”. And we may even re-align ourselves, to start recognizing all the ways the world may change - for better or worse.&lt;/p&gt;

&lt;p&gt;One of the strongest medicines against cynical mindset which I’ve tried is to do a “&lt;a href="https://tim.blog/2007/09/18/real-mind-control-the-21-day-no-complaint-experiment/"&gt;no-complain challenge&lt;/a&gt;”. I find its effects to be enormous and durable for years. In this challenge, you move a bracelet from one wrist to the other when you catch yourself complaining. The goal is to make it 21 days without having move the wrist. I did it years ago, and by the end of it, it felt as if I am in a different universe. With the years, some of its effect has rubbed off, and I am due a refresher.&lt;/p&gt;

&lt;p&gt;&lt;img alt="shred complaining in 21 days" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/08complaining.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;By realizing that the world is always changing, and by stopping to complain, we opt out of seeing the world through cynical glasses, and become part of the change. When we realize that our life can change, we can change it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Sun, 12 Dec 2021 23:44:28 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/12/12/opt-out-of-cynicism</guid></item><item><title>Advent of Code 2021 - Day 1</title><link>https://geekmonkey.org/aoc2021-day1/</link><description>The advent of code is an annual coding challenge in the month of December. This article covers the first day's puzzle with a solution written in Go.</description><author>geekmonkey</author><pubDate>Sun, 12 Dec 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/aoc2021-day1/</guid></item><item><title>Plain Org v1.1 released 🎄☃️</title><link>https://xenodium.com/plain-org-v11-released</link><description>&lt;p&gt;&lt;a href="https://plainorg.com"&gt;Plain Org&lt;/a&gt; v1.1 is now available on the &lt;a href="https://apps.apple.com/app/id1578965002"&gt;App Store&lt;/a&gt;. The update receives new features and bugfixes.&lt;/p&gt;
&lt;p&gt;If you're finding Plain Org useful, &lt;strong&gt;please help support this effort&lt;/strong&gt; by getting the word out. Tell your friends, &lt;a href="https://twitter.com/intent/tweet?text=Plain%20Org%20https%3A%2F%2Fapps.apple.com%2Fapp%2Fid1578965002%20"&gt;tweet&lt;/a&gt;, or blog about it.&lt;/p&gt;
&lt;h2&gt;What is Plain Org?&lt;/h2&gt;
&lt;p&gt;Ok, now on to what's included in the v1.1 release…&lt;/p&gt;
&lt;h2&gt;Compact mode&lt;/h2&gt;
&lt;p&gt;By default, Plain Org layout uses generous padding. The new option &lt;code&gt;Menu -&amp;gt; View -&amp;gt; Compact mode&lt;/code&gt; packs more content into your screen.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/compact.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Regroup active and inactive tasks&lt;/h2&gt;
&lt;p&gt;Regrouping tasks now bubbles active ones up. Similarly, inactive tasks drop to the bottom of their node. Changes are persisted to the org file.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/regroup.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Native table rendering&lt;/h2&gt;
&lt;p&gt;Tables are now rendered natively but also support displaying links and other formatting within cells.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/table.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Open local ID links&lt;/h2&gt;
&lt;p&gt;If your file provider supports granting access to folders, local ID links (ie. &lt;code&gt;id:eb155a82-92b2-4f25-a3c6-0304591af2f9&lt;/code&gt;) can now be resolved and opened from Plain Org. Note that for ID links to resolve, other org files must live in either the same directory or a subdirectory.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/idlink.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Fill paragraphs&lt;/h2&gt;
&lt;p&gt;If your org paragraphs contain newlines optimizing for bigger screens, you can toggle &lt;code&gt;Menu -&amp;gt; View -&amp;gt; Fill paragraph&lt;/code&gt; to optimize rendering for your iPhone. This rendering option makes no file modifications.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/fillparagraph.gif" /&gt;&lt;/p&gt;
&lt;p&gt;By the way, the previous screenshot text comes from &lt;a href="http://doc.norang.ca/org-mode.html"&gt;Org Mode - Organize Your Life In Plain Text&lt;/a&gt;, a magnificent org resource.&lt;/p&gt;
&lt;h2&gt;Show/hide basic scheduling&lt;/h2&gt;
&lt;p&gt;Use the new &lt;code&gt;Menu -&amp;gt; View -&amp;gt; Scheduling&lt;/code&gt; to toggle showing &lt;code&gt;SCHEDULED&lt;/code&gt; or &lt;code&gt;DEADLINE&lt;/code&gt; dates.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/scheduling.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Show/hide tags&lt;/h2&gt;
&lt;p&gt;Similarly, the new &lt;code&gt;Menu -&amp;gt; View -&amp;gt; Tags&lt;/code&gt; option toggles displaying tags.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/hidetags.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Native list rendering&lt;/h2&gt;
&lt;p&gt;Lists are now rendered natively. With the exception of numbered cases, list items now share a common bullet icon. Description lists are also recognized and receive additional formatting when rendered.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-org"&gt;- First list item
* Second list item
+ Third list item
1. Numbered list item
+ Term :: Description for term
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/listitems.png" /&gt;&lt;/p&gt;
&lt;p&gt;Numbered checkboxes are now recognized and receive the same formatting and interaction as their non-numbered counterparts.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-org"&gt;1. [ ] First checkbox
2. [X] Second checkbox
3. [X] Third checkbox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-v11-released/numbered.png" /&gt;&lt;/p&gt;
&lt;h2&gt;Reload current file&lt;/h2&gt;
&lt;p&gt;Plain Org may not be able to automatically reload files for some syncing providers. In those instances, use &lt;code&gt;Menu -&amp;gt; Reload&lt;/code&gt; to explicitly request a reload.&lt;/p&gt;
&lt;h2&gt;Open .txt files&lt;/h2&gt;
&lt;p&gt;Although .org files are plain text files, they aren't always recognized by other text-editing apps. This release enables opening .txt files, so you can choose to render them in Plain Org, while giving you the option to edit elsewhere.&lt;/p&gt;
&lt;h2&gt;Bugfixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Improve vertical whitespace handling.&lt;/li&gt;
&lt;li&gt;Fixes rendering edge cases.&lt;/li&gt;
&lt;li&gt;Fail gracefully when creating new files on unsupported cloud providers.&lt;/li&gt;
&lt;li&gt;Prevent creating new files with redundant extensions.&lt;/li&gt;
&lt;li&gt;File access improvements.&lt;/li&gt;
&lt;li&gt;Replicates property spacing behaviour using Emacs's &lt;code&gt;org-property-format&lt;/code&gt; default value.&lt;/li&gt;
&lt;li&gt;Fixes keyword picker border rendering.&lt;/li&gt;
&lt;li&gt;Improves rendering performance for large nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;center&gt;
  &lt;a href="https://apps.apple.com/app/id1578965002"&gt;
    &lt;img alt="download-on-app-store.png" src="https://xenodium.github.io/images/flat-habits-for-ios/download-on-app-store.png" width="180px" /&gt;
  &lt;/a&gt;
&lt;/center&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 12 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/plain-org-v11-released</guid></item><item><title>"Open source" is not broken</title><link>https://nadh.in/blog/open-source-is-not-broken/</link><description>&lt;p&gt;I read &lt;a href="https://christine.website/blog/open-source-broken-2021-12-11"&gt;this article&lt;/a&gt; (&lt;em&gt;&amp;ldquo;Open Source&amp;rdquo; is Broken&lt;/em&gt; by Xe) written in the aftermath of the unfortunate &lt;a href="https://en.wikipedia.org/wiki/Log4j#Log4Shell_vulnerability"&gt;&lt;code&gt;log4j2&lt;/code&gt; fiasco&lt;/a&gt;. The author discusses a pertinent problem that has plagued the FOSS (Free and Open Source) world ever since large for-profit corporations started their widespread consumption of FOSS, ever since countless &amp;ldquo;unicorns&amp;rdquo; raised infinite amounts of funding on valuations built pretty much entirely on FOSS, ever since FOSS got co-opted into corporatisation and capitalisation. And yet, countless maintainers of critical and widely used FOSS struggle to make a living.&lt;/p&gt;</description><author>Blog on Kailash Nadh / Personal homepage</author><pubDate>Sun, 12 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://nadh.in/blog/open-source-is-not-broken/</guid></item><item><title>Berkeley Mono December Update</title><link>https://neil.computer/notes/berkeley-mono-december-update/</link><description>&lt;p&gt;It&amp;apos;s been a while since I had &lt;a href="https://neil.computer/notes/introducing-berkeley-mono/"&gt;announced&lt;/a&gt; the release of Berkeley Mono. The more I examine it under scrutinous eyes, the more I find that there is more work to be done. At one point, I had the following &lt;em&gt;masters&lt;/em&gt; going through the final tuning:&lt;/p&gt;&lt;figure class="kg-card kg-image-card kg-card-hascaption"&gt;&lt;img alt="Berkeley Mono Masters" class="kg-image" height="1291" src="https://neil.computer/content/images/2021/12/image-3.png" width="2000" /&gt;&lt;figcaption&gt;Berkeley Mono&lt;/figcaption&gt;&lt;/figure&gt;</description><author>Neil Panchal</author><pubDate>Sun, 12 Dec 2021 01:23:29 GMT</pubDate><guid isPermaLink="true">https://neil.computer/notes/berkeley-mono-december-update/</guid></item><item><title>Zen of python</title><link>https://yasha.solutions/posts/zen-of-python/</link><description>A known thing - I am just posting this here as a reference
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren&amp;rsquo;t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.</description><author>Yasha Solutions</author><pubDate>Sun, 12 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/zen-of-python/</guid></item><item><title>2021–12–12: Pinephone Keyboard – p-boot landscape mode</title><link>https://xnux.eu/log/#057</link><author>megi's PinePhone Development Log</author><pubDate>Sun, 12 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#057</guid></item><item><title>Analog or digital, prepping makes no sense</title><link>https://stop.zona-m.net/2021/12/analog-or-digital-prepping-makes-no-sense/</link><description>&lt;p&gt;Not that it ever did, of course&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 11 Dec 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/analog-or-digital-prepping-makes-no-sense/</guid></item><item><title>Guide to 3D OSM City Models - Trier Edition</title><link>https://geo.rocks/post/guide-to-3d-osm-city-models/</link><description>&lt;p&gt;&lt;img alt="Trier" src="https://geo.rocks/img/trier_center.png" title="Trier" /&gt;&lt;/p&gt;
&lt;p&gt;There are few cities providing open source LIDAR data for 3D-modeling. However, leveraging OSM building, road and landuse data gives you neat city models in no time!&lt;/p&gt;</description><author>Geography &amp;amp; Coding</author><pubDate>Sat, 11 Dec 2021 17:14:06 GMT</pubDate><guid isPermaLink="true">https://geo.rocks/post/guide-to-3d-osm-city-models/</guid></item><item><title>After a year in development, my friend Jonatan and I are releasing a new game for all the word game&amp;amp;hellip;</title><link>https://kajic.com/post/670265944410652672</link><description>&lt;p&gt;After a year in development, my friend &lt;a href="http://heyman.info"&gt;Jonatan&lt;/a&gt; and I are releasing a new game for all the word game lovers out there: &lt;/p&gt;&lt;p&gt;&lt;a href="https://ronigame.com"&gt;https://ronigame.com&lt;/a&gt;&lt;/p&gt;&lt;div class="npf_row"&gt;&lt;figure class="tmblr-full"&gt;&lt;img src="https://64.media.tumblr.com/55c20ab2127caddacb8072336701cada/5808898db8088836-e5/s640x960/7f70608fcf9d1725ca2b02b86db16082612e6f74.jpg" /&gt;&lt;/figure&gt;&lt;/div&gt;</description><author>ka-yich</author><pubDate>Sat, 11 Dec 2021 11:36:56 GMT</pubDate><guid isPermaLink="true">https://kajic.com/post/670265944410652672</guid></item><item><title>Migrating a Windows 10 VM to Windows 11 in Parallels Desktop: a story of TPM chips and BIOS upgrades</title><link>https://nicolaiarocci.com/migrating-a-windows-10-vm-to-windows-11-in-parallels-desktop-a-story-of-tpm-chips-and-bios-upgrades/</link><description>&lt;p&gt;This weekend assignment was to upgrade a couple of old Windows 10 VMs to
Windows 11 in Parallels Desktop 17. I couldn&amp;rsquo;t do that right away because
Windows Update was complaining about the lack of the TPM chip. A little
research revealed that TPM chips only work on UEFI BIOS.  To check which BIOS
version was being used in my VMs, I used the msinfo32 (System Information)
application. It showed the BIOS to be of &amp;ldquo;Legacy&amp;rdquo; type. So my task was now to
switch it to UEFI.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 11 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/migrating-a-windows-10-vm-to-windows-11-in-parallels-desktop-a-story-of-tpm-chips-and-bios-upgrades/</guid></item><item><title>Philadelphia</title><link>https://faingezicht.com/photos/2021/12/11/philly/</link><description>After briefly seeing our friends Annie and Michael in &lt;a href="/photos/2021/12/05/ky-dc/"&gt;Louisville&lt;/a&gt; for their wedding party, we planned to meet them for a bit longer in Philadelphia. We had planned an East Coast trip to see my family in NYC for Thanksgiving, so adding one more city to the itinerary was pretty easy.</description><author>Avy Faingezicht</author><pubDate>Sat, 11 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/12/11/philly/</guid></item><item><title>Detecting if a PWA/TWA is Installed</title><link>https://thomashunter.name/posts/2021-12-11-detecting-if-pwa-twa-is-installed</link><author>Thomas Hunter II</author><pubDate>Sat, 11 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://thomashunter.name/posts/2021-12-11-detecting-if-pwa-twa-is-installed</guid></item><item><title>My Kindle 3rd Gen (2010-2021)</title><link>/2021/12/kindle-3rd-gen</link><description>My first exposure to ebooks was through the Kindle platform. I had money saved from a summer internship, and (among other things) used it to purchase a then-new third-generation Kindle. While I have continued to enjoy some aspects of paper, my Kindle had distinct advantages—most notably that it was actually lighter than many of the books I was reading. With it, I was able to maintain longer reading sessions without having to deal with my arms getting sore.</description><author>Elliott Slaughter</author><pubDate>Sat, 11 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">/2021/12/kindle-3rd-gen</guid></item><item><title>Probabilistic spin glass - Part I</title><link>https://bytepawn.com/probabilistic-spin-glass.html</link><description>&lt;p&gt;I run Monte Carlo simulations on probabilistic spin glasses, a simple mathematical model of magnetized matter with short range interactions. I use entropy to characterize the model's order-disorder transition. &lt;br /&gt;&lt;br /&gt;&lt;img alt="." src="/images/prob_spin_glass_21.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 11 Dec 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/probabilistic-spin-glass.html</guid></item><item><title>Nudification is for women NEAR you</title><link>https://stop.zona-m.net/2021/12/nudification-is-for-women-near-you/</link><description>&lt;p&gt;And that is its worst part.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 10 Dec 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/nudification-is-for-women-near-you/</guid></item><item><title>Thoughts on Metaplex, candymachine and NFTs on Solana</title><link>https://david.coffee/thoughts-on-metaplex-candymachine-and-nfts-on-solana/</link><description>&lt;p&gt;I have a few comments on metaplex, the batteries-included go-to solution for everything NFT related out-of-the-box, that I wanted to share with the world.&lt;/p&gt;
&lt;p&gt;These are mostly thoughts I had when dealing with metaplex during the lifetime of a few projects, from the perspective of someone who just joined the Solana ecosystem.&lt;/p&gt;
&lt;p&gt;About myself: I worked on a few production projects on Solana, am sometimes active on the Solana discord &lt;code&gt;#developer-support&lt;/code&gt; channel to help newcomers out, and currently work on other (NFT related) projects. The following points are my thoughts and experience from building projects in this space.&lt;/p&gt;
&lt;h2 id="instructions-often-unclear"&gt;Instructions (often) unclear&lt;/h2&gt;
&lt;p&gt;When you arrive in Solana land and want to do anything NFT related, chances are that you end up on the metaplex GitHub repo, or documentation at &lt;a href="https://docs.metaplex.com/"&gt;https://docs.metaplex.com/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Coming from Ethereum, we have the ERC721 standard that dictates a handful of functions a smart contract has to implement to be considered ERC721 compliant.&lt;/p&gt;
&lt;p&gt;In solana there are no smart contracts like in Ethereum, instead we&amp;rsquo;re dealing with &lt;em&gt;pRoGRaMs&lt;/em&gt;. Tokens are also not handled by an individual ERC20/ERC721 contract per token, but by the central spl-token program. In fact, each token on Solana is owned by the spl-token program (spl stands for Solana Program Library).&lt;/p&gt;
&lt;p&gt;After clicking around you might land on &lt;a href="https://docs.metaplex.com/nft-standard"&gt;this page&lt;/a&gt; describing a metadata standard on Solana. Okay so far so good, but how do you actually use it? What is the address of this metadata program that you&amp;rsquo;re supposed to use? How does the function call look like to register metadata to something? Don&amp;rsquo;t know.&lt;/p&gt;
&lt;p&gt;There is a metadata overview docs page available at &lt;a href="https://docs.metaplex.com/overviews/metadata_overview"&gt;https://docs.metaplex.com/overviews/metadata_overview&lt;/a&gt; but at the time of writing is still empty.&lt;/p&gt;
&lt;p&gt;After a bit more of digging and wrapping my head around it, I found the metadata program address defined in the &lt;a href="https://github.com/metaplex-foundation/metaplex/blob/master/rust/token-metadata/program/src/lib.rs#L15"&gt;lib.rs&lt;/a&gt; file of the program itself:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-rust"&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; 	solana_program::&lt;span style="color: #a6e22e;"&gt;declare_id!&lt;/span&gt;(&lt;span style="color: #e6db74;"&gt;"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Why is such a crucial bit of information not easier to find?&lt;/p&gt;
&lt;p&gt;Then there is a single &lt;a href="https://crates.io/crates/spl-token-metadata"&gt;spl-token-metadata&lt;/a&gt; crate available on crates.io that you might want to use to submit metadata from your onchain rust programs, but after banging my head against it for literal hours and trying to get it working, it turned out this isn&amp;rsquo;t an official package and &lt;a href="https://github.com/metaplex-foundation/metaplex/issues/349"&gt;has a bug&lt;/a&gt; that makes it impossible to submit metadata - whoops. (Thanks for the kind folks over at #developer-support that helped me debug this)&lt;/p&gt;
&lt;p&gt;The issue I have is that for something that wants to be the &lt;em&gt;standard&lt;/em&gt; of something like NFTs on the entire chain, the experience can&amp;rsquo;t be frustrating but has to be as welcoming and easy to use as possible. Right now, from a newcomers perspective, figuring out how to simply add metadata for a token is incredibly frustrating. (Nowadays there is a &lt;a href="https://github.com/metaplex-foundation/js/blob/main/src/actions/createMetadata.ts#L18"&gt;JS SDK&lt;/a&gt; available that has functions to create metadata, but try to figure that out without initial guidance)&lt;/p&gt;
&lt;p&gt;When dealing with other metaplex components such as candymachine (a program to sell mints of a NFT collection) we are running into a similar situation - the official documentation available &lt;a href="https://docs.metaplex.com/create-candy/introduction"&gt;here&lt;/a&gt; is missing half it&amp;rsquo;s pages* and instead of giving a technical overview of how it works, relies on commands to copy. If something isn&amp;rsquo;t working, which it often doesn&amp;rsquo;t, people flock into the discord to ask questions because they aren&amp;rsquo;t able to debug it themselves.&lt;/p&gt;
&lt;p&gt;Instead of official documentation guidance, people relied on blog posts by other people that explain how the candymachine is working and how to set one up properly.&lt;/p&gt;
&lt;p&gt;*/EDIT: in the time it took me to finish this post, the documentation has been updated and has more pages now. Thanks!&lt;/p&gt;
&lt;p&gt;That brings me to my next point&lt;/p&gt;
&lt;h2 id="copy--pasting-encouraged-over-thinking-and-understanding"&gt;Copy &amp;amp; Pasting encouraged over thinking and understanding&lt;/h2&gt;
&lt;p&gt;While dealing with metaplex, be it the metadata program, candymachine or other components, it became very apparent to me that the creators of metaplex had envisioned one very specific way of dealing with their projects.&lt;/p&gt;
&lt;p&gt;Candymachine, fair-launch and other tools have a very specific way to set them up that they want users to copy and paste, rather than trying to understand and reason with. Of course being friendly to non-technical people is good, but this doesn&amp;rsquo;t teach users (technical and non-technical alike) how things are actually working, but instead encourages to stop thinking and follow guides. When technical people (like myself) try to understand and reason with the programs, we run into more road blocks due to lack of documentation, code quality and resources available.&lt;/p&gt;
&lt;p&gt;In tech, copy-pasting and using big blobs of programs without attempts to understand them is usually a bad idea, especially when money flow and your business depends on it. If anything breaks you need to be able to reason with the program and figure out what is wrong.&lt;/p&gt;
&lt;p&gt;When using metaplex like the creators intended to, so following candymachine instructions step by step with the provided CLI, and submit NFT metadata through candymachine itself, then everything works fine (unless you run any of the small issue resulting in one of the many crypto errors that have no reply on github). But when you try to use them individually, such as only using the metadata program without any of the master edition stuff, or candymachine components without the CLI, things can get fuzzy. (I still don&amp;rsquo;t know if it&amp;rsquo;s okay to remove the minting authority from a token that needs metadata - &lt;a href="https://github.com/metaplex-foundation/metaplex/issues/279"&gt;https://github.com/metaplex-foundation/metaplex/issues/279&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;One example is IPFS. Candymachine supports IPFS, but only through Infura instead of allowing to actually use IPFS p2p. Without paying for Infura it is not even possible to create a candymachine with 10 mints without running into rate limitations and constant retries (I had to find the code and add a blocking &lt;code&gt;sleep&lt;/code&gt; for things to go smooth). Of course when using arweave, like the creators wanted it to be used with, everything works fine. It&amp;rsquo;s also what 80% of the candymachine projects use because the instructions use that as default, often without understanding what arweave actually is.&lt;/p&gt;
&lt;p&gt;Of course biased tools are completely fine, but my problem is more the messaging that everyone in Solana is supposed to be using this otherwise you&amp;rsquo;ll have a &amp;ldquo;&lt;em&gt;bad time&lt;/em&gt;&amp;rdquo;. There are occasionally even people that don&amp;rsquo;t want to deal with you if you created your own minting onchain program and don&amp;rsquo;t use candymachine.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s a shame because&amp;hellip;&lt;/p&gt;
&lt;h2 id="candymachine-is-centralized-and-heavily-botted"&gt;Candymachine is centralized and heavily botted&lt;/h2&gt;
&lt;p&gt;I mentioned above that in Solana, the spl-token program is kind of a central program (like the system program) that handles one thing - tokens.&lt;/p&gt;
&lt;p&gt;For a system program I think that&amp;rsquo;s fair, but candymachine (and metadata but that&amp;rsquo;s not that big of an issue) are following the same pattern and trying to be the centralized thing that your application or dApp is talking to. Imagine it a little bit like having a &amp;ldquo;NFT sale server&amp;rdquo; and to buy an NFT you make a request like this (imaginary example):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;POST candymachine.com/buy
{
	candymachineID: "abc123",
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The candymachine program manages &lt;em&gt;all&lt;/em&gt; candymachines. This program can also be upgraded at will (upgrade authority has not been revoked - &lt;a href="https://explorer.solana.com/address/cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ"&gt;https://explorer.solana.com/address/cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ&lt;/a&gt; by it&amp;rsquo;s creators and functionality can be changed without prior notice).&lt;/p&gt;
&lt;p&gt;Being a central program, it is very trivial to see the transactions from other projects to setup a new NFT mint. It usually goes like this (pseudocode):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;POST candymachine/setup-machine
POST candymachine/add-mint project1 1 "My Mint #1"
POST candymachine/add-mint project2 2 "Another Mint #2"
POST candymachine/add-mint project2 5 "Another Mint #5"
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once you monitor the onchain program you can &lt;em&gt;very easily&lt;/em&gt; extract all available NFTs including their mint order from any project that uses candymachine. This sparked a huge amount of botters that take advantage of this, to either snipe rare NFTs, buy-out huge amounts of NFTs or in some cases even accidentally buying &lt;em&gt;all&lt;/em&gt; available NFTs.&lt;/p&gt;
&lt;p&gt;In other words: If you are a NFT buyer on Solana, you have &lt;strong&gt;almost no chance to mint a rare one&lt;/strong&gt;. Bots know ahead of time if a mint is rare, and will be faster than you buying it.&lt;/p&gt;
&lt;p&gt;To combat this, projects are now creating &lt;em&gt;multiple candymachines&lt;/em&gt; with some fake ones, just to confuse botters.&lt;/p&gt;
&lt;p&gt;If, however, candymachine was setup to be a program that you fork, adjust and deploy yourself, it would be a different story because botters would be unable to figure the program address ahead of time and thus can&amp;rsquo;t bot it (let alone knowing what input is needed to submit a mint transaction).&lt;/p&gt;
&lt;p&gt;I see no reason that candymachine should be a central program. A strength of blockchains is decentralization so let&amp;rsquo;s take more advantage of it instead of relying on central entities that aren&amp;rsquo;t immutable.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;I sound critical and grumpy, but it&amp;rsquo;s good that there is so much NFT adoption happening on Solana. Candymachine being this easy to setup is certainly a reason why we have a huuuge amount of new projects popping up every day (although some with very low quality).&lt;/p&gt;
&lt;p&gt;I hope the Solana foundation can pickup the Metadata standard or make an official one, and I hope that &lt;a href="https://github.com/metaplex-foundation/metaplex/tree/master/rust/nft-candy-machine-v2"&gt;candymachinev2&lt;/a&gt; will solve some of the issues I mentioned above. Or even better: Make something slim that people can fork, modify and deploy themselves, that would be nice!&lt;/p&gt;</description><author>David Mohl</author><pubDate>Fri, 10 Dec 2021 13:00:00 GMT</pubDate><guid isPermaLink="true">https://david.coffee/thoughts-on-metaplex-candymachine-and-nfts-on-solana/</guid></item><item><title>Beware of transit gateways bearing large packets</title><link>https://www.skouf.com/posts/beware-of-transit-gateways/</link><description>Investigating an outage caused by AWS Transit Gateways and some quirks of handling TCP.</description><author>skouf.com</author><pubDate>Fri, 10 Dec 2021 04:30:18 GMT</pubDate><guid isPermaLink="true">https://www.skouf.com/posts/beware-of-transit-gateways/</guid></item><item><title>Search Result Relevance</title><link>https://www.marginalia.nu/log/41-search-result-relevance/</link><description>&lt;p&gt;This entry is about a few problems the search engine has been struggling with lately, and how I&amp;rsquo;ve been attempting to remedy them.&lt;/p&gt;
&lt;p&gt;Before the article starts, I wanted to share an amusing new thing in the world of Internet spam.&lt;/p&gt;
&lt;p&gt;For a while, people have been adding things like &amp;ldquo;reddit&amp;rdquo; to the end of their Google queries to get less blog spam. Well, guess what? The blog spammers are adding &amp;ldquo;reddit&amp;rdquo; to the end of their titles now.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 10 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/41-search-result-relevance/</guid></item><item><title>Your cautionary tale before purchasing any "smart home" stuff</title><link>https://stop.zona-m.net/2021/12/your-cautionary-tale-before-purchasing-any-smart-home-stuff/</link><description>&lt;p&gt;DO listen to these words of wisdom:&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 09 Dec 2021 15:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/your-cautionary-tale-before-purchasing-any-smart-home-stuff/</guid></item><item><title>A big nail in the coffin of MySQL</title><link>https://nicolaiarocci.com/a-big-nail-in-the-coffin-of-mysql/</link><description>&lt;p&gt;After five years in Oracle&amp;rsquo;s MySQL team, Steinar H. Gunderson resigned a few
days ago. On the same day, he dropped the bomb on his &lt;a href="https://blog.sesse.net/blog/tech/2021-12-05-16-41_leaving_mysql"&gt;blog&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;let me point out something that I&amp;rsquo;ve been saying both internally and
externally for the last five years (although never on a stage—which explains
why I&amp;rsquo;ve been staying away from stages talking about MySQL): &lt;strong&gt;MySQL is
a pretty poor database, and you should strongly consider using Postgres
instead&lt;/strong&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 09 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/a-big-nail-in-the-coffin-of-mysql/</guid></item><item><title>Self-hosting Wikipedia using Kiwix</title><link>https://ounapuu.ee/posts/2021/12/09/self-hosting-wikipedia/</link><description>Hosting your own Wikipedia instance has never been this easy!</description><author>./techtipsy</author><pubDate>Thu, 09 Dec 2021 06:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/12/09/self-hosting-wikipedia/</guid></item><item><title>On the, er... limits of tiny houses</title><link>https://stop.zona-m.net/2021/12/on-the-er...-limits-of-tiny-houses/</link><description>&lt;p&gt;When fools meet sustainability fads, bad things ensue.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 09 Dec 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/on-the-er...-limits-of-tiny-houses/</guid></item><item><title>Leveraging Gatsby Incremental Builds With ButterCMS</title><link>https://rafaelquintanilha.com/leveraging-gatsby-incremental-builds-with-buttercms/</link><description>Learn how to speed up your static builds with code examples.</description><author>Software Engineering - Rafael Quintanilha</author><pubDate>Wed, 08 Dec 2021 20:08:00 GMT</pubDate><guid isPermaLink="true">https://rafaelquintanilha.com/leveraging-gatsby-incremental-builds-with-buttercms/</guid></item><item><title>On the incredible opportunities offered by Starship</title><link>https://nicolaiarocci.com/on-the-incredible-opportunities-offered-by-starship/</link><description>&lt;p&gt;Space-junkie me is back, this time reading about the innumerable opportunities
that SpaceX&amp;rsquo;s Starship will offer once it becomes operational, hopefully no
later than this year or the next. In his &lt;a href="https://caseyhandmer.wordpress.com/2021/11/17/science-upside-for-starship/"&gt;Science Upside for Starship&lt;/a&gt;, the
exceptionally knowledgeable Casey Handmer makes a case for Starship relevance
in the future of space exploration by listing an astounding number of
reasonable use-cases for the vessel.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think it is relatively straightforward to think of cool things to do with
SpaceX Starships, so recent posts have focused on trying to understand the
more mixed consequences for incumbent industrial organizations that are not
ideally positioned to exploit the coming advances. It is, however, a fun
exercise to enumerate all the ways in which Starship and related technologies
can help execute bold, ambitious missions of scientific discovery.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 08 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/on-the-incredible-opportunities-offered-by-starship/</guid></item><item><title>Thoughts on Managing Managers</title><link>https://james-carr.org/posts/manager-of-managers/</link><description>In any organization, the role of a manager is crucial to ensure that the team performs to its full potential. As teams grow larger and more complex, managing them effectively becomes increasingly challenging. At a certain point, you need to subdivide the team into several teams and make the decision to either promote or hire additional managers. This is where a director takes on the role of a “manager of managers.</description><author>James Carr</author><pubDate>Wed, 08 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://james-carr.org/posts/manager-of-managers/</guid></item><item><title>Vim tip 1: increment/decrement numbers</title><link>https://learnbyexample.github.io/tips/vim-tip-1/</link><description>&lt;p&gt;Did you know that you can easily increment or decrement a number in Vim?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;a&lt;/kbd&gt; will increment the number under the cursor or the first occurrence of a number to the right of the cursor&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;x&lt;/kbd&gt; will decrement the number under the cursor or the first occurrence of a number to the right of the cursor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also provide a count prefix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;3&lt;/kbd&gt; followed by &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;a&lt;/kbd&gt; will add &lt;code&gt;3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;1000&lt;/kbd&gt; followed by &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;x&lt;/kbd&gt; will subtract &lt;code&gt;1000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Numbers prefixed with &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;0x&lt;/code&gt; and &lt;code&gt;0b&lt;/code&gt; will be treated as octal, hexadecimal and binary respectively. You can also use uppercase for &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;. What if you want numbers prefixed with &lt;code&gt;0&lt;/code&gt; to be treated as decimal? You can use the &lt;code&gt;nrformats&lt;/code&gt; setting as shown below:&lt;/p&gt;
&lt;pre class="language-vim " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-vim"&gt;&lt;span style="color: #b39f04;"&gt;set&lt;/span&gt;&lt;span&gt; nrformats-=octal
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Decimal numbers prefixed with &lt;code&gt;-&lt;/code&gt; will be treated as negative numbers. For example, using &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;a&lt;/kbd&gt; on &lt;code&gt;-100&lt;/code&gt; will give you &lt;code&gt;-99&lt;/code&gt;. While this is handy, this trips me up often when dealing with date formats like 2021-12-08.&lt;/p&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; See &lt;a href="https://vimhelp.org/change.txt.html#CTRL-A"&gt;:h ctrl-a&lt;/a&gt;, &lt;a href="https://vimhelp.org/change.txt.html#CTRL-X"&gt;:h ctrl-x&lt;/a&gt; and &lt;a href="https://vimhelp.org/options.txt.html#%27nrformats%27"&gt;:h nrformats&lt;/a&gt; for documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Video demo&lt;/strong&gt;:&lt;/p&gt;
&lt;p align="center"&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; See also my &lt;a href="https://learnbyexample.github.io/curated_resources/vim.html"&gt;curated list of resources for Vim&lt;/a&gt; and &lt;a href="https://github.com/learnbyexample/vim_reference"&gt;Vim Reference Guide&lt;/a&gt;.&lt;/p&gt;</description><author>learnbyexample</author><pubDate>Wed, 08 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/tips/vim-tip-1/</guid></item><item><title>Running IntelliJ IDEA with JDK 17 for Better Render Performance with Metal</title><link>/posts/2021-12-08-running-intellij-idea-with-jdk17-for-better-render-performance/</link><description>Over the years, although a tremendously valuable tool, IntelliJ IDEA started to feel slowed down, even on these latest M1 Pro machines. It&amp;rsquo;s become frustrating. I disabled as many plugins as not needed. However, it&amp;rsquo;s still not sufficient. I have discovered that you can improve the performance by switching IDEA to the latest JDK. JetBrains has its runtime with its patches for HiDPI supports, performance improvements, and bugfixes for running the IntelliJ family more smoothly.</description><author>Mustafa Akın</author><pubDate>Wed, 08 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">/posts/2021-12-08-running-intellij-idea-with-jdk17-for-better-render-performance/</guid></item><item><title>Debugging Electron.js native crashes on macOS</title><link>https://www.jviotti.com/2021/12/08/debugging-electronjs-native-crashes-on-macos.html</link><description>This article describes in detail how to symbolicalize Electron.js native crashes on macOS</description><author>Juan Cruz Viotti</author><pubDate>Wed, 08 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jviotti.com/2021/12/08/debugging-electronjs-native-crashes-on-macos.html</guid></item><item><title>From porch cameras to police states</title><link>https://stop.zona-m.net/2021/12/from-porch-cameras-to-police-states/</link><description>&lt;p&gt;It&amp;rsquo;s just a short step.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 08 Dec 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/from-porch-cameras-to-police-states/</guid></item><item><title>Make lots of sandbox pages</title><link>https://river.me/blog/lot-of-sandbox-pages/</link><description>Instead of putting all of your code experiments on a single page, make lots of little sandbox pages!</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 08 Dec 2021 01:41:34 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/lot-of-sandbox-pages/</guid></item><item><title>Procedural Christmas cards</title><link>https://qubyte.codes/blog/procedural-christmas-cards</link><description>&lt;p&gt;Below is a procedural snowflake. I'm using a little code to create Christmas
cards this year, and I wanted each to be unique! If you received a card from me,
you may see something like &lt;code&gt;?seed=1234567890&lt;/code&gt; in the URL bar. That will be the
random seed which generated your snowflake (and it's yours to keep). To see a
random snowflake, remove everything after the question mark and hit enter.
Refresh the page to see a fresh random snowflake!&lt;/p&gt;
&lt;p&gt;The red colour is a convenience to make it show up in the software I use to
cut card.&lt;/p&gt;</description><author>Qubyte Codes</author><pubDate>Tue, 07 Dec 2021 18:01:05 GMT</pubDate><guid isPermaLink="true">https://qubyte.codes/blog/procedural-christmas-cards</guid></item><item><title>What stats do people want to learn?</title><link>https://minireference.com/blog/what-stats-do-people-want-to-learn/</link><description>If I&amp;#8217;ve learned anything about the startup world, it is that you have to listen to your customers, which in my case are the readers of the No Bullshit Guide textbooks. With this principle in mind, I sent out a survey to readers interested in the upcoming statistics requesting feedback on the general direction for [&amp;#8230;]</description><author>Minireference blog</author><pubDate>Tue, 07 Dec 2021 17:53:58 GMT</pubDate><guid isPermaLink="true">https://minireference.com/blog/what-stats-do-people-want-to-learn/</guid></item><item><title>Deploy My Startup</title><link>https://www.nothingeasyaboutthis.com/deploy-my-startup/</link><description>There is a real opportunity here to make DevSecOps best practices an easy opt-in for SaaS founders.</description><author>Nothing Easy About This</author><pubDate>Tue, 07 Dec 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://www.nothingeasyaboutthis.com/deploy-my-startup/</guid></item><item><title>Thirty Years Ago Today</title><link>https://blog.herlein.com/post/thirty-years/</link><description>&lt;p&gt;Thirty years ago today I left the US Navy.  A reflection.&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Tue, 07 Dec 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/thirty-years/</guid></item><item><title>Camping in the Headlands</title><link>https://faingezicht.com/photos/2021/12/07/headlands/</link><description>After far too long since our last camping trip, Hannah and I decided to spontaneously book one for the upcoming weekend. There happened to be an opening at Bicentennial Campground in Marin, a tiny 3-site campsite near Point Bonita.</description><author>Avy Faingezicht</author><pubDate>Tue, 07 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/12/07/headlands/</guid></item><item><title>Hyperscaler Shopping List: 2022 Data Center Tech Frenzy ☁️</title><link>https://ashvardanian.com/posts/server-supercycle/</link><description>DDR5, PCIe Gen5, CXL, and new CPUs from Intel, AMD, and NVIDIA converge in 2022. The biggest datacenter hardware refresh in a decade.</description><author>Ash's Blog</author><pubDate>Tue, 07 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ashvardanian.com/posts/server-supercycle/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #49</title><link>https://justingarrison.com/123dev/2021-12-07-123dev-49/</link><description>&lt;img alt="A pipe falls off a truck and rolls into a storm drain" src="https://justingarrison.com/img/123dev/4-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="problems"&gt;Problems&lt;/h2&gt;
&lt;p&gt;Isn&amp;rsquo;t it great when a problem fixes itself? I&amp;rsquo;ve seen this happen more often with people systems than with technology. Ignore that email a couple of days and it&amp;rsquo;s likely not to be a problem anymore.&lt;/p&gt;
&lt;p&gt;With technology we usually have to take some action. Reboot a server, add a sleep, or add more resources. It&amp;rsquo;s not a &amp;ldquo;fix&amp;rdquo; but the problem isn&amp;rsquo;t visible for a little while so we can go back to doing other things.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 07 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-12-07-123dev-49/</guid></item><item><title>Foundations of High-Modernist Ideology in Metropolis</title><link>https://lambdaland.org/posts/2021-12-07_metropolis_essay/</link><description>&lt;p&gt;&lt;em&gt;The following is from a essay from a class on German literature and film.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Fritz Lang’s movie &lt;em&gt;Metropolis&lt;/em&gt; is primarily about the struggle between the oppressed working class and the ruling elite. What drives this tension, however, is a particular view of technology and technological progress that exacerbates the problems the film focuses on. This mentality is called &lt;em&gt;high modernist ideology&lt;/em&gt; by Scott in his book &lt;em&gt;Seeing Like a State&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What is high modernism, then? It is best conceived as a strong (one might even say muscle-bound) version of the beliefs in scientific and technical progress that were associated with industrialization in Western Europe and in North America from roughly 1830 until World War I. At its center was a supreme self-confidence about continued linear progress, the development of scientific and technical knowledge, the expansion of production, the rational design of social order, the growing satisfaction of human needs, and, not least, an increasing control over nature (including human nature) commensurate with scientific understanding of natural laws.&lt;/p&gt;
&lt;p&gt;(Scott, 89)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that this is different from the literary definition of high modernism. For the purposes of this paper, &lt;em&gt;high modernist ideology&lt;/em&gt; is understood as a belief that rational, scientific systems will solve all of mankind’s ailments. &lt;em&gt;Metropolis&lt;/em&gt; is situated right at the tail end of the reign of high modernist ideology, and illustrates some of the failings of that mentality.&lt;/p&gt;
&lt;p&gt;In this paper we will examine how &lt;em&gt;Metropolis&lt;/em&gt; illustrates the dangers of high modernist thinking in how it portrays man’s subservient relationship to machinery and the fragility of the city of Metropolis resulting from top-down, rational planning. Ironically, even though these themes of centralized failure have been common place in film for more than a century now,&lt;sup&gt;&lt;a class="footref" href="https://lambdaland.org/posts/2021-12-07_metropolis_essay/#fn.1" id="fnr.1"&gt;1&lt;/a&gt;&lt;/sup&gt; the dream of a perfectly organized Utopia persists in our culture today. &lt;em&gt;Metropolis&lt;/em&gt; therefore remains an important case study of the dangers and failings of high modernism.&lt;/p&gt;
&lt;h2 id="man-serves-the-machine"&gt;Man Serves the Machine&lt;/h2&gt;
&lt;p&gt;The opening of &lt;em&gt;Metropolis&lt;/em&gt; shows a stark example of men working as slaves to the machine. Freder goes down to the lower levels to see how the workers live. He passes workers mechanically going from pose to pose to keep the machines running. Their jerky, repetitive motions cast the human workers merely as organic components of each machine that they tend.&lt;/p&gt;
&lt;p&gt;When Freder arrives at the M-Machine, he witnesses an explosion that kills or seriously maims several workers, all because one worker was unable to reach a particular valve in time. The machine was obviously not designed to be ergonomic and was lacking critical safety features that would have prevented such a devastating accident. The machine’s complexity and human-hostile design make  accidents like this essentially inevitable. The massive requirements that the machines impose on the humans shows the high modernist idea that humans should submit themselves to rational ideas, and that human nature can be overcome by rational, technical means.&lt;/p&gt;
&lt;p&gt;Yet this submission to rational, mechanical systems is in itself an irrational one. Freder’s vision of Moloch gobbling up workers who dutifully march into the gaping jaws of the machine speaks to the almost fanatic devotion certain planners of large systems had to rationality. The vision closes and Freder sees some workers wordlessly&lt;sup&gt;&lt;a class="footref" href="https://lambdaland.org/posts/2021-12-07_metropolis_essay/#fn.2" id="fnr.2"&gt;2&lt;/a&gt;&lt;/sup&gt; collecting the bodies of the injured and dead, whilst others take up their posts to keep the machine working. There’s some redundancy in how the human workers are allocated to account for mechanical failures or accidents, but there’s no redundancy &lt;em&gt;in the machines&lt;/em&gt; to account for human failures.&lt;/p&gt;
&lt;p&gt;We get other hints throughout the film of the submission of men to the machine. Metropolis looks to be absolutely unlivable. In the upper levels, we do see crowds gathered for entertainment and sport, but the city streets are either entirely devoted to automobiles, or are so foreboding and unwelcoming that no pedestrian-centric economy could ever flourish there. The streets are sterile and perfectly regular; the buildings are all blocks and perfectly featureless. This prefigures some of the architecture and city planning that was present during the DDR: massive apartment complexes built out of prefabricated concrete slabs were often placed far away from the city center, so as to make the Plattenbau districts feel stifling. The straight lines might look good on paper, but maps and city plans are of necessity a simplification over what real life looks like. Life flourishes in the hand-crafted and in the unique. Top-down imposed grids crush what makes life interesting and livable in the city.&lt;/p&gt;
&lt;h2 id="the-fragility-of-monoculture-and-centralized-planning"&gt;The Fragility of Monoculture and Centralized Planning&lt;/h2&gt;
&lt;p&gt;After the accident at the M-Machine, Freder rushes to tell his father, the architect and ruler of Metropolis, about what he saw. In his office at the top of the New Tower of Babel, Jon Frederson controls the city from a central vantage point: aides digest and bring him information, while others record his decisions and carry out his orders. He is the very model of a high modernist: his rationality has brought order and prosperity to the citizens of the upper levels, and everyone above is free to spend their time as they please. It seems that the city is working perfectly well. However, Metropolis turns out to be very fragile.&lt;/p&gt;
&lt;p&gt;Frederson’s control begins unraveling when his aids fail to bring him the information that he wants, and continues when Rotwang plots against him. Eventually the city collapses when the striking workers’ protest goes further than he had foreseen. The destruction of the Heart Machine causes the lower levels of the city to flood and the upper levels to loose power. Cars halt in the streets, and elevators plummet down their shafts. The apparently well-ordered city with bumper-to-bumper cars moving smoothly down the streets suffers cardiac arrest: broken elevators and stopped cars hinder the protagonists as they try to reach safety.&lt;/p&gt;
&lt;p&gt;This fragility has a good analogue from the mistakes of high modernist ideology. &lt;em&gt;Scientific forestry&lt;/em&gt; came about in the 1700s in Germany as a way to improve lumber yield. A plot of forest would be cleared of all “junk” trees and underbrush, and new trees of a single species were planted in straight lines. The ground was cleared to make way for the saplings, and each year there was a neat rotation between different parcels of land from which mature wood could easily and quickly be harvested. Initially, this was a great success: the wood was strong, straight, and plentiful. There was no junk wood that interfered, and lumber outputs could be reliably predicted.&lt;/p&gt;
&lt;p&gt;It was not, however, without its drawbacks. In a book published in 1986, Richard Plochmann describes how yields dropped after the first few batches (Lang and Pye). The reasons for the drop are complex, but the cause was simple: the “clean” forest that produced such predictable quantities of lumber was a monoculture: there was no variety to protect against a pest that targeted a single species of tree. The soil fauna that are critical to a flourishing forest died off, and the trees withered when they didn’t get the nutrients that they needed.&lt;/p&gt;
&lt;p&gt;One of the hallmarks of high modernist ideology is that problems can be solved by large, complex systems, be they mechanical, political, or bureaucratic. Such aspirations usually fail to take account of the difficulties in getting complex systems to run for long periods of time without encountering significant failures. &lt;em&gt;Metropolis&lt;/em&gt; vividly illustrates the fall of Jon Frederson’s pride as the fragility in the massive system he created causes everything to fail around him.&lt;/p&gt;
&lt;h2 id="mediation"&gt;Mediation&lt;/h2&gt;
&lt;p&gt;Amidst the warnings of the dangers of high modernist ideology, the message of &lt;em&gt;Metropolis&lt;/em&gt; is tentatively hopeful: “the mediator between the hands and the head must be the heart!” By the end of the movie, we see the robot masquerading as a human unmasked and destroyed, and the beginnings of greater understanding between the architect of the city, and those that are actually required to live in the city and maintain it. This ultimately is also the cure for high modernist ideology: the designers of complex systems must take into account the needs and the experiences of those who would suffer the brunt of its effects. &lt;em&gt;Metropolis&lt;/em&gt; casts this lesson in the discourse of class struggle, but it can also apply to those who design cities, automobiles, software, and other systems.&lt;/p&gt;
&lt;h2 id="works-cited"&gt;Works Cited&lt;/h2&gt;
&lt;p&gt;Scott, James C. Seeing like a State: How Certain Schemes to Improve the Human Condition Have Failed. Nachdr., Yale Univ. Press, 2008.&lt;/p&gt;
&lt;p&gt;Lang, Chris, and Oliver Pye. “Blinded by Science: The Invention of Scientific Forestry and Its Influence in the Mekong Region.” Chrislang.Org, 1 Nov. 2000, &lt;a class="link-external" href="https://chrislang.org/2000/11/01/blinded-by-science-the-invention-of-scientific-forestry-and-its-influence-in-the-mekong-region/" rel="external"&gt;https://chrislang.org/2000/11/01/blinded-by-science-the-invention-of-scientific-forestry-and-its-influence-in-the-mekong-region/&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;p&gt;&lt;sup&gt;&lt;a href="https://lambdaland.org/posts/2021-12-07_metropolis_essay/#fnr.1" id="fn.1"&gt;1&lt;/a&gt;&lt;/sup&gt; Other films with central-point-of-failure plots include Star Wars, (the thermal exhaust port on the Death Star) The Lord of the Rings, (cast it into the fire!) Batman Begins, (the train with the microwave generator) etc.&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;&lt;a href="https://lambdaland.org/posts/2021-12-07_metropolis_essay/#fnr.2" id="fn.2"&gt;2&lt;/a&gt;&lt;/sup&gt; The failure of Texas’s power grid is an extremely interesting case study about the dangers of fragile systems.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Tue, 07 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-12-07_metropolis_essay/</guid></item><item><title>Constitution DAO, thanks for a good laugh</title><link>https://stop.zona-m.net/2021/12/constitution-dao-thanks-for-a-good-laugh/</link><description>&lt;p&gt;Let&amp;rsquo;s just hope it brings more wisdom, too.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 07 Dec 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/constitution-dao-thanks-for-a-good-laugh/</guid></item><item><title>The Sorting Hat of Sales Mindset</title><link>http://dimitarsimeonov.com/2021/12/06/the-sorting-hat-of-sales-mindset</link><description>&lt;blockquote&gt;
  &lt;p&gt;Always be closing. Always be closing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Alec Baldwin’s character in Glengarry Glen Ross
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Welcome to part 7 of the series on “How to be a game changer”. You can find the previous posts here &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;1&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;2&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer"&gt;3&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources"&gt;4&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/21/exiting-artificial-scarcity"&gt;5&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/28/climb-your-own-status-everest"&gt;6&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As an engineer, I’ve had an aversion to sales for a long time. It seemed like something which doesn’t add value in the grand scheme of things, but instead it just shuffles it around. I had this view of sales being adversarial, and zero-sum. I win, you lose, or vice versa. It seemed to me that when a salesperson makes a deal, they are somehow tricking the other person into getting something bad for them. It felt like it does a lot of persuasion to twist someone’s arm into a bad deal for them. Over time, my mindset about sales changed, and I started seeing salesmanship as a service to the customer.&lt;/p&gt;

&lt;p&gt;I started to look at sales not through the prism of how much do you close, but through the prism of how much you and your counterpart LEARN about whether what you’re selling fits for them. If as a salesperson you uncover whether and how your product will best help the customer, you are actually providing a service to that customer in the form of education. If you focus on maximizing the amount of &lt;strong&gt;education service you provide&lt;/strong&gt;, and uncovering situations where the product is a natural fit, then you are no longer playing a zero-sum adversarial game, and instead you are now playing a win-win game.&lt;/p&gt;

&lt;p&gt;You opt out of thinking of your customer as an adversary.&lt;/p&gt;

&lt;p&gt;This becomes harder to do in a competitive market. You may be selling a product, or an idea, or an action or a philosophy, or your own personal brand. The general “selling” is the act of getting a customer to choose to “buy your product”. For this, they not only have to pick your product among many others, but they also need to have enough conviction to buy any product at all! There are two steps - the first is “buy”, and the second is “yours”. By focusing on providing education and uncovering the natural “buy” and solving for the benefit of the customer, it becomes much easier to transition to the “yours” step afterwards. The customer can be annoyed to hear reasons why product A is better than product B if they don’t want either one!&lt;/p&gt;

&lt;p&gt;It is also a valid thing to recommend to “not buy”. Some people may think this is a reverse psychology trick. But another way to look at why this reverse psychology works at all, is that this is exactly what a you as a salesperson focused on the “buy” problem will do if you think it is not the right fit - recommend a no-buy and let everyone move on. By doing that you have already provided a win to the customer, by saving them from a bad decision, and you’ve added a win for yourself, by improving that relationship with the customer. That same customer may not be buying this time, but their circumstances may change, or they may have friends who are better fit, and spread the word. Furthermore, there is an extra win there. That extra win is turning the market away from being a “lemon market” where everyone is selling shit, and one step closer to a useful market where both customers and sellers profit. This extra win can help grow the market itself.&lt;/p&gt;

&lt;p&gt;By helping the customer, you put on your sorting-hat hat. You help the customer find their best outcome, and sometimes they end up buying from you. Over time, you also learn from them, about which types of customers are the best fit, and how you can change your product to address the ones for whom it is not.&lt;/p&gt;

&lt;p&gt;&lt;img alt="climb your own everest" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/07hat.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Today, as an entrepreneur I may need to pitch my company to customers, investors, or potential employees. There can be an explicit “ask” in customer interviews (buy my product), elevator pitches (give me money) or job interviews (come work for me). By trying to opt out of fixating on the ask, and turning towards communicating and conversing, I can help potential employees find their happiness better, and potential investors see more clearly if this would be a great fit. The “buyer” might not volunteer any information explicitly, but there are always cues, and those turn a pre-determined pitch to a dynamic conversation. If both of us are open and listening, we often reach a much more amicable solution which we didn’t even anticipate. I believe that being systematically focusing on improving their outcomes will also improve my outcome over time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Mon, 06 Dec 2021 02:21:34 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/12/06/the-sorting-hat-of-sales-mindset</guid></item><item><title>Candy Cane Ice Cream</title><link>https://dustin.lammiman.ca/icecream/candycane/</link><description>&lt;h2 id="candy-cane-ice-cream" tabindex="-1"&gt;Candy Cane Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/candycane/#candy-cane-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/candycane/#ingredients"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1/2 Cup Granulated Sugar&lt;/li&gt;
&lt;li&gt;2 Tbsp Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;1 1/3 Cups Milk (I used 2%)&lt;/li&gt;
&lt;li&gt;2 Tbsp Light Corn Syrup&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;li&gt;1/2 Tsp Vanilla Extract&lt;/li&gt;
&lt;li&gt;1/4 Tsp Mint Extract&lt;/li&gt;
&lt;li&gt;2 Ounces Dark (70%) Chocolate&lt;/li&gt;
&lt;li&gt;1 Tsp Vegetable Oil&lt;/li&gt;
&lt;li&gt;3 Regular Size Candy Canes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/candycane/#instructions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Use a food processor to break the candy canes into small pieces.&lt;/li&gt;
&lt;li&gt;In a bowl whisk together sugar, milk powder, and xanthan gum.&lt;/li&gt;
&lt;li&gt;In a pot add milk and corn syrup.&lt;/li&gt;
&lt;li&gt;Whisk dry ingredients into the pot of liquid ingredients. Be sure to mix well.&lt;/li&gt;
&lt;li&gt;Heat ingredients for ~5 minutes on medium heat, stirring well, until sugar is disolved.&lt;/li&gt;
&lt;li&gt;Remove from heat and add remaining cream and allow mixture to cool for a few minutes.&lt;/li&gt;
&lt;li&gt;Refridgerate for 12+ hours. Then churn according to your ice cream maker's instructions.&lt;/li&gt;
&lt;li&gt;While ice cream is churning, place a heatproof bowl above a pot that contains about 1 inch of water. The bowl should rest on the pot, but not touch the water.&lt;/li&gt;
&lt;li&gt;Combine chocolate and vegetable oil in the bowl. Turn the heat to low and stir occasionally until the chocolate is melted. Remove from heat, but leave the bowl on top of the pot until your ice cream is almost ready.&lt;/li&gt;
&lt;li&gt;When your ice cream is almost ready, slowly drizzle the chocolate into the ice cream. Pour in a thin, steady stream. This will allow the chocolate to harden as it touches the ice cream, creating tasty chocolate speckles.&lt;/li&gt;
&lt;li&gt;Add candy cane pieces to the ice cream. Once it is well mixed, move your ice cream into an airtight container. Layer half the ice cream, then chocolate sauce, then the rest of the ice cream.&lt;/li&gt;
&lt;li&gt;Freeze the ice cream for 4+ hours before serving.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="chocolate-sauce" tabindex="-1"&gt;Chocolate Sauce &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/candycane/#chocolate-sauce"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Chocolate sauce is optional. It's delicious without it too!&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="ingredients-1" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/candycane/#ingredients-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1/4 Cup Cocoa Powder&lt;/li&gt;
&lt;li&gt;1/4 Cup Sugar&lt;/li&gt;
&lt;li&gt;1/4 Cup Water&lt;/li&gt;
&lt;li&gt;Pinch of Salt&lt;/li&gt;
&lt;li&gt;1/2 Tsp Vanilla&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions-1" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/candycane/#instructions-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Whisk together cocoa and sugar in a saucepan.&lt;/li&gt;
&lt;li&gt;Add water and salt and bring to a boil over medium-high heat, continuing to whisk the mixture.&lt;/li&gt;
&lt;li&gt;Reduce to a simmer and continue to whisk for about 30 seconds.&lt;/li&gt;
&lt;li&gt;Remove from heat and add vanilla.&lt;/li&gt;
&lt;li&gt;Store in the refridgerator in a sealed container.&lt;/li&gt;
&lt;/ol&gt;</description><author>Dustin.Lammiman</author><pubDate>Mon, 06 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/icecream/candycane/</guid></item><item><title>A Public Slipbox</title><link>https://explog.in/slipbox/a%20public%20slipbox.html</link><description>&lt;p&gt;
    Introducing my slip-box: &lt;a href="https://explog.in/slipbox/"&gt;https://explog.in/slipbox/&lt;/a&gt;.
    &lt;/p&gt;

    &lt;p&gt;
    I take org notes in &lt;a href="https://logseq.com"&gt;logseq&lt;/a&gt;; which are then processed with a tiny
    python script &amp;amp; published using Emacs's org-export with an automatic
    Github Action every few hours.
    &lt;/p&gt;</description><author>RSS</author><pubDate>Mon, 06 Dec 2021 01:17:00 GMT</pubDate><guid isPermaLink="true">https://explog.in/slipbox/a%20public%20slipbox.html</guid></item><item><title>Big Mouth: Season 3</title><link>https://olshansky.info/tv/big_mouth_season_3/</link><description>Olshansky's review of Big Mouth: Season 3</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 05 Dec 2021 09:36:01 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/tv/big_mouth_season_3/</guid></item><item><title>Gunpowder Milkshake</title><link>https://olshansky.info/movie/gunpowder_milkshake/</link><description>Olshansky's review of Gunpowder Milkshake</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 05 Dec 2021 09:27:22 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/gunpowder_milkshake/</guid></item><item><title>Big Mouth: Season 2</title><link>https://olshansky.info/tv/big_mouth_season_2/</link><description>Olshansky's review of Big Mouth: Season 2</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 05 Dec 2021 09:10:01 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/tv/big_mouth_season_2/</guid></item><item><title>Big Mouth: Season 1</title><link>https://olshansky.info/tv/big_mouth_season_1/</link><description>Olshansky's review of Big Mouth: Season 1</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 05 Dec 2021 09:09:19 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/tv/big_mouth_season_1/</guid></item><item><title>My First Amazon Blog... ML/AI Chatbot over the Phone!</title><link>https://blog.herlein.com/post/amazon-blog/</link><description>&lt;p&gt;A month ago I told you I &lt;a href="https://blog.herlein.com/post/webrtc-phone/"&gt;changed roles&lt;/a&gt; at AWS.  Now I can show you what I&amp;rsquo;ve done since then!&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Sun, 05 Dec 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/amazon-blog/</guid></item><item><title>Louisville, Kentucky &amp;amp; Washington DC</title><link>https://faingezicht.com/photos/2021/12/05/ky-dc/</link><description>As part of the post-COVID wedding marathon, we flew to Louisville and DC back to back.</description><author>Avy Faingezicht</author><pubDate>Sun, 05 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/12/05/ky-dc/</guid></item><item><title>Christmas in Europe Is No Joke!</title><link>https://godsip.club/articles/christmas-in-europe-is-no-joke/</link><description>&lt;h1 id="first-avoid-being-chased-down"&gt;First: Avoid Being Chased Down &lt;a class="anchor" href="#first-avoid-being-chased-down"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;I live in Italy and, when I was a child, I didn’t really believe in Santa – I was already asking too many questions. I didn’t mind too much being a good child to receive nice gifts, but a few hundreds of kilometers from me, things were (are!) quite different. In the Eastern Alps, in Austria, Slovenia and other Slavic countries, naughty children are not gifted with black coal by the &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Befana" rel="noopener" target="_blank"&gt;Befana&lt;/a&gt;&lt;/em&gt;, as in Italy. In those countries, bad kids are actually &lt;em&gt;hunted down&lt;/em&gt; by a horned demon – the &lt;a href="https://en.wikipedia.org/wiki/Krampus" rel="noopener" target="_blank"&gt;Krampus&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Every year, the 5th of December, during the Krampusnacht (“Krampus Night”), men and women march down the street dressed up like demons, with horrifying masks that take the whole year to make. The Krampus Run, or &lt;em&gt;Krampuslauf&lt;/em&gt;, is a joyful parade for adults, mostly drunk, but a bit less cherish for children, who are continuously scared and chased by these horrible figures. &lt;a href="https://godsip.club/articles/if-trees-could-talk/#let-me-stand-next-to-your-fire" rel="noopener" target="_blank"&gt;Large bonfires&lt;/a&gt; are lit in the snowy streets as the quiet Alpine villages come to life.&lt;/p&gt;
&lt;p&gt;The Krampus is clearly St. Nichola’s counterpart, or &lt;em&gt;Santa’s villain&lt;/em&gt; from a modern perspective – a very bad Grinch. He leaves wooden sticks for bad children and takes the meanest with him. Christianity tried to banish these figures and traditions, but in this case, at the end of 19th century, this folklore was rediscovered because of… consumerism. Since when Austrian government quit the monopolisation of postcards, &lt;em&gt;Krampuskarten&lt;/em&gt; boomed: Krampus-themed holiday cards filled with dark humor.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="&amp;ldquo;Greetings from Krampus&amp;rdquo;&amp;hellip; isn&amp;rsquo;t it lovely? I feel like I designed it myself." src="https://godsip.club/img/krampus-postcard.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;&amp;ldquo;Greetings from Krampus&amp;rdquo;&amp;hellip; isn&amp;rsquo;t it lovely? I feel like I designed it myself.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h1 id="second-chase-the-origins"&gt;Second: Chase the Origins &lt;a class="anchor" href="#second-chase-the-origins"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;As always, the fascinating aspect of this tradition is its origin – even if these are hypotheses and we will never have the absolute certainty. What is sure, though, is that the &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Perchta" rel="noopener" target="_blank"&gt;Percht&lt;/a&gt;&lt;/em&gt; (female: &lt;em&gt;Perchta&lt;/em&gt;) was widely known in the same regions before the Krampus itself. It&amp;rsquo;s the figure of a woman considered the goddess of the Winter. She has two forms: one is a beautiful young lady white as snow, while the other one is an ugly elderly woman.&lt;/p&gt;
&lt;p&gt;The name &lt;em&gt;Perchta&lt;/em&gt; may mean “the bright one”, suggesting the lady’s pale skin, but there are also common aspects with the Krampus and the old hag: in historical descriptions, both have two different feet – one human and a larger one, called “goose foot”. Overall, though, &lt;em&gt;Percht-themed&lt;/em&gt; masks were already worn during festivals in past times – and this tradition continues nowadays. They reflected the duality of the figure: some costumes were beautiful and bright, while others were horrible horned demons wearing animal furs. Those festivals are still called &lt;em&gt;Perchtenlaufen&lt;/em&gt;, probably an earlier form of the Krampus runs.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="This costume represents the duality of the Perchta – both beauty and beast&amp;hellip; Maybe a bit tending to the latter." src="https://godsip.club/img/percht-costume.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;This costume represents the duality of the Perchta – both beauty and beast&amp;hellip; Maybe a bit tending to the latter.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Probably, the &lt;em&gt;Percht&lt;/em&gt; evolved in the Krampus after St. Nichola’s introduction. Nowadays, the two shapes may a bit intertwined, but what is interesting is their past. In fact, similarities with the &lt;em&gt;Perchta&lt;/em&gt; and other European figures and names can be observed: &lt;em&gt;Bertha&lt;/em&gt;, &lt;em&gt;Holda&lt;/em&gt; (who also appears in a Grimm’s fairy tale), the Russian &lt;em&gt;Baba Yaga&lt;/em&gt;, and even the Italian &lt;em&gt;Befana&lt;/em&gt; that I cited before. All these folklore characters, with so many common traits… &lt;strong&gt;must&lt;/strong&gt; have some common roots! This was one of the thoughts fascinating me when I decided to explore our lore.&lt;/p&gt;
&lt;h1 id="third-connect-the-gods-dots"&gt;Third: Connect the &lt;del&gt;Gods&lt;/del&gt; Dots &lt;a class="anchor" href="#third-connect-the-gods-dots"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;A simple answer may be also the best one: those were goddesses, or a sort of. All of them were often referred as “guardians of the animals” and, in the form of beautiful women, probably they were &lt;em&gt;Freya&lt;/em&gt;. She, Odin’s wife, was often linked to the nature and the animals – she was often identified with boars and cats. Also, the latter ones are a universal symbol for witches. Freya, indeed, was the most powerful sorceress of the whole Norse pantheon; so powerful that she could have been Odin’s teacher regarding magical arts.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Freya by Johannes Gehrts. Note the cats at her feet&amp;hellip; they used to tow her chariot!" src="https://godsip.club/img/freya-gehrts.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Freya by Johannes Gehrts. Note the cats at her feet&amp;hellip; they used to tow her chariot!&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It is possible that, in Slavic countries, Freya had been a “Winter queen” and then evolved into figures as &lt;em&gt;Holda&lt;/em&gt;: the Middle Ages may have shaped her from Norse goddess into a folklore character. But if Freya had been undoubtedly beautiful, why was the &lt;em&gt;Perchta&lt;/em&gt; depicted also as an ugly witch?&lt;/p&gt;
&lt;p&gt;Short answer: as often happened in the past centuries, probably some roles were mixed. Freya herself is already a notable example of how deities could be merged together – or even split! &lt;em&gt;Frigg&lt;/em&gt;, in fact, is another name with which she is referred to in the poems; it is never clear if they are two distinct goddesses and what are the differences between them. One hypothesis is that, at some point, there had been the tendency to split her in two different beings&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But if Freya had been undoubtedly beautiful, why was the &lt;em&gt;Perchta&lt;/em&gt; depicted also as an ugly witch?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Another resemblance with Norse mythology is again about the Krampus, maybe inherited from the &lt;em&gt;Percht&lt;/em&gt;: it is told to be the son of &lt;em&gt;Hel&lt;/em&gt;, the ruler of the homonymous world, the Land of the Dead. &lt;em&gt;Hel&lt;/em&gt; was one of &lt;em&gt;Loki&lt;/em&gt;’s daughters&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; and she was portrayed with a gloomy appearance. Her name means “the hidden one” – do you know which name could have the same meaning, according to the Old German language? &lt;em&gt;Percht&lt;/em&gt;, that’s correct!&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not over yet: it is told that the &lt;em&gt;Perchta&lt;/em&gt; howled in the woods in winter nights, as the &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Wild_Hunt" rel="noopener" target="_blank"&gt;Wild Hunt&lt;/a&gt;&lt;/em&gt; did, and lived in the elderberry bush, which is often linked to Freya. The list of common features could be &lt;em&gt;so much&lt;/em&gt; longer! But of course (it is mandatory to remind that) all of these are thesis with no &amp;ldquo;real&amp;rdquo; proofs. I feel so heavy-hearted when I think about all that past knowledge that we won&amp;rsquo;t be able to recover.&lt;/p&gt;
&lt;p&gt;What is certain, though, is that today is the 5th of December, and tonight the Krampus will run through the snowy streets of small rural Alpine villages, chasing down children – and it will happen again next year, and the next, and the next…&lt;/p&gt;
&lt;figure&gt;&lt;img alt="That glance&amp;hellip; Enough said. Children, beware! 👀" src="https://godsip.club/img/nikolaus-krampus.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;That glance&amp;hellip; Enough said. Children, beware! 👀&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Unfortunately, we have only relatively “modern” poems of Norse mythology since, until less one thousand years ago, those stories were retold verbally and with rock carvings.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Loki had offspring as father… but &lt;em&gt;as mother&lt;/em&gt; too! Did you know that he gave birth to an eight-legged horse? We’ll talk about that.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Godsip Club</author><pubDate>Sun, 05 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://godsip.club/articles/christmas-in-europe-is-no-joke/</guid></item><item><title>Flashing the Supermicro variant of SAS9201-16e (SAS2116)</title><link>https://du.nkel.dev/blog/2021-12-05_supermicro_sas2116_flashing/</link><description>Flashing SAS Cards feels like a trip to the 90s, with all the peculiarities included. This particular LSI SAS9201-16e card left me puzzled, until I realized it is the Supermicro variant that needs special handling. Let's see if I can pull together the important pieces, so that those who come after me don't need to give up on their cards.</description><author>du.nkel.dev</author><pubDate>Sun, 05 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://du.nkel.dev/blog/2021-12-05_supermicro_sas2116_flashing/</guid></item><item><title>Conspirituality is pervasive, but why?</title><link>https://stop.zona-m.net/2021/12/conspirituality-is-pervasive-but-why/</link><description>&lt;p&gt;Because the Web is too fast, of course.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 05 Dec 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/conspirituality-is-pervasive-but-why/</guid></item><item><title>Contacts</title><link>https://mfashby.net/posts/2021-12-04-contacts/</link><description/><author>Home on mfashby.net</author><pubDate>Sat, 04 Dec 2021 17:05:47 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-12-04-contacts/</guid></item><item><title>Fixing AMD OpenCL on Windows</title><link>https://bastibe.de/2021-12-04-fixing-amd-opencl-on-windows.html</link><description>&lt;p&gt;When I recently installed Windows 11 on my desktop, my photo editor Darktable suddenly got much slower than it used to be. When I looked into its preferences, I noticed OpenCL was no longer available.&lt;/p&gt;
&lt;p&gt;As it turns out, some versions of the AMD graphics driver apparently no longer ship with OpenCL support on Windows. However, they &lt;em&gt;do&lt;/em&gt; ship with the necessary libraries, it's just that these libraries are not registered any longer.&lt;/p&gt;
&lt;p&gt;To register them, open the Registry Editor (aka &lt;em&gt;regedit.exe&lt;/em&gt;), navigate to the key &lt;code&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenCL\Vendors&lt;/code&gt; (if the key does not exist, create it), and create a new DWORD of Value &lt;code&gt;0&lt;/code&gt;. Now rename the DWORD to be the path to your &lt;em&gt;amdocl64.dll&lt;/em&gt;. Mine is &lt;code&gt;C:\Windows\System32\DriverStore\FileRepository\u0344717.inf_amd64_d38cec78c83eee99\B343886\amdocl64.dll&lt;/code&gt;. Search &lt;em&gt;C:\Windows\System32\&lt;/em&gt; for &lt;em&gt;amdocl64.dll&lt;/em&gt; to find the correct path on your computer.&lt;/p&gt;
&lt;p&gt;With that, OpenCL was once again recognized by Darktable and the rest of my photo editing programs.&lt;/p&gt;
&lt;p&gt;(&lt;a href="https://github.com/ethereum-mining/ethminer/issues/2001#issuecomment-662288143"&gt;source&lt;/a&gt;)&lt;/p&gt;</description><author>bastibe.de</author><pubDate>Sat, 04 Dec 2021 11:19:00 GMT</pubDate><guid isPermaLink="true">https://bastibe.de/2021-12-04-fixing-amd-opencl-on-windows.html</guid></item><item><title>Book Review: A useless man</title><link>https://nicolaiarocci.com/book-review-a-useless-man/</link><description>&lt;p&gt;Sait Faik Abasıyanık is an acclaimed Turkish storyteller. &lt;em&gt;A useless man&lt;/em&gt; is
a collection of short stories that spans nearly two decades of the author&amp;rsquo;s
output, offering a glimpse into his imaginative and troubled mind. His
overflowing love for others (even sensual, with a preference for street kids)
combined with a &amp;ldquo;mal de vivre&amp;rdquo; that pushes him towards self-destruction are
apparent.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A useless man" src="https://nicolaiarocci.com/images/a-useless-man.jpg#right" /&gt;
His passion for the most popular areas of Istanbul and, in contrast, the
atavistic nostalgia for the simple life of the nearby fishermen islets exudes
from these stories, which often run similar one after another. The composition
is sometimes complex, making the text hard to parse and comprehend. Several
novels are noteworthy, though, especially those narrated in third-person, where
the narrator is not involved. The author is holding back his poetic prowess and
the resulting text is more linear. Notable examples are &lt;em&gt;The samovar&lt;/em&gt;, on the
elaboration of mourning, and &lt;em&gt;A story for two&lt;/em&gt;, a touching story of friendship
between a bird and a fisherman. Some first-person ones are exceptional, too.
For example, &lt;em&gt;Milk&lt;/em&gt; is Sait Faik&amp;rsquo;s worthy tribute to Proust&amp;rsquo;s madeleine. One
short story on the protagonist&amp;rsquo;s struggle with a street kid was profoundly
touching.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 04 Dec 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-a-useless-man/</guid></item><item><title>Editing and making myself finish the first series</title><link>https://liza.io/editing-and-making-myself-finish-the-first-series/</link><description>&lt;p&gt;I have been editing the next science fiction book. The plan is to hire an editor to go through it as well, since the previous books made enough income to pay for that. Editing will be the first service I pay for in my self-publishing journey. If things continue to go well, I might start buying proper on-market covers as the next step (but I also really like designing my own covers, so we&amp;rsquo;ll see&amp;hellip;it&amp;rsquo;s just that I know nothing I come up with will be as good as a professional cover designer in my genre).&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Sat, 04 Dec 2021 08:02:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/editing-and-making-myself-finish-the-first-series/</guid></item><item><title>Wasted Resources</title><link>https://www.marginalia.nu/log/40-wasted-resources/</link><description>&lt;p&gt;At a previous job, we had a new and fancy office. The light switches were state of the art. There was an on button, and a separate off button. When you pressed the on button, the lights would fade on. When you pressed the off button, they would fade off. In the cloud somewhere was two functions that presumably looked a bit like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;fun turnOnLamp() {
 while (!bright()) increaseBrightness();
}
fun turnOffLamp() {
 while (!dark()) decreaseBrightness();
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I have deduced this from the fact that if you pressed both buttons at the same time, the lights would flicker on and off until someone was contacted to restart something. It is a marvellous time to be alive when you need to reboot your light switches because of a race condition. Modern computers are so fast that we often don&amp;rsquo;t even recognize when we are doing things inefficiently. We can end messages half way around the world to turn on the lights and it seems like it&amp;rsquo;s just a wire between the switch and the lamp.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sat, 04 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/40-wasted-resources/</guid></item><item><title>The Whole World is Your Antilibrary</title><link>https://faingezicht.com/articles/2021/12/04/antilibrary/</link><description>&lt;small&gt;&lt;em&gt;Este ensayo también está disponible &lt;a href="/articles/2021/12/04/antilibrary-es/"&gt;en español&lt;/a&gt;.&lt;/em&gt;&lt;/small&gt;

In &lt;a href="https://www.google.com/books/edition/How_to_Travel_with_a_Salmon/3bytC0aZs5IC"&gt;_How to Travel with a Salmon_&lt;/a&gt;, Umberto Eco writes about the shock visitors to his home experienced upon encountering the thousands of tomes in his personal library. The essence of the essay became famous when Nassim Taleb paraphrased it in his bestseller _Black Swan_. Much like the japanese concept of &lt;a href="https://en.wikipedia.org/wiki/Tsundoku"&gt;"Tsundoku,"&lt;/a&gt; Taleb's opening quote now adorns many book lovers' blog posts who share it without giving it much thought:

&gt; The writer Umberto Eco belongs to that small class of scholars who are encyclopedic, insightful, and nondull. He is the owner of a large personal library (containing thirty thousand books), and separates visitors into two categories: those who react with “Wow! Signore professore dottore Eco, what a library you have. How many of these books have you read?” and the others —a very small minority— who get the point that a private library is not an ego-boosting appendage but a research tool. The library should contain as much of what you do not know as your financial means ... allow you to put there. You will accumulate more knowledge and more books as you grow older, and the growing number of unread books on the shelves will look at you menacingly. Indeed, the more you know, the larger the rows of unread books. Let us call this collection of unread books an _antilibrary_.

I'm a book hoarder, so the idea of an _antilibrary_ clearly resonates. I've known Taleb's version of the story focusing on the &lt;a href="https://en.wikipedia.org/wiki/There_are_known_knowns"&gt;known unknowns&lt;/a&gt; for years, but had never looked into its source until this week. Funnily enough, after an online search for the title of the Eco book where the essay appears, I realized I had picked up the book years ago and put it into my own _antilibrary_ to sit and wait for an opportune moment. &lt;a href="https://docs.google.com/spreadsheets/d/1VkbAVV86-HRxxl0PlCTVl_beF2GjvofUYeVyuNAAQTg/edit"&gt;My books spreadsheet&lt;/a&gt; is nearing a thousand titles, and it metaphorically sits next to the ever-expanding shelf in my living room that, as of today, includes another four volumes by Eco (out of which I've only read two).

According to Taleb, the value of owning unread books is in having unknown information ready at arms length at the right time. Today, however, there's an inherent paradox in the activity of literary accumulation. Eco and his fish first traveled together back in the summer of '94. Coincidentally, Jeff Bezos founded Amazon around the same time, setting in motion a machine that'd ultimately change that dynamic and redefine the value of physical books. The potential value stored in our shelves was sizable then, but has since diminished. To write this essay I got &lt;a href="https://www.overdrive.com/apps/libby/"&gt;Libby&lt;/a&gt; to instantly deliver one of the San Francisco Public Library's copies of _Black Swan_ to my Kindle, and if I wanted to purchase the paper version I could have had it delivered to my door within 48 hours. Thanks, Jeff.

Today, unknown unknowns aren't resolved merely by holding more books. Personal libraries are useful research tools as long as the books in them wouldn't be otherwise available. While a good library may shine in terms of curation, that quality could also be achieved with a simple list and no actual books. There's a reason libraries abstract their collections into classification systems and indices: physicality, almost by definition, represents a loss in discoverability compared to a pared down index or, more controversially, a search box. When your local library can send the digital version of any book to your smartphone for free, and when purchasing a copy online is a couple of taps away, the availability constraint is no longer binding, even for Taleb's skeptical _antischolars_. Except for rare and out of print books, annotated works, or other oddities, I'd argue that the value of a physical _antilibrary_ no longer holds. Somewhat irrationally, this hasn't stopped me from building one.

Reading is exponential by nature. Intertextuality means that for every book we read, we're likely to find references to another N that we'll want to add to our stack. I accumulate unread books, and I enjoy it. Browsing bookstores and getting lost between their shelves is always in my checklist when visiting new cities. I prefer reading on paper than on a screen, and I like the feeling I get when in conversation someone brings up a book that I know is sitting on my shelf. And yet, if I am honest, I have no idea why I return home so often with books to add to my collection.

There's no rational justification behind a personal antilibrary, but there doesn't need to be one. Ultimately, Taleb's point was not that accumulating unread books is good, but that we can only thrive when we admit the limitation of our knowledge. An _antilibrary_ is valuable because it reminds us of how much we don't know. These days, nearly the whole output of the human enterprise is at our fingertips. That fact is more daunting than the rows of books of any physical _antilibrary_.

&lt;hr /&gt;

&lt;small&gt;&lt;em&gt;Photo: Lungarno degli Archibusieri, Florence, by me. Previously posted on &lt;a href="/photos/2021/10/19/florence/"&gt;Italia II - Firenze&lt;/a&gt;.&lt;/em&gt;&lt;/small&gt;

&lt;hr /&gt;</description><author>Avy Faingezicht</author><pubDate>Sat, 04 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/articles/2021/12/04/antilibrary/</guid></item><item><title>How to Solve Incidents - a research proposal</title><link>https://ztoz.blog/posts/how-to-solve-incidents/</link><description>&lt;p&gt;&lt;a href="https://press.princeton.edu/books/paperback/9780691164076/how-to-solve-it"&gt;&lt;em&gt;How to Solve It&lt;/em&gt;&lt;/a&gt;, but for operational incidents.&lt;/p&gt;
&lt;p&gt;Engineers with site reliability responsibilities are often faced with operational issues (incidents) that have an unknown cause and uncertain solution. &lt;em&gt;How to Solve It&lt;/em&gt; is a classic work describing heuristics for solving mathematical problems. There have been adaptations of this book for different domains, but none yet for software-intensive operations. Within this study, we aim to build a taxonomy or categorization of techniques/heuristics in practice for understanding and overcoming operational issues. The catalog will be based on a survey of experienced engineers and real incident logs and postmortems from a variety of organizations. Readers will be able to leverage the catalog to solve tricky and novel production issues.&lt;/p&gt;
&lt;h1 id="research-plan"&gt;Research Plan&lt;/h1&gt;
&lt;p&gt;This project will use both survey and field study methods to catalog &amp;ldquo;in practice&amp;rdquo; incident mitigation techniques and heuristics. We expect the project to take between 6 and 9 months (to the point of a submission-ready article), although the timeline may be extended due to data access issues since operational data is often sensitive and negotiations may be prolonged.&lt;/p&gt;
&lt;h2 id="data-collection"&gt;Data Collection&lt;/h2&gt;
&lt;p&gt;We will reach out to organizations (primarily SaaS or computing intensive companies) for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Interviews with senior engineers &amp;mdash; how do they approach an unfamiliar production problem? do they use any explicit mental models, heuristics, or existing processes?&lt;/li&gt;
&lt;li&gt;Access to incident logs, chat/ticket threads, and/or root cause analyses. Basically, documents describing how the team is working to resolve incidents, preferably &amp;ldquo;raw&amp;rdquo;. The research team will then:
&lt;ol&gt;
&lt;li&gt;Document problem solving threads by incident &amp;mdash; i.e., faced with &lt;em&gt;x&lt;/em&gt; situation, attempted &lt;em&gt;y&lt;/em&gt; or sought to determine &lt;em&gt;z&lt;/em&gt; status. Was this successful?&lt;/li&gt;
&lt;li&gt;Document what mental models (if any) were being used? Was this mental model correct?&lt;/li&gt;
&lt;li&gt;Note: Raw incident logs are preferred to RCAs. For example, &lt;a href="https://slack.engineering/what-happened-during-slacks-dnssec-rollout/"&gt;Slack&amp;rsquo;s DNSSEC RCA&lt;/a&gt; is very detailed on the cause of the problem, the prior attempts to prevent an outage, and actions to correct the outage once it happened, but is light on details about specific troubleshooting steps and unsuccessful threads. RCAs can provide useful data, but usually gloss over the troubleshooting activity.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Access to &amp;ldquo;Try if everything else fails&amp;rdquo;-style runbooks (if they exist).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A suitable organization should have at least 30 relevant incidents. We are targeting at least 10 organizations within the study.&lt;/p&gt;
&lt;p&gt;A sample of incidents will be excluded from the coding effort to form an &amp;ldquo;out of band&amp;rdquo; pool. We will also augment the out of band pool with publicly documented incident RCAs.&lt;/p&gt;
&lt;h3 id="data-protection"&gt;Data Protection&lt;/h3&gt;
&lt;p&gt;We anticipate that all organizations that participate in the study will be acknowledged within the paper and that individuals interviewed may be quoted with their consent. The paper will not link heuristics to specific companies or incidents; heuristics and techniques are generalized and anonymized. A heuristic must be practiced by at least two organizations to be referenced.&lt;/p&gt;
&lt;p&gt;We expect to sign NDAs or similar legal agreements to protect sensitive data and may accept reasonable constraints on access and storage of any shared data.&lt;/p&gt;
&lt;p&gt;As members of the Association of Computing Machinery, we are bound by the Code of Ethics, in particular &lt;a href="https://www.acm.org/code-of-ethics#h-1.7-honor-confidentiality."&gt;section 1.7 Honor Confidentiality&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Computing professionals are often entrusted with confidential information such as trade secrets, client data, nonpublic business strategies, financial information, research data, pre-publication scholarly articles, and patent applications. Computing professionals should protect confidentiality except in cases where it is evidence of the violation of law, of organizational regulations, or of the Code. In these cases, the nature or contents of that information should not be disclosed except to appropriate authorities. A computing professional should consider thoughtfully whether such disclosures are consistent with the Code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="create-taxonomycategorization"&gt;Create taxonomy/categorization&lt;/h2&gt;
&lt;p&gt;Using the collected data, we expect to code it via a faceted coding approach. An example of a facet coding is Ranganathan&amp;rsquo;s five core categories, typically labeled PMEST. P is the personality or most specific subject, M is the material or component, E is the energy or activity, operation, or process, S is the space or location, and T is the time. A specific heuristic may not have all five facets.&lt;/p&gt;
&lt;p&gt;As an example, let&amp;rsquo;s say that within an incident the engaging team has detected a drop in production rate from a distributed group of queue-based workers. For coding, the personality is the queue-based job workers. The material could be the workers, the queue, the underlying compute infrastructure, a worker dependency, or even ancillary items like the logging and monitoring subsystem. The energy will be the specific action, such as restarting workers, checking connections to the queue, or verifying health of dependencies.&lt;/p&gt;
&lt;p&gt;The coded values can then be grouped into a taxonomy by grouping similar items by facets and frequency.&lt;/p&gt;
&lt;p&gt;Why go through the effort to create a taxonomy? &lt;em&gt;How to Solve It&lt;/em&gt; organizes the heuristics alphabetically, &lt;em&gt;How to Solve It: Modern Heuristics&lt;/em&gt; organizes items hierarchically by academic discipline, and &lt;em&gt;Discussion of the Method : Conducting the Engineer&amp;rsquo;s Approach to Problem Solving&lt;/em&gt; lacks any reference-able organization. There are two reasons to go through the effort: construction and reference. Construction-wise, a taxonomy of heuristics can identify gaps in the catalog (similar to gaps in the Periodic Table identified elements). Reference-wise, a good organization will make it easier for readers to apply to incidents.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Typology versus Taxonomy.&lt;/em&gt; Both are terms for categorization, but in some domains a typology is conceptual while a taxonomy is empirical. Typologies are organized by dimensions that a human specifies, while a taxonomy is created via cluster analysis based on natural attributes or dimensions selected by humans. Since heuristics lack natural attributes, the more correct term for this activity is typology, albeit more obscure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="testing-the-taxonomy"&gt;Testing the Taxonomy&lt;/h2&gt;
&lt;p&gt;Ideally, the true test of efficacy would track reduction in repair time due to improved problem solving with and without the catalog. However, this would require controlled experiments or a large statistical study, both of which are studies in their own right. Instead, the validity of the catalog will be evaluated for (practical) completeness and likelihood of utility.&lt;/p&gt;
&lt;p&gt;Completeness will be evaluated via out-of-band examples. For the incidents and public RCAs marked as out-of-band during the data collection phase, we will map the incident&amp;rsquo;s narrative to the coded catalog to check for any absences in the catalog or difficulties in following the organization. Out-of-band examples may be added to the catalog (assuming they meet our inclusion threshold), but their initial absence is a useful statistical indicator of completeness.&lt;/p&gt;
&lt;p&gt;Likelihood of utility will be evaluated via an outside group of both inexperienced and experienced engineers who will be asked to use the catalog on prior incidents (of their choosing) and current incidents and record their experiences. (See &lt;a href="https://accidental-taxonomist.blogspot.com/2013/03/testing-taxonomies.html"&gt;Use testing&lt;/a&gt; for more context.) Their experiences will be recorded via survey and aggregated.&lt;/p&gt;
&lt;h1 id="hypothetical-categorization"&gt;Hypothetical Categorization&lt;/h1&gt;
&lt;p&gt;To provide an idea of what the categorization might look like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Have you tried turning it off and on again? (Blind luck strategies)
&lt;ol&gt;
&lt;li&gt;Restarting the complaining service&lt;/li&gt;
&lt;li&gt;Restarting the complaining service&amp;rsquo;s dependencies&lt;/li&gt;
&lt;li&gt;Redeploying&lt;/li&gt;
&lt;li&gt;Moving to another host / cluster&lt;/li&gt;
&lt;li&gt;Rollback&lt;/li&gt;
&lt;li&gt;Search internet for similar error messages&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Configuration management
&lt;ol&gt;
&lt;li&gt;Restore previous configuration&lt;/li&gt;
&lt;li&gt;Attempt to isolate problem to certain instances / shard&lt;/li&gt;
&lt;li&gt;Verify configuration same between instances&lt;/li&gt;
&lt;li&gt;Restore previous configuration (dependency)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Capacity management
&lt;ol&gt;
&lt;li&gt;Increase allocation&lt;/li&gt;
&lt;li&gt;Decrease allocation&lt;/li&gt;
&lt;li&gt;Rebalance among shards&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Traffic management
&lt;ol&gt;
&lt;li&gt;Throttle traffic source&lt;/li&gt;
&lt;li&gt;Eliminate traffic source&lt;/li&gt;
&lt;li&gt;Route traffic to another shard&lt;/li&gt;
&lt;li&gt;Verify traffic along route&lt;/li&gt;
&lt;li&gt;Eliminate traffic magnification&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Broaden scope
&lt;ol&gt;
&lt;li&gt;Orchestration issue&lt;/li&gt;
&lt;li&gt;Operating System/Host Issue&lt;/li&gt;
&lt;li&gt;DNS / Service Lookup Issue&lt;/li&gt;
&lt;li&gt;Network Issue&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Potentially, the categorization could become a decision tree, depending on the statistical strength of the data.&lt;/p&gt;
&lt;p&gt;Each entry would contain a description, including any known failure modes and additional context.&lt;/p&gt;
&lt;h1 id="prior-art-and-why-this-is-novel"&gt;Prior Art (and why this is novel)&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://press.princeton.edu/books/paperback/9780691164076/how-to-solve-it"&gt;How to Solve It&lt;/a&gt; by G.Polya&lt;/p&gt;
&lt;p&gt;This is a classic work on mathematical methods to solve problems. Although some of the heuristics described (e.g. Do you know a related problem, Draw a figure, Generalization) are applicable beyond mathematical problems, most of the heuristics fall within the mathematics domain. In contrast, the proposed project targets operating software intensive systems and also seeks to quantitatively find which heuristics seem to work most often.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://link.springer.com/book/10.1007/978-3-662-07807-5"&gt;How to Solve It: Modern Heuristics&lt;/a&gt; by Z. Michalewicz and B. Fogel&lt;/p&gt;
&lt;p&gt;This book follows a similar approach to &lt;em&gt;How to Solve It&lt;/em&gt;, but changes the domain to computer science algorithms by discussing approaches like constraint solving, search, and evolution. None of the heuristics are particularly relevant to operations.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://sre.google/sre-book/effective-troubleshooting/"&gt;Effective Troubleshooting&lt;/a&gt; by Chris Jones (Google&amp;rsquo;s SRE Book); &lt;a href="https://www.atlassian.com/incident-management"&gt;Atlassian Incident Management Handbook&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Both focus on the management of incidents (e.g. communication, maintaining focus, leadership) and the general process of solving problems rather than specific actions or techniques the engineering group can perform, particularly if the cause is novel. Although a scientific or structured approach certainly aids in mitigating issues, our work will be focused on specific techniques to be used within that greater framework.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dl.acm.org/doi/abs/10.1145/3368089.3417054"&gt;How to mitigate the incident? An effective troubleshooting guide recommendation technique for online service systems&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;
This paper studies &amp;ldquo;troubleshooting guides&amp;rdquo; (TSGs) at Microsoft and develops a recommendation system for suggesting specific actions operators can take based on an incident description. Examining incident records, they find a &lt;em&gt;lower-bound&lt;/em&gt; of 27.2% of incidents that had an existing TSG and a large proportion (~60%) of incidents contained novel failures (no existing or relevant guide). The proposed work is not focused on making it easier to find existing run books to solve recurring or known failure modes, but shorten the time to restore for novel or ambiguous failure modes, which the paper suggests are the majority of cases within their sample.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="publication-venue"&gt;Publication Venue&lt;/h1&gt;
&lt;p&gt;Peer reviewed with free access to final paper (e.g. Open Access). Potential venues include SREcon, DevOpsDays, and magazines with broader scope like &lt;em&gt;Communications of the ACM&lt;/em&gt; (Contributed Articles section).&lt;/p&gt;
&lt;p&gt;If there is a large amount of material, this could become a book or long-form website, but we would prefer to start with an article.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Jiajun Jiang, Weihai Lu, Junjie Chen, Qingwei Lin, Pu Zhao, Yu Kang, Hongyu Zhang, Yingfei Xiong, Feng Gao, Zhangwei Xu, Yingnong Dang, and Dongmei Zhang. 2020. How to mitigate the incident? an effective troubleshooting guide recommendation technique for online service systems. In Proceedings of the 28th ACM Joint Meeting on European Software Engineering Conference and Symposium on the Foundations of Software Engineering (ESEC/FSE 2020). Association for Computing Machinery, New York, NY, USA, 1410–1420. DOI:https://doi.org/10.1145/3368089.3417054&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;As noted in the paper, Microsoft does not require TSGs to be developed nor maintained. We expect that the availability of runbooks to vary widely between organizations based on their culture.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>ℤ→ℤ</author><pubDate>Sat, 04 Dec 2021 00:09:56 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/how-to-solve-incidents/</guid></item><item><title>Why aren't the most useful Mac apps on the App Store?</title><link>https://alinpanaitiu.com/blog/apps-outside-app-store/</link><description>&lt;p&gt;Let’s set the stage first. So, it’s Tuesday night and I’m &lt;code&gt;Command&lt;/code&gt; &lt;code&gt;Tab&lt;/code&gt;-ing my way through 10 different apps, some with 3-4 windows, while trying to patch bugs in &lt;strong&gt;


&lt;img alt="Lunar Icon" height="16" src="https://alinpanaitiu.comimages/icons/lunar_16@1x.webp#x16" /&gt; &lt;a href="https://lunar.fyi" rel="noopener" target="_blank"&gt;Lunar&lt;/a&gt;&lt;/strong&gt; faster than the users can submit the reports. I’m definitely failing.&lt;/p&gt;
&lt;p&gt;I feel my brain pulsing and my ring finger going numb on the &lt;code&gt;Tab&lt;/code&gt; key. I stop switching apps and just stare at the Xcode window, containing what I knew was Swift code but looked like gibberish right now.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Feels like burnout”&lt;/em&gt; I think. Wasn’t that what I ran away from when I quit my job to make apps for a living?&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I heard a joke recently:&lt;/p&gt;


&lt;details style="margin-top: 0;"&gt;
    &lt;b&gt;Show joke&lt;/b&gt;
    &lt;img alt="Didn't want a 9 to 5 job, now I work 24/7" src="https://files.alinpanaitiu.com/9to5-joke.webp" /&gt;
&lt;/details&gt;

&lt;p&gt;It’s probably only funny for a small group of workaholics, but the reality of those words struck me in the middle of the hysterical laughter I was trying to stop.&lt;/p&gt;
&lt;p&gt;Why am I still developing this app?&lt;/p&gt;
&lt;p&gt;Why am I adding all the features the users are asking for, then deal with the flood of frustrated emails saying &lt;em&gt;“what an overcomplicated stupid app, I just want to make my screen brighter!!”&lt;/em&gt;, then try to hide advanced features to make it simpler, then get assaulted with the confused &lt;em&gt;“I can’t change volume anymore fix this ASAP!!!”&lt;/em&gt; because UI changes can very easily introduce bugs by simply forgetting to bind a slider to a value, then get back to scotch taping broken parts slower than the users can report them?&lt;/p&gt;
&lt;p&gt;Those features should have probably been their own independent app.&lt;/p&gt;
&lt;p&gt;I start to feel my fingers again, press &lt;code&gt;Command&lt;/code&gt; &lt;code&gt;Tab&lt;/code&gt; once more, and while looking at the list of app icons I realise something.&lt;/p&gt;
&lt;p&gt;Maybe pressing &lt;code&gt;Tab&lt;/code&gt; 4-5 times while visually assessing if the selected app icon is the one I want to focus, isn’t the best solution for this kind of workflow.&lt;/p&gt;
&lt;p&gt;So what does my brain do when I feel burnt out? Gives me ideas for even more apps…&lt;/p&gt;
&lt;h2 id="rcmd"&gt;rcmd&lt;/h2&gt;
&lt;p&gt;That’s how the idea of &lt;strong&gt;


&lt;img alt="rcmd Icon" height="16" src="https://alinpanaitiu.comimages/icons/rcmd_16@1x.webp#x16" /&gt; &lt;a href="https://lowtechguys.com/rcmd/" rel="noopener" target="_blank"&gt;rcmd&lt;/a&gt;&lt;/strong&gt; began. We have two Command keys on a Mac keyboard, and the right hand side one is almost never used. What if I use it exclusively for switching apps?&lt;/p&gt;














&lt;a href="https://alinpanaitiu.comimages/dynamic-rcmd.jpg"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;rcmd app screenshot&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="rcmd app screenshot" src="https://alinpanaitiu.comimages/dynamic-rcmd.jpg" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;

&lt;p&gt;When I used Windows for reverse engineering malware, I liked switching apps using &lt;code&gt;Win&lt;/code&gt; + &lt;code&gt;Number&lt;/code&gt; where the number meant the position of the app icon in the taskbar. I didn’t like counting apps however.&lt;/p&gt;
&lt;p&gt;Using the app name felt the most natural. I remembered using &lt;a href="https://contexts.co/" rel="noopener" target="_blank"&gt;Contexts&lt;/a&gt; for a while, which provides a Spotlight like search bar for fuzzy searching your running apps. But that needed a bit more key presses than I wanted &lt;em&gt;(that is &lt;strong&gt;1&lt;/strong&gt;)&lt;/em&gt; and more attention than I wanted to give &lt;em&gt;(which is none)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;My idea sounded a bit simpler: &lt;code&gt;Right Command&lt;/code&gt; + &lt;code&gt;the first letter of the app name&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So simple that people were offended by it&amp;hellip;&lt;/p&gt;
&lt;p&gt;


&lt;img alt="hacker news comment screenshot where someone is offended by the price" src="https://files.alinpanaitiu.com/hn-comment-rcmd-price-offended.webp" /&gt;&lt;/p&gt;
&lt;p&gt;I pitched this idea to &lt;strong&gt;Ovidiu Rusu&lt;/strong&gt;, a very good friend of mine, who surprisingly seemed to have the same need as me. We created the first prototype in about a week &lt;em&gt;(icons and graphics take so much time…)&lt;/em&gt;  and started using it in our day to day work to see if it made sense.&lt;/p&gt;
&lt;p&gt;In less than a day, rcmd became so ingrained in our app switching that we got incredibly annoyed when we had to quit the app for recompiling and debugging. We just kept pressing &lt;code&gt;Right Command&lt;/code&gt; &lt;code&gt;X&lt;/code&gt; and staring at the screen like complete idiots, not understanding why Xcode wasn&amp;rsquo;t being focused.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;What most people overlook when they have a simple idea is that 80% of the effort goes into handling edge cases that are not visible in the original idea.&lt;/p&gt;
&lt;p&gt;Just for this simple app we had to solve the following problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What do I do when there are multiple running apps with the same first letter?
&lt;ul&gt;
&lt;li&gt;How do I decide which one gets priority so that I meet user expectations?&lt;/li&gt;
&lt;li&gt;How do I get to the other apps with less priority in as few key presses as possible?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How do I persist key assignments if necessary?&lt;/li&gt;
&lt;li&gt;What if the user assigned the key to an app that’s no longer running?
&lt;ul&gt;
&lt;li&gt;Should I launch the app when the key is pressed?&lt;/li&gt;
&lt;li&gt;Where is the app located?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There are dozens of running apps while only about 10% of them are actual apps launched by the user. How do I filter those out? The others could be:
&lt;ul&gt;
&lt;li&gt;Menu bar apps &lt;em&gt;(e.g. Alfred, Lunar etc)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;OS services &lt;em&gt;(e.g. Spotlight, Siri etc.)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Daemons &lt;em&gt;(e.g. CoreAudio etc)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How do I only listen to the Right Command key?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This last question is what led me to write this article. It turned out we needed to do quite a few hacks if we wanted to publish this app in the App Store.&lt;/p&gt;
&lt;h2 id="the-sandbox"&gt;The Sandbox&lt;/h2&gt;
&lt;p&gt;Every app that is submitted to the App Store must be compiled to run within a sandbox. This means that the app will run in a container which will have the same structure as your home directory, but with mostly empty folders.&lt;br /&gt;
The sandbox also limits what APIs you can use, and which system components you can communicate with.&lt;/p&gt;
&lt;p&gt;The defacto way of reacting to &lt;code&gt;Right Command&lt;/code&gt; + &lt;code&gt;some other key&lt;/code&gt; is to monitor all key events &lt;em&gt;(yes, just like a keylogger)&lt;/em&gt;, and discard events that don’t contain the Right Command modifier flag.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="nc"&gt;NSEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModifierFlags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;rightCommand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModifierFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;UInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NX_DEVICERCMDKEYMASK&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NSEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addGlobalMonitorForEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matching&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modifierFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;rightCommand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="c1"&gt;// do your thing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Easy peasy, right? Well no, because that’s &lt;a href="https://stackoverflow.com/questions/32116095/how-to-use-accessibility-with-sandboxed-app" rel="noopener" target="_blank"&gt;not allowed on the App Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To use that API you need to first request &lt;strong&gt;Accessibility Permissions&lt;/strong&gt; from the user. Those permissions are &lt;strong&gt;prohibited inside the Sandbox&lt;/strong&gt;, because with those permissions, an app would be able to do all kinds of nasty stuff:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Log all your key presses &lt;em&gt;(including passwords if you aren’t using a Secure Input field)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Extract text from all the running apps&lt;/li&gt;
&lt;li&gt;Click on buttons inside other apps&lt;/li&gt;
&lt;li&gt;Write text in fields or send key combinations to the system&lt;/li&gt;
&lt;li&gt;Render elements like buttons and tooltips over the interface of other apps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are perfectly reasonable things in the context of assistive software, because you need the computer to do stuff for you when you aren’t able to use a keyboard or a mouse/trackpad.&lt;/p&gt;
&lt;p&gt;And you need the computer to read out text from other apps, or show choice buttons which you can trigger with your voice.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;a href="#the-app-store-review"&gt;Technical content ahead. Click to skip this section if you&amp;rsquo;re not interested in macOS internals.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But for rcmd’s use case, we’re restricted to APIs that don’t require these permissions. APIs so old that 64-bit wasn’t even a thing when they launched and they require passing C function pointers instead of our beloved powerful Swift closures.&lt;/p&gt;
&lt;p&gt;That’s the &lt;a href="https://en.wikipedia.org/wiki/Carbon_%28API%29" rel="noopener" target="_blank"&gt;Carbon API&lt;/a&gt; and it goes a little something like this:&lt;/p&gt;
&lt;h4 id="registering-a-commandr-hotkey"&gt;Registering a Command+R hotkey&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Install the key event handler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;pressedEventType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EventTypeSpec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pressedEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eventClass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OSType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kEventClassKeyboard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pressedEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eventKind&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OSType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kEventHotKeyPressed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;InstallEventHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetEventDispatcherTarget&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kc"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OSStatus&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handlePressedKeyboardEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inEvent&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pressedEventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Register the hotkey&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;hotKeyId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EventHotKeyID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UTGetOSTypeFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"some-unique-identifier"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;carbonHotKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventHotKeyRef&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;RegisterEventHotKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kVK_ANSI_R&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                    &lt;span class="nb"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmdKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                    &lt;span class="n"&gt;hotKeyId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                    &lt;span class="n"&gt;GetEventDispatcherTarget&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                    &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;carbonHotKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Handle the event&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;handlePressedKeyboardEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;_&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OSStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="bp"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetEventClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;kEventClassKeyboard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unknown event class"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;hotKeyId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EventHotKeyID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetEventParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                                  &lt;span class="n"&gt;EventParamName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kEventParamDirectObject&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                                  &lt;span class="n"&gt;EventParamName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeEventHotKeyID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                                  &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                                  &lt;span class="n"&gt;MemoryLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventHotKeyID&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                                  &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;                                  &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;hotKeyId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;noErr&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="bp"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hotKeyId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;UTGetOSTypeFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"some-unique-identifier"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Invalid hot key id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;GetEventKind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;EventParamName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kEventHotKeyPressed&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="c1"&gt;// do your thing.. eventually&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="bp"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unknown event kind"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;noErr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Not so pretty as the &lt;code&gt;NSEvent&lt;/code&gt; method, but does the job. Kind of.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You see, that beautiful code macaroni above only lets us listen to &lt;code&gt;Any Command&lt;/code&gt; + &lt;code&gt;R&lt;/code&gt;, not specifically the &lt;code&gt;Right Command&lt;/code&gt;. There&amp;rsquo;s no way to pass something like &lt;code&gt;rightCmdKey&lt;/code&gt; into &lt;code&gt;RegisterEventHotKey&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A workaround I found for this was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listen for &lt;code&gt;flagsChanged&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set a global boolean to &lt;code&gt;true&lt;/code&gt; when the there&amp;rsquo;s a &lt;code&gt;rightCommand&lt;/code&gt; modifier&lt;/li&gt;
&lt;li&gt;Discard the event if the boolean is &lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;rcmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NSEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addGlobalMonitorForEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matching&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flagsChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;rcmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modifierFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;rightCommand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;handleHotkey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;rcmd&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;focusApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;Doing this reminded me of the days I worked with Rust, and how wonderfully impossible a task like this would be. I don&amp;rsquo;t think I&amp;rsquo;m touching it again, I like my global atomic booleans.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now the weirdest limitation hits me. There&amp;rsquo;s no way to discard a hotkey event and forward it back to the system so it can use it for the next handler.&lt;/p&gt;
&lt;p&gt;Say I register &lt;code&gt;Command&lt;/code&gt; &lt;code&gt;C&lt;/code&gt; and I only want to do something when &lt;code&gt;Right Command&lt;/code&gt; is held. If I do nothing when &lt;code&gt;Left Command&lt;/code&gt; is held, then you can&amp;rsquo;t copy text anymore using &lt;code&gt;Command&lt;/code&gt; &lt;code&gt;C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I tried returning the inappropriately named &lt;code&gt;OSStatus(eventNotHandledErr)&lt;/code&gt; but the event still doesn&amp;rsquo;t return to the handler chain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At this point we seriously considered dropping the App Store idea and just going the self publishing route.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But just thinking what we would have to do for that triggered something akin to PTSD.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a list with what I can remember off the top of my head from Lunar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement the Paddle SDK for licensing&lt;/li&gt;
&lt;li&gt;Add Sentry for error reporting&lt;/li&gt;
&lt;li&gt;Lose the useful App Store analytics&lt;/li&gt;
&lt;li&gt;Lose ratings and reviews&lt;/li&gt;
&lt;li&gt;Add Sparkle for auto-updating
&lt;ul&gt;
&lt;li&gt;Generate signing keys&lt;/li&gt;
&lt;li&gt;Create DMG with the app&lt;/li&gt;
&lt;li&gt;Sign every single update&lt;/li&gt;
&lt;li&gt;Try not to lose the signing key&lt;/li&gt;
&lt;li&gt;Generate the appcast XML&lt;/li&gt;
&lt;li&gt;Host that appcast somewhere&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Host the app bundle for download somewhere&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finding yet another workaround seemed much easier.&lt;/p&gt;
&lt;p&gt;Thankfully it really was easy. It turns out that &lt;code&gt;RegisterEventHotKey&lt;/code&gt; is plenty fast. So fast that we were able to register the hotkeys only when &lt;code&gt;Right Command&lt;/code&gt; was being held, and unregister them when the key was released.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Atomics&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;_rcmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ManagedAtomic&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;rcmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kr"&gt;get&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_rcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ordering&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relaxed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="kr"&gt;set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_rcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ordering&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequentiallyConsistent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NSEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addGlobalMonitorForEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matching&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flagsChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;rcmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modifierFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;rightCommand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rcmd&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="n"&gt;registerHotkeys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;        &lt;span class="n"&gt;unregisterHotkeys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr /&gt;
&lt;h2 id="the-app-store-review"&gt;The App Store review&lt;/h2&gt;
&lt;p&gt;Now rcmd was ready for publishing on the App Store.&lt;/p&gt;
&lt;p&gt;There was one little thing that bothered me though. I usually keep 4-5 separate projects open in Sublime Text, each with its own window. Because of the sandbox, there&amp;rsquo;s no way to get a list of windows for an app and, say, focus a specific one, or cycle between them.&lt;/p&gt;
&lt;p&gt;But I found a little gem while I was customising my fork of &lt;a href="https://github.com/koekeishiya/yabai" rel="noopener" target="_blank"&gt;yabai&lt;/a&gt;, a way to trigger Exposé for a single app:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;CoreDockSendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.apple.expose.front.awake"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;













&lt;a href="https://alinpanaitiu.comimages/app-window-expose.jpg"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;app window expose screenshot&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="app window expose screenshot" src="https://alinpanaitiu.comimages/app-window-expose.jpg" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;

&lt;p&gt;We decided to show Exposé if for example you press &lt;code&gt;rcmd&lt;/code&gt; &lt;code&gt;s&lt;/code&gt; while Sublime Text is already focused. It was good enough for us.&lt;/p&gt;
&lt;p&gt;Not for the App Store reviewer though.&lt;/p&gt;














&lt;a href="https://alinpanaitiu.comimages/coredock-review.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;app store review rejecting the expose feature&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="app store review rejecting the expose feature" src="https://alinpanaitiu.comimages/coredock-review.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;

&lt;p&gt;I knew private and undocumented APIs are not seen well on the App Store. But I had no idea they will guarantee a rejection.&lt;/p&gt;
&lt;h2 id="free-trials"&gt;Free Trials&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;I like breaking the norm with my creations.&lt;/strong&gt; Some of them will be flukes, some will be criticised into oblivion, but a small number of them might turn out to be something a lot of people wanted but didn&amp;rsquo;t know they needed.&lt;/p&gt;
&lt;p&gt;rcmd is one of those things: a bit quirky, unique in its approach, and incredibly useful for a specific group of people.&lt;/p&gt;
&lt;p&gt;That is also its weak point though. It&amp;rsquo;s hard to communicate this usefulness without being able to try the app first. But as it turns out, the App Store doesn&amp;rsquo;t provide any support for creating a &lt;em&gt;free XX-day trial&lt;/em&gt; before buying an app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free trials for non-subscription apps have been allowed since mid-2018 on the App Store&lt;/strong&gt;, and are supposed to be implemented using in-app purchases. Unfortunately, this approach has a lot of inconveniences which are very well detailed in this article: &lt;a href="https://bitsplitting.org/2018/06/06/ersatz-free-trials/" rel="noopener" target="_blank"&gt;Ersatz Free Trials | Bitsplitting.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These are the biggest shortcomings for my case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The trial doesn&amp;rsquo;t start immediately
&lt;ul&gt;
&lt;li&gt;The users have to be nagged with a custom &lt;em&gt;Activate Trial&lt;/em&gt; UI to start the trial manually&lt;/li&gt;
&lt;li&gt;Starting the trial means &lt;strong&gt;buying a $0.00 in-app purchase&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;If the users skip the trial UI out of frustration, they&amp;rsquo;ll be left with a dumbed down version of the app, possibly missing the functionality they downloaded it for&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You have to create your own UI for starting a trial, buying the full app after the trial ends, showing the trial status etc.&lt;/li&gt;
&lt;li&gt;You have to provide some kind of limited functionality if the trial ends
&lt;ul&gt;
&lt;li&gt;In the context of simple and small apps like rcmd, this doesn&amp;rsquo;t really make sense&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tried a few dozen apps on the App Store and I couldn&amp;rsquo;t find a single one offering a free trial for a non-subscription purchase using the above method.&lt;/p&gt;
&lt;p&gt;Having to pay upfront is steering away a lot of possible users, but with all that bad UX, we decided to not implement any free trial and just sell the app for a one-time fair price.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;3 years ago, I would have probably chosen to make the app open source and give it away for free, just like I did with Lunar.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I would have thought:&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I&amp;rsquo;m making a ton of money at this company, what I would get by selling a small app would be peanuts anyway.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Only recently I realised that this approach kept me dependent on having a job where I click-clack useless programs 8 hours a day, only to get 1-2 hours after work for my projects, and sacrifice my health and sanity in the process.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In my whole 7-year career as a professional API Glue Technician and experienced Wheel Reinventer, I never did anything remotely as useful as even the simplest app I can code and publish in 2 months right now. At those companies, most of my work was scraped anyway when the redesign period of the year came.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So I&amp;rsquo;d rather have those peanuts please.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="everyones-choosing-to-be-left-out"&gt;Everyone&amp;rsquo;s choosing to be left out&lt;/h2&gt;




&lt;p&gt;Now, with so many limitations, I think we can take a fair guess at why most indie developers choose to distribute their app outside the App Store.&lt;/p&gt;
&lt;p&gt;Here are some of the apps I find most useful, and what I think is the main reason for them not being in the App Store:&lt;/p&gt;
&lt;h3 id="alfred-iconimagesiconsalfredwebpx20-alfredhttpswwwalfredappcom"&gt;


&lt;img alt="Alfred Icon" height="20" src="https://alinpanaitiu.comimages/icons/alfred.webp#x20" /&gt; &lt;a href="https://www.alfredapp.com/" rel="noopener" target="_blank"&gt;Alfred&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The app&amp;rsquo;s main functionality (searching the filesystem) needs &lt;strong&gt;Full Disk Access&lt;/strong&gt; permissions which are not allowed inside the sandbox.&lt;/p&gt;
&lt;p&gt;It also uses &lt;strong&gt;Accessibility Permissions&lt;/strong&gt; for auto-expanding snippets and other custom workflows.&lt;/p&gt;
&lt;h3 id="bettertouchtool-iconimagesiconsbttwebpx20-bettertouchtoolhttpsfolivoraai"&gt;


&lt;img alt="BetterTouchTool Icon" height="20" src="https://alinpanaitiu.comimages/icons/btt.webp#x20" /&gt; &lt;a href="https://folivora.ai/" rel="noopener" target="_blank"&gt;BetterTouchTool&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Capturing and responding to all kinds of keyboard and trackpad events needs &lt;strong&gt;Accessibility Permissions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The app also encapsulates the older BetterSnapTool utility for snapping windows to a grid. Resizing windows requires the same permissions.&lt;/p&gt;
&lt;h3 id="karabiner-elements-iconimagesiconskarabinerwebpx20-karabiner-elementshttpskarabiner-elementspqrsorg"&gt;


&lt;img alt="Karabiner-Elements Icon" height="20" src="https://alinpanaitiu.comimages/icons/karabiner.webp#x20" /&gt; &lt;a href="https://karabiner-elements.pqrs.org/" rel="noopener" target="_blank"&gt;Karabiner-Elements&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reacting to and changing keyboard input in realtime needs a special keyboard driver which is only allowed by Apple on a case by case basis. You have to &lt;a href="https://developer.apple.com/documentation/driverkit/requesting_entitlements_for_driverkit_development" rel="noopener" target="_blank"&gt;request DriverKit entitlements from Apple&lt;/a&gt;, and they have to deem you worthy of those entitlements.&lt;/p&gt;
&lt;p&gt;Needless to say, they won&amp;rsquo;t give hardware driver entitlements for a software app mimicking a keyboard.&lt;/p&gt;
&lt;h3 id="sublime-text-iconimagesiconssublimewebpx20-sublime-texthttpswwwsublimetextcom"&gt;


&lt;img alt="Sublime Text Icon" height="20" src="https://alinpanaitiu.comimages/icons/sublime.webp#x20" /&gt; &lt;a href="https://www.sublimetext.com/" rel="noopener" target="_blank"&gt;Sublime Text&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Full Disk Access&lt;/strong&gt; is probably the biggest requirement here.&lt;/p&gt;
&lt;p&gt;Of course, there are other code editors on the App Store like &lt;a href="https://apps.apple.com/us/app/bbedit/id404009241?mt=12" rel="noopener" target="_blank"&gt;BBEdit&lt;/a&gt; but they have this initial phase where you have to manually give them access to your &lt;code&gt;/&lt;/code&gt; (root) directory.&lt;/p&gt;
&lt;p&gt;













&lt;a href="https://alinpanaitiu.comimages/bbedit-sandbox-access-dialog.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;bbedit sandbox access dialog&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="bbedit sandbox access dialog" src="https://alinpanaitiu.comimages/bbedit-sandbox-access-dialog.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;
&lt;br /&gt;














&lt;a href="https://alinpanaitiu.comimages/bbedit-allow-access.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;bbedit allow access&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="bbedit allow access" src="https://alinpanaitiu.comimages/bbedit-allow-access.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Compared to Sublime Text&amp;rsquo;s &lt;em&gt;&lt;strong&gt;launch and edit instantly&lt;/strong&gt;&lt;/em&gt; first time experience, I feel this is a bit annoying. I&amp;rsquo;m pretty sure this confuses a lot of first time users, and they will probably blame the developer, not knowing that this is the only way to access files from the Sandbox.&lt;/p&gt;
&lt;h3 id="swish-iconimagesiconsswishwebpx20-swishhttpshighlyopinionatedcoswish"&gt;


&lt;img alt="Swish Icon" height="20" src="https://alinpanaitiu.comimages/icons/swish.webp#x20" /&gt; &lt;a href="https://highlyopinionated.co/swish/" rel="noopener" target="_blank"&gt;Swish&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Resizing windows, listening for global trackpad gestures, detecting titlebars, moving windows to other spaces/screens. All of these need &lt;strong&gt;Accessibility Permissions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s even an FAQ for that on their page:&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id="why-is-swish-not-on-the-app-store"&gt;Why is Swish not on the App Store?&lt;/h3&gt;
&lt;p&gt;Apple only allows sandboxed apps on the App Store. Swish needs to perform low-level system operations which prevent it from being sandboxed. &lt;a href="https://stackoverflow.com/q/32116095" rel="noopener" target="_blank"&gt;Read more here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="sip-iconimagesiconssipwebpx20-siphttpssipappio"&gt;


&lt;img alt="Sip Icon" height="20" src="https://alinpanaitiu.comimages/icons/sip.webp#x20" /&gt; &lt;a href="https://sipapp.io/" rel="noopener" target="_blank"&gt;Sip&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As outlined in their 2017 article, &lt;a href="https://sipapp.medium.com/moving-from-mac-app-store-b41c9e2f53e8" rel="noopener" target="_blank"&gt;Moving from Mac App Store&lt;/a&gt;, the sandbox limitation is the primary reason&lt;/p&gt;
&lt;h3 id="cleanshot-x-iconimagesiconscleanshotwebpx20-cleanshot-xhttpscleanshotcom"&gt;


&lt;img alt="CleanShot X Icon" height="20" src="https://alinpanaitiu.comimages/icons/cleanshot.webp#x20" /&gt; &lt;a href="https://cleanshot.com/" rel="noopener" target="_blank"&gt;CleanShot X&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Their Screen Recording feature has three very useful functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audio recording&lt;/strong&gt; (this needs installing an &lt;strong&gt;audio loopback&lt;/strong&gt; device)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pressed keys overlay&lt;/strong&gt; (needs &lt;strong&gt;Input Monitoring&lt;/strong&gt; permissions)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select single window&lt;/strong&gt; (needs &lt;strong&gt;Accessibility Permissions&lt;/strong&gt; for getting the window position and size)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sketch-iconimagesiconssketchwebpx20-sketchhttpswwwsketchcom"&gt;


&lt;img alt="Sketch Icon" height="20" src="https://alinpanaitiu.comimages/icons/sketch.webp#x20" /&gt; &lt;a href="https://www.sketch.com/" rel="noopener" target="_blank"&gt;Sketch&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Honestly, I&amp;rsquo;m not sure about this one. The App Store is full of image editors and graphic content creation tools.&lt;/p&gt;
&lt;p&gt;I thing the unique pricing model is something they would have a hard time implementing on the App Store.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The unique pricing model of Sketch&lt;/em&gt;&lt;br /&gt;














&lt;a href="https://alinpanaitiu.comimages/sketch-pricing.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;sketch pricing model&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="sketch pricing model" src="https://alinpanaitiu.comimages/sketch-pricing.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="parallels-desktop-iconimagesiconsparallelswebpx20-parallels-desktophttpswwwparallelscomeuproductsdesktop"&gt;


&lt;img alt="Parallels Desktop Icon" height="20" src="https://alinpanaitiu.comimages/icons/parallels.webp#x20" /&gt; &lt;a href="https://www.parallels.com/eu/products/desktop/" rel="noopener" target="_blank"&gt;Parallels Desktop&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;They actually have an App Store edition, but it&amp;rsquo;s severely limited.&lt;/p&gt;
&lt;p&gt;Sharing things between the host and the VM is probably the largest functionality affected by the sandbox.&lt;/p&gt;
&lt;p&gt;They provide a table with everything that&amp;rsquo;s missing in their App Store version of the app: &lt;a href="https://kb.parallels.com/123796" rel="noopener" target="_blank"&gt;KB Parallels: What is the difference between Parallels Desktop App Store Edition and Standard Edition?&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="lunar-iconimagesiconslunarwebpx20-lunarhttpslunarfyi"&gt;


&lt;img alt="Lunar Icon" height="20" src="https://alinpanaitiu.comimages/icons/lunar.webp#x20" /&gt; &lt;a href="https://lunar.fyi/" rel="noopener" target="_blank"&gt;Lunar&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Low-level communication with monitors is only possible by using a lot of private and reverse engineered APIs (&lt;code&gt;IOKit&lt;/code&gt;, &lt;code&gt;DisplayServices&lt;/code&gt;, &lt;code&gt;IOAVService&lt;/code&gt; etc.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Accessibility Permissions&lt;/strong&gt; are also needed for listening and reacting to brightness and volume key events.&lt;/p&gt;
&lt;p&gt;Because of the sandbox, the &lt;a href="https://apps.apple.com/us/app/lunar-lite-screen-brightness/id1594174299" rel="noopener" target="_blank"&gt;lite App Store version of Lunar&lt;/a&gt; only supports software dimming and can only react to F1/F2 keys.&lt;/p&gt;
&lt;h3 id="soulver-iconimagesiconssoulverwebpx20-soulverhttpssoulverapp"&gt;


&lt;img alt="Soulver Icon" height="20" src="https://alinpanaitiu.comimages/icons/soulver.webp#x20" /&gt; &lt;a href="https://soulver.app/" rel="noopener" target="_blank"&gt;Soulver&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I think the &lt;strong&gt;free trial limitation&lt;/strong&gt; is the only thing keeping such a self-contained app outside the App Store.&lt;/p&gt;
&lt;p&gt;Soulver is incredibly complex and useful in its functionality, but I don&amp;rsquo;t think too many people would splurge $35 on a notepad-calculator app without trying it first. It deserves every single dollar of that price, that I can say for sure.&lt;/p&gt;</description><author>Copper • A blog about conductive layers</author><pubDate>Fri, 03 Dec 2021 19:28:39 GMT</pubDate><guid isPermaLink="true">https://alinpanaitiu.com/blog/apps-outside-app-store/</guid></item><item><title>Certain risks of drones were wildly exhagerated</title><link>https://stop.zona-m.net/2021/12/certain-risks-of-drones-were-wildly-exhagerated/</link><description>&lt;p&gt;But only because we were looking the wrong way.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 03 Dec 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/certain-risks-of-drones-were-wildly-exhagerated/</guid></item><item><title>How to customize your Transistor.fm Website with JS and CSS</title><link>https://www.swyx.io/customize-transistor-website</link><description>The default Transistor.fm website is kinda ugly. Here's how to customize your Transistor.fm website if you use Transistor. But also it's a simple guide to do clientside customizations of almost any website whose code you don't control.</description><author>swyx's site RSS Feed</author><pubDate>Fri, 03 Dec 2021 09:58:15 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/customize-transistor-website</guid></item><item><title>Will VR in Space be Indispensable?</title><link>https://www.tderflinger.com/en/vr-in-space-indispensable</link><description>With the proposed flight to Mars and its exploration, there is a long stay required by the astronauts under claustrophobic conditions. Could Virtual Reality (VR) systems alleviate some stress of long
stays in space?</description><author>Thoughts by Thomas Derflinger</author><pubDate>Fri, 03 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/vr-in-space-indispensable</guid></item><item><title>The job of regulation is to make decentralization work</title><link>https://stop.zona-m.net/2021/12/the-job-of-regulation-is-to-make-decentralization-work/</link><description>&lt;p&gt;Especially in a digitally networked age.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 02 Dec 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/the-job-of-regulation-is-to-make-decentralization-work/</guid></item><item><title>Introducing the OSS Quickstart Archetype</title><link>https://www.morling.dev/blog/introducing-oss-quickstart-archetype/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_the_challenge"&gt;The Challenge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_the_oss_quickstart_archetype"&gt;The OSS Quickstart Archetype&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_outlook"&gt;Outlook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I am very happy to announce the availability of the &lt;a href="https://github.com/moditect/oss-quickstart"&gt;OSS Quickstart Archetype&lt;/a&gt;!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Part of the &lt;a href="https://github.com/moditect/"&gt;ModiTect&lt;/a&gt; family of open-source projects,
this is a Maven archetype which makes it very easy to bootstrap new Maven-based open-source projects,
satisfying common requirements such as configuring plug-in versions, and adhering to best practices like auto-formatting the source code.
Think &lt;a href="https://maven.apache.org/archetypes/maven-archetype-quickstart/scm.html"&gt;Maven Quickstart Archetype&lt;/a&gt; and friends, but more modern, complete, and opinionated.&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Thu, 02 Dec 2021 19:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/introducing-oss-quickstart-archetype/</guid></item><item><title>XKB With Sway</title><link>https://blog.gnoack.org/post/xkb-with-sway</link><description>&lt;p&gt;This explains how to configure multiple keyboards in Sway, and how to
use advanced configuration when regular &lt;code&gt;xkb_options&lt;/code&gt; are not enough.&lt;/p&gt;
&lt;h2 id="sway-configuration"&gt;Sway configuration&lt;/h2&gt;
&lt;p&gt;Luckily, Sway has great support for multiple keyboards with different
layouts, so it nicely adjusts to whatever keyboard you have plugged in
or can use separate configurations for a physical keyboard and the
laptop keyboard.&lt;/p&gt;
&lt;p&gt;First, identify the identifiers for the keyboards you have plugged in,
using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-shell"&gt;$ swaymsg -t get_inputs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look for the lines starting with &lt;code&gt;Identifier:&lt;/code&gt; for the device you&amp;rsquo;re
interested in.&lt;/p&gt;
&lt;p&gt;Next, add one section for each of your keyboards to &lt;code&gt;~/.config/sway/config&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-text"&gt;input &amp;quot;1278:32:PFU_Limited_HHKB-Classic&amp;quot; {
	xkb_layout &amp;quot;us&amp;quot;
	xkb_model &amp;quot;hhk&amp;quot;
	xkb_options &amp;quot;compose:ralt&amp;quot;
	xkb_capslock &amp;quot;disabled&amp;quot;
}

input &amp;quot;1133:49948:Logitech_USB_Keyboard&amp;quot; {
	xkb_file &amp;quot;.xkb/keymap/logitech&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The example above lists a HHKB (&amp;ldquo;Happy Hacking&amp;rdquo;) keyboard where right
Alt acts as the Compose key. More options are documented in the man
pages &lt;a href="https://manned.org/sway-input.5"&gt;sway-input(5)&lt;/a&gt; as well as
&lt;a href="https://manned.org/xkeyboard-config.7"&gt;xkeyboard-config(7)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the case where the existing XKB options are not sufficient, you
need to refer to a separate XKB configuration file, as described below.&lt;/p&gt;
&lt;h2 id="xkb-configuration"&gt;XKB configuration&lt;/h2&gt;
&lt;p&gt;To configure my Logitech keyboard, the plan was to have the key
mapping reasonably close to the HHKB layout.&lt;/p&gt;
&lt;p&gt;In my case, I have two files under &lt;code&gt;~/.xkb&lt;/code&gt; (the directories need to
first be created): This is the &lt;code&gt;~/.xkb/keymap/logitech&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-text"&gt;// Derived from `setxkbmap -print`,
// added the &amp;quot;+unixy&amp;quot; part to xkb_symbols.
xkb_keymap {
	xkb_keycodes  { include &amp;quot;evdev+aliases(qwerty)&amp;quot;	};
	xkb_types     { include &amp;quot;complete&amp;quot;	};
	xkb_compat    { include &amp;quot;complete&amp;quot;	};
	xkb_symbols   { include &amp;quot;pc+us+inet(evdev)+unixy&amp;quot;	};
	xkb_geometry  { include &amp;quot;pc(pc105)&amp;quot;	};
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file is the output from running &lt;code&gt;setxkbmap -print&lt;/code&gt;, but adds the
&lt;code&gt;+unixy&lt;/code&gt; part to the &lt;code&gt;xkb_symbols&lt;/code&gt; section. This refers to the
&lt;code&gt;~/.xkb/symbols/unixy&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-text"&gt;partial alphanumeric_keys
xkb_symbols &amp;quot;unixy&amp;quot; {
	// exchange backspace and backslash
	key &amp;lt;BKSL&amp;gt; {	[ BackSpace, Backspace ] };
	key &amp;lt;BKSP&amp;gt; {	[ backslash, bar ] };

	// caps is ctrl
	key &amp;lt;CAPS&amp;gt; {	[ Control_L ] };
	modifier_map Control { &amp;lt;CAPS&amp;gt; };

	// right alt is compose
	key &amp;lt;RALT&amp;gt; {	[ Multi_key ] };
};
&lt;/code&gt;&lt;/pre&gt;</description><author>Blog on blog.gnoack.org</author><pubDate>Thu, 02 Dec 2021 19:24:17 GMT</pubDate><guid isPermaLink="true">https://blog.gnoack.org/post/xkb-with-sway</guid></item><item><title>Looking for a Serious-Use Travel Mouse</title><link>http://evantravers.com/articles/2021/12/02/looking-for-a-serious-use-travel-mouse/</link><description>&lt;div class="admonition tldr"&gt;
&lt;p&gt;I've been looking for a high-quality wireless mouse that supports USB-C: I found it in the &lt;a href="#steelseries-prime-mini"&gt;Steelseries Prime Mini&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For years, I have been using gaming mice for code and design. I used MX501 for a &lt;em&gt;long&lt;/em&gt; time, then a steady series of Razers… currently a Razer Deathadder. I'm just used to the feel, the speed, the quality. I can't use the bars of soap that the fruit company calls magic&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-magic" id="fnref-magic"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;As Mac has slowly moved to USB-C… so have I. I have tried to keep an eye on mice that support USB-C, and thought I'd take another look.&lt;/p&gt;
&lt;p&gt;Earlier in the year, I had picked up the latest Logitech MX Master 3. I did not like it. It didn't fit how I hold mice, I don't use 60% of the buttons and features, and it felt &lt;em&gt;very&lt;/em&gt; laggy.&lt;/p&gt;
&lt;p&gt;We are considering doing some traveling, so more coffee shop work could be a possibility. I want something USB-C, travel friendly, and fast. I don't like the lag I've seen in previous wireless mice.&lt;/p&gt;
&lt;p&gt;The last good travel mouse that I ever had was the first version Razer Orochi. It supported both wired and bluetooth, used AA batteries, and was my weapon of choice for a long time.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://images.evantravers.com/articles/2021/12/size_comparison.jpeg" /&gt;
  &lt;figcaption&gt;Orochi V1, G Pro Superlight, Prime Mini&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Look how small the Orochi is!&lt;/p&gt;
&lt;p&gt;What I'm looking for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OSX-friendly&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-osx" id="fnref-osx"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;USB-C&lt;/li&gt;
&lt;li&gt;low lag&lt;/li&gt;
&lt;li&gt;wireless, but supporting wired for charging and use&lt;/li&gt;
&lt;li&gt;not loud… no garish gaming logos and light effects&lt;/li&gt;
&lt;li&gt;travel friendly: small, onboard battery preferred&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the purposes of this search, I'm not going to try and evaluate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ergonomics&lt;/li&gt;
&lt;li&gt;tracking on weird surfaces. I bring a small mouse pad.&lt;/li&gt;
&lt;li&gt;how delightfully clicky are the buttons (so clicky!)&lt;/li&gt;
&lt;li&gt;build quality&lt;/li&gt;
&lt;li&gt;customer support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is also not one of my long-use reviews… yet. I'm making a purchase decision and returning the others. I put in an order for three options, and have spent the last day evaluating the following:&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#razer-orochi-v2" id="razer-orochi-v2"&gt;&lt;/a&gt;Razer Orochi V2&lt;/h2&gt;
&lt;p&gt;(&lt;a href="https://www.razer.com/gaming/mice/razer-orochi-v2"&gt;official link&lt;/a&gt;, $69.99 MSRP)&lt;/p&gt;
&lt;p&gt;&lt;img alt="orochi" src="https://images.evantravers.com/articles/2021/12/orochi.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;Because of my long fondness for the first version and the fact that my Deathadder has been so successful on OSX, this was the first one that I reached for. Of the three, this is the one that is billed as a &amp;quot;travel mouse.&amp;quot;&lt;/p&gt;
&lt;p&gt;The Orochi v2 is also the only one of the three that supports wireless without a dongle through bluetooth, just like the first version. The Orochi v2 does &lt;em&gt;not&lt;/em&gt; support any wired mode.&lt;/p&gt;
&lt;p&gt;The Orochi v2 is significantly bigger than the v1, and a touch smaller than the other two mice. It has that familiar Razer grip.&lt;/p&gt;
&lt;p&gt;You have to download Razer Synapse to get the mouse speed up to my customary speeds. The default three settings weren't fast enough. I'd tell you how it went, but Synapse doesn't work after OSX Mojave. 👎 Fail.&lt;/p&gt;
&lt;p&gt;I don't like that it requires batteries… nothing else in my bag does. It's nice that you have the choice of using either AA or AAA, but it's a dealbreaker for me. I want my &lt;a href="https://evantravers.com/articles/2018/12/10/whats-in-my-bag/#goruck-wire-dopp"&gt;wire dopp&lt;/a&gt; to just have some USB-C cables, a power brick, and go.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#logitech-g-pro-superlight" id="logitech-g-pro-superlight"&gt;&lt;/a&gt;Logitech G Pro Superlight&lt;/h2&gt;
&lt;p&gt;(&lt;a href="https://www.logitechg.com/en-us/products/gaming-mice/pro-x-superlight-wireless-mouse.910-005878.html"&gt;official link&lt;/a&gt;, $149.99 MSRP)&lt;/p&gt;
&lt;p&gt;&lt;img alt="g pro" src="https://images.evantravers.com/articles/2021/12/gpro.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;Immediately after taking this one out of the box I gasped. It's &lt;em&gt;really&lt;/em&gt; light.&lt;/p&gt;
&lt;p&gt;It fails my USB-C requirement… twice. It has a trim little USB-A dongle, and it's charging port has a custom fitted USB-micro cable. It's not a bad mouse cable, but it's stiff and not as nice as the one with the Prime.&lt;/p&gt;
&lt;p&gt;The USB-A dongle does have the neat feature of being stowable inside the mouse's shell for travel… but I'll need to carry an adapter anyway which kind of ruins it for me.&lt;/p&gt;
&lt;p&gt;I put it on the list because I saw a lot of people using it online, and was curious how a wireless mouse held up in terms of responsiveness and latency. Of the three I tested, it has the most options and the highest DPI/CPI, but there's a catch:&lt;/p&gt;
&lt;p&gt;There's no onboard DPI/polling controls! Out of the box, it's set to the lowest setting, a paiiiinful crawl. You have to install Logitech's &lt;a href="https://www.logitechg.com/en-us/innovation/g-hub.html"&gt;G Hub&lt;/a&gt; software (&lt;code&gt;brew install logitech-g-hub&lt;/code&gt;) which is powerful and works on Mac, but I just need a mouse, not separate sniping macros for each game. G Hub wanted me to do a firmware update on both the mouse and the dongle as soon as it booted, which required me finding two USB-A adapters because they have to both be connected to update.&lt;/p&gt;
&lt;p&gt;While it took overly long to setup (made &lt;em&gt;painful&lt;/em&gt; by the default sloooooow settings) once connected the mouse is fast and responsive. I had no clue that a wireless mouse could feel so fast. I am really impressed, but in order to use this mouse on the road, I have to keep track of a couple custom cables &lt;em&gt;just&lt;/em&gt; for the G Pro. Not ideal.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#steelseries-prime-mini" id="steelseries-prime-mini"&gt;&lt;/a&gt;Steelseries Prime Mini&lt;/h2&gt;
&lt;p&gt;(&lt;a href="https://steelseries.com/gaming-mice/prime-mini-wireless"&gt;official link&lt;/a&gt;, $59.99 MSRP)&lt;/p&gt;
&lt;p&gt;&lt;img alt="prime" src="https://images.evantravers.com/articles/2021/12/prime.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;I am a little surprised how much I liked this mouse. I had two Steelseries mice in the past, the Xai and the Sensei. Both had &lt;em&gt;very&lt;/em&gt; strange tracking issues I was never able to nail down. It could have to do with my lack of knowledge on polling and such, but it was frustrating.&lt;/p&gt;
&lt;p&gt;The Steelseries Prime Mini is the first USB-C only gaming mouse that I have yet found. It's &lt;em&gt;very&lt;/em&gt; right-handed, so lefties beware. It's a touch heavier than the ridiculously light G Pro, but not overwhelmingly. Like the G Pro, I was impressed with it's wireless latency… it feels as fast as a wired mouse.&lt;/p&gt;
&lt;p&gt;The included dongle for the connection is USB-C! It's fat though… but will barely permit both the default Mac charging USB-C cable and the old Earpods TRRS. It doesn't store in the body like the G Pro, so don't lose it&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-dongle" id="fnref-dongle"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="dongle" src="https://images.evantravers.com/articles/2021/12/prime_dongle.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;More importantly, the port on the front of the mouse is also USB-C, and seems to work fine with the number of USB-C cables I have lying around. They have provided a USB-C -&amp;gt; USB-A cable with it, it's paracord wrapped, very light and indistinguishable from a normal wired mouse. It's as nice or nicer than the fixed cable I've got with my Deathadder. It actually feels as light as the one on my friend's Glorious Model O.&lt;/p&gt;
&lt;p&gt;I appreciate that the Prime Mini &lt;em&gt;requires no software to update or maintain&lt;/em&gt;, there's a hardware DPI/polling button on the bottom. There is &lt;a href="https://steelseries.com/engine/"&gt;Steelseries GG&lt;/a&gt;, which does work on the mac and supports even higher DPI/CPI settings. Didn't need it anyway, the external button was enough.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#conclusion" id="conclusion"&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;For me, the Steelseries Prime Mini wins hands down. Because of my travel objectives, the fact that it uses the same cables as my keyboards, iPads, and computer charging makes it an easy choice to grab on my way to a coffeeshop. I like that I could use it wired on my standing desk, grab it and throw it in my backpack to take to a remote location to use with the dongle. Best, at half the price of the G Pro Superlight I feel less guilty buying &lt;em&gt;another&lt;/em&gt; peripheral. If I was choosing purely for gaming/desk feel, I would probably be keeping the G Pro Superlight. It just feels that nice… but not nice enough to maintain yet another special cable that only works for it.&lt;/p&gt;
&lt;p&gt;It will serve as my primary mouse for a season, both at my desk and on the road. The Prime Mini. A serious-use mouse for serious people, just plug and play. 👍&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-magic"&gt;
&lt;p&gt;If you or someone you love insists on using the Magic Mouse, consider the wonderful &lt;a href="https://www.elevationlab.com/products/magicgrips-for-magic-mouse"&gt;Elevation Labs Magic Grips&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref-magic"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-osx"&gt;
&lt;p&gt;You'd be surprised how many mice don't work quite right on OSX. I also recommend &lt;a href="https://github.com/evantravers/dotfiles/commit/627f99478f33ae84ab67b2dee1d3ce796f40deeb"&gt;turning off mouse acceleration&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref-osx"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-dongle"&gt;
&lt;p&gt;It does look like Steelseries lets you &lt;a href="https://steelseries.com/gaming-accessories/prime-mini-wireless-usb-c-dongle"&gt;buy replacement dongles&lt;/a&gt; for $9.99, which isn't bad. &lt;a class="footnote-backref" href="#fnref-dongle"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Thu, 02 Dec 2021 15:33:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/12/02/looking-for-a-serious-use-travel-mouse/</guid></item><item><title>How to set up Sphinx for Read the Docs</title><link>https://river.me/blog/how-to-readthedocs/</link><description>A tutorial for configuring Sphinx for Read the Docs for your Python projects</description><author>River Writes - A MediaWiki Blog</author><pubDate>Thu, 02 Dec 2021 11:54:37 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/how-to-readthedocs/</guid></item><item><title>Fave New Podcasts in 2021</title><link>https://www.swyx.io/fave-podcasts-2021</link><description>My picks of best podcasts I found in 2021, across tech, business, and creator categories. I also pick up on best of the best from 2020.</description><author>swyx's site RSS Feed</author><pubDate>Thu, 02 Dec 2021 09:25:13 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/fave-podcasts-2021</guid></item><item><title>Becoming physically immune to brute-force attacks</title><link>https://seirdy.one/posts/2021/01/12/password-strength/</link><description>Using thermal physics, cosmology, and computer science to calculate password vulnerability to the biggest possible brute-force attack.</description><author>All content on Seirdy’s Home</author><pubDate>Thu, 02 Dec 2021 08:01:21 GMT</pubDate><guid isPermaLink="true">https://seirdy.one/posts/2021/01/12/password-strength/</guid></item><item><title>Engineering vs. Technology</title><link>https://ayewo.com/engineering-vs-technology-company/</link><description>&lt;p&gt;In this article, I attempt to chronicle how I arrived at what I think is a layman explanation of the difference between the terms engineering and technology .&lt;/p&gt;&lt;h2 id="an-incomplete-devil-s-dictionary"&gt;An Incomplete Devil&amp;#x2019;s Dictionary&lt;/h2&gt;&lt;p&gt;My goal with this series is to offer layman definitions of words used in the technology industry.&lt;/p&gt;</description><author>Misleading Metaphors</author><pubDate>Thu, 02 Dec 2021 03:16:38 GMT</pubDate><guid isPermaLink="true">https://ayewo.com/engineering-vs-technology-company/</guid></item><item><title>Italia V - Positano Revisited, Napoli</title><link>https://faingezicht.com/photos/2021/12/02/italia-revisited/</link><description>In retrospect, I should have taken a lot more film photos during our trip to Italy. I still don't fully trust myself with film, but there's something that makes these feel very special.

Below are a few more pics from our time in Positano, and then the end of the trip in Napoli.

_This is the fourth in a series shared throughout Fall 2021. When you're done here, make sure to also check out parts &lt;a href="/photos/2021/10/14/umbria/"&gt;one&lt;/a&gt;, &lt;a href="/photos/2021/10/19/florence/"&gt;two&lt;/a&gt;, &lt;a href="/photos/2021/10/31/roma/"&gt;three&lt;/a&gt;, and &lt;a href="/photos/2021/11/12/positano/"&gt;four&lt;/a&gt;._</description><author>Avy Faingezicht</author><pubDate>Thu, 02 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/12/02/italia-revisited/</guid></item><item><title>LEAD Capstone</title><link>https://solomon.io/lead-capstone/</link><description>Leadership Exploration, Application and Development—or LEAD as it’s known—is a 6-month leadership course offered by Salesloft.</description><author>Sam Solomon</author><pubDate>Thu, 02 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://solomon.io/lead-capstone/</guid></item><item><title>An incoming risk from Google, Apple, Twitter etc.. is coming</title><link>https://stop.zona-m.net/2021/12/an-incoming-risk-from-google-apple-twitter-etc..-is-coming/</link><description>&lt;p&gt;Trouble is, WHEN?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 01 Dec 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/12/an-incoming-risk-from-google-apple-twitter-etc..-is-coming/</guid></item><item><title>Raster-Based 3D Models with QGIS, OSM and Aerialod</title><link>https://geo.rocks/post/simple-raster-based-3d-models/</link><description>&lt;p&gt;&lt;img alt="Streets of Rome" src="https://geo.rocks/img/rome.jpg" title="Streets of Rome" /&gt;&lt;/p&gt;
&lt;p&gt;Creating 3D models from raster or vector data has never been that easy! This tutorial takes you through the whole process from vector/raster data to 3D in just 3 steps.&lt;/p&gt;</description><author>Geography &amp;amp; Coding</author><pubDate>Wed, 01 Dec 2021 17:58:12 GMT</pubDate><guid isPermaLink="true">https://geo.rocks/post/simple-raster-based-3d-models/</guid></item><item><title>STEM: Science, Technology, Engineering, and Mathematics</title><link>https://ayewo.com/stem/</link><description>A clear and concise definition for each discipline that makes up the STEM acronym.</description><author>Misleading Metaphors</author><pubDate>Wed, 01 Dec 2021 17:56:38 GMT</pubDate><guid isPermaLink="true">https://ayewo.com/stem/</guid></item><item><title>How to avoid a climate disaster</title><link>https://www.bajkowski.com/how-to-avoid-a-climate-disaster/</link><description>A brief review of Bill Gates' book How to Avoid a Climate Disaster: The Solutions We Have and the Breakthroughs We Need</description><author>Bajkowski.com</author><pubDate>Wed, 01 Dec 2021 06:44:36 GMT</pubDate><guid isPermaLink="true">https://www.bajkowski.com/how-to-avoid-a-climate-disaster/</guid></item><item><title>CKAD Experience</title><link>https://blog.yelinaung.com/posts/ckad-experience/</link><description>&lt;p&gt;I recently took the &lt;a href="https://www.cncf.io/certification/ckad/"&gt;Certified Kubernetes Application Developer (CKAD)&lt;/a&gt; exam and &lt;a href="https://www.credly.com/badges/e3cf473d-e91a-4f8d-a732-59261dbdefe9/public_url"&gt;passed&lt;/a&gt; with 97 out of 100.
Some background about myself. I have been a backend + infra engineer working with Kubernetes via Google Cloud&amp;rsquo;s managed service: &lt;a href="https://cloud.google.com/kubernetes-engine"&gt;GKE&lt;/a&gt; for the past few years.
I am also a (Ubuntu) Linux user for both the desktop and servers. I am familiar with basic linux commands, bash, tmux and vim.&lt;/p&gt;
&lt;p&gt;The primary motivations for me were&lt;/p&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Wed, 01 Dec 2021 06:08:12 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/posts/ckad-experience/</guid></item><item><title>Advent of Code 2021, day 1</title><link>https://viralinstruction.com/posts/aoc2021_1/index.html</link><description>Curious about Julia? Why not use Advent of Code 2021 to learn it&amp;#33;</description><author>viralinstruction</author><pubDate>Wed, 01 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://viralinstruction.com/posts/aoc2021_1/index.html</guid></item><item><title>DICOM File Format Basics</title><link>https://www.vladsiv.com/posts/dicom-file-format-basics</link><description>Explore the fundamentals of the DICOM file format! This quick introduction covers the basics of DICOM's structure, its essential uses, and tips for easily navigating its complex and abstract components.</description><author>vladsiv</author><pubDate>Wed, 01 Dec 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.vladsiv.com/posts/dicom-file-format-basics</guid></item><item><title>Math may be just math, but algorithms are written by people</title><link>https://stop.zona-m.net/2021/11/math-may-be-just-math-but-algorithms-are-written-by-people/</link><description>&lt;p&gt;And people have biases. Just ask Safiya Noble.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 01 Dec 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/math-may-be-just-math-but-algorithms-are-written-by-people/</guid></item><item><title>Develop Ali Express Scraper in Python with Scraper API</title><link>https://blog.adnansiddiqi.me/develop-ali-express-scraper-in-python-with-scraper-api/</link><description>&lt;p&gt;This is another post in ScrapeTheFamous, in which I will be parsing some famous websites and will discuss my development process. The posts will be using Scraper API for parsing purposes which makes me free from all worries about blocking and rendering dynamic sites since Scraper API takes care of everything. In this post, we are going to scrape AliExpress. AliExpress is a Chinese B2C portal to buy stuff. The script I am going to make consists of two parts, or I say, two functions: fetch and parse. The fetch will accept a category and return all links of individual items and parse will parse an individual entry and returns a few data points in JSON format. So, let&amp;#8217;s begin! def fetch(url): links = [] headers = { 'authority': 'www.aliexpress.com', 'pragma': 'no-cache', 'cache-control': 'no-cache', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'dnt': '1', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'sec-fetch-site': 'none', 'sec-fetch-mode': 'navigate', 'sec-fetch-user': '?1', 'sec-fetch-dest': 'document', 'accept-language': 'en-US,en;q=0.9,ur;q=0.8,zh-CN;q=0.7,zh;q=0.6', } with open('API_KEY.txt', encoding='utf8') as f: API_KEY = f.read() URL_TO_SCRAPE = url BASE_URL = 'https://www.aliexpress.com' payload = {'api_key': API_KEY, 'url': URL_TO_SCRAPE, 'render': 'true'} r = requests.get('http://api.scraperapi.com', params=payload, timeout=60, headers=headers) if r.status_code == 200: html = r.text.strip() soup = BeautifulSoup(html, 'lxml') links_section = soup.select('._3KNwG') for link in links_section: links.append(BASE_URL + link['href']) return links The API_KEY.txt file contains the Scraper API key. After that, I fetch all elements that have the class ._3KNwG in them and then store them in a list(). You may store them in a file or database, up to you. The second function is parse() and it looks like below: def parse(url): title = '' price = '' image = '' store_name = '' record = {} headers = { 'authority': 'www.aliexpress.com', 'pragma': 'no-cache', 'cache-control': 'no-cache', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'dnt': '1', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'sec-fetch-site': 'none', 'sec-fetch-mode': 'navigate', 'sec-fetch-user': '?1', 'sec-fetch-dest': 'document', 'accept-language': 'en-US,en;q=0.9,ur;q=0.8,zh-CN;q=0.7,zh;q=0.6', } with open('API_KEY.txt', encoding='utf8') as f: API_KEY = f.read() payload = {'api_key': API_KEY, 'url': url, 'render': 'true', 'country_code': 'pk', 'keep_headers': 'true'} r = requests.get('http://api.scraperapi.com', params=payload, timeout=60, headers=headers) if r.status_code == 200: html = r.text.strip() soup = BeautifulSoup(html, 'lxml') title_section = soup.select('.product-title-text') if title_section: title = title_section[0].text.strip() price_section = soup.select('.uniform-banner-box-price') if price_section: price = price_section[0].text.strip() image_section = soup.select('.image-viewer img') if image_section: image = image_section[0]['src'] store_section = soup.select('.shop-name a') if store_section: store_name = store_section[0].text.strip() record = {'title': title, 'price': price, 'image': image, 'store': store_name} return record You need to pay attention to the payload variable where I introduced two new parameters: country_code and keep_headers. The reason I set country_code here because AliExpress is available in multiple countries. Since ScraperAPI uses proxies by default, it was picking the proxy IP of a random country and was not displaying price in local Pakistani currency. By setting the country I am forcing ScraperAPI to use Pakistan-based proxies. It is quite a useful feature because many websites allow local traffic only and don&amp;#8217;t allow global visitors to visit them. The rest of the code is self-explanatory. The reason I am using render as True because AliExpress is generating content by parsing Javascript. Conclusion So in this post, you learned how to scrape e-commerce sites like AliExpress to get the desired data by using Scraper API. You do not have to worry about Proxy IPs either nor do you have to pay hundreds of dollars, especially when you are an individual or working in a startup. Companies spend 100s of dollars on a monthly basis just for the proxy IPs. Oh if you sign up here with my referral link or enter promo code adnan10, you&amp;#8217;ll get a 10% discount on it. In case you do not get the discount then just let me know via email on my site and I’d sure help you out. &amp;#160; If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/develop-ali-express-scraper-in-python-with-scraper-api/"&gt;Develop Ali Express Scraper in Python with Scraper API&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Tue, 30 Nov 2021 13:30:33 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/develop-ali-express-scraper-in-python-with-scraper-api/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #48</title><link>https://justingarrison.com/123dev/2021-11-30-123dev-48/</link><description>&lt;img alt="A very large cable connector is plugged into an old machine" src="https://justingarrison.com/img/123dev/48-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Logic&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How people think has been on the mind of philosophers for ages. Aristotle had the most famous works on logic for around 2000 years. In the 1600&amp;rsquo;s Gottfried Leibniz attempted to improve on Aristotle&amp;rsquo;s logic by simplifying it and representing it in mathematical notation.&lt;/p&gt;
&lt;p&gt;Two hundred years later George Boole further simplified Leibniz&amp;rsquo;s logic and notation into what we know today as Boolean algebra. Every introduction to computer science will teach you AND, OR, and NOT which are the foundational components of human logic.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 30 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-11-30-123dev-48/</guid></item><item><title>What I learnt building a SaaS</title><link>https://gaganpreet.in/posts/lessons-learnt-saas/</link><description>&lt;h2 class="relative group" id="background"&gt;Background &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#background"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;It was around the start of the pandemic in 2020 that I started hacking away at some of the ideas that I had collected but didn&amp;rsquo;t have a chance to work on. I had come across the FingerprintJS project, and I had an idea to use similar fingerprints for a cookie-free analytics platform. At that time, other competitors in this market were still in their infancy, and I wanted to build an analytics product that will have more information for marketeers to base business decisions on. I started building &lt;a href="https://nocookieanalytics.com/" rel="noreferrer" target="_blank"&gt;No Cookie Analytics&lt;/a&gt;.&lt;/p&gt;</description><author>Gaganpreet Arora</author><pubDate>Mon, 29 Nov 2021 23:26:02 GMT</pubDate><guid isPermaLink="true">https://gaganpreet.in/posts/lessons-learnt-saas/</guid></item><item><title>O Kafka, Where Art Thou?</title><link>https://www.morling.dev/blog/kafka-where-art-thou/</link><description>&lt;div class="paragraph"&gt;
&lt;p&gt;The other day, I came across an &lt;a href="https://www.reddit.com/r/java/comments/r2z17a/has_any_one_attempted_to_write_logs_directly_to/"&gt;interesting thread&lt;/a&gt; in the Java sub-reddit, with someone asking:
"Has anyone attempted to write logs directly to Kafka?".
This triggered a number of thoughts and questions for myself,
in particular how one should deal in an application when an attempt to send messages to Kafka fails,
for instance due to some network connectivity issue?
What do you do when you cannot reach the Kafka broker?&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Mon, 29 Nov 2021 19:55:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/kafka-where-art-thou/</guid></item><item><title>一些实用工具列表</title><link>https://www.4async.com/2021/11/awesome-toolkit/</link><description>&lt;img alt="Featured image of post 一些实用工具列表" src="https://www.4async.com/2021/11/awesome-toolkit/toolkit.png" /&gt;&lt;p&gt;一些在工作中经常使用的一些工具。如果有什么推荐的，也欢迎在评论中提供。这个列表后续会持续更新&lt;/p&gt;
&lt;h2 id="http工具"&gt;HTTP工具
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://curlie.io" rel="noopener" target="_blank"&gt;&lt;strong&gt;curlie&lt;/strong&gt;&lt;/a&gt; - httpie-like 工具，底层是curl&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/go-acme/lego" rel="noopener" target="_blank"&gt;&lt;strong&gt;lego&lt;/strong&gt;&lt;/a&gt; - Let&amp;rsquo;s Encrypt证书工具&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/FiloSottile/mkcert" rel="noopener" target="_blank"&gt;&lt;strong&gt;mkcert&lt;/strong&gt;&lt;/a&gt; - 方便导入本地证书&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://paw.cloud" rel="noopener" target="_blank"&gt;&lt;strong&gt;paw.cloud&lt;/strong&gt;&lt;/a&gt; - 原生的macOS HTTP调试工具，现在每年都会免费送，有兴趣关注一下&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="编译工具"&gt;编译工具
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://taskfile.dev" rel="noopener" target="_blank"&gt;&lt;strong&gt;go-task&lt;/strong&gt;&lt;/a&gt; - 我用来替代Makefile，并无什么特殊必要，主要是不想写Makefile&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="代码质量"&gt;代码质量
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/golangci/golangci-lint" rel="noopener" target="_blank"&gt;&lt;strong&gt;golangci-lint&lt;/strong&gt;&lt;/a&gt; - 感觉无需介绍了，集成了很多实用工具，重复的就不列举了&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pre-commit.com" rel="noopener" target="_blank"&gt;&lt;strong&gt;pre-commit&lt;/strong&gt;&lt;/a&gt; - 提交前检查代码质量，比如代码风格，缩进，空格等等&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/boyter/dcd" rel="noopener" target="_blank"&gt;&lt;strong&gt;dcd&lt;/strong&gt;&lt;/a&gt; - 查找代码中的重复代码&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="代码统计"&gt;代码统计
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/boyter/scc" rel="noopener" target="_blank"&gt;&lt;strong&gt;scc&lt;/strong&gt;&lt;/a&gt; - 高性能统计代码行数&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="图表工具"&gt;图表工具
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/blushft/go-diagrams" rel="noopener" target="_blank"&gt;&lt;strong&gt;go-diagrams&lt;/strong&gt;&lt;/a&gt; - 使用Go语言描述系统架构图&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/k1LoW/ndiag" rel="noopener" target="_blank"&gt;&lt;strong&gt;ndiag&lt;/strong&gt;&lt;/a&gt; - 如果不想用Go描述，也可以选择用YAML描述系统架构&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/lucasepe/draft" rel="noopener" target="_blank"&gt;&lt;strong&gt;draft&lt;/strong&gt;&lt;/a&gt; - 另外一个用YAML描述的工具，风格不一样&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/mkimuram/k8sviz" rel="noopener" target="_blank"&gt;&lt;strong&gt;k8sviz&lt;/strong&gt;&lt;/a&gt; - 你也可以从现成的K8s环境中生成系统架构图&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/storj/archview" rel="noopener" target="_blank"&gt;&lt;strong&gt;archview&lt;/strong&gt;&lt;/a&gt; - 通过代码中注释生成应用内部分层结构&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/bykof/go-plantuml" rel="noopener" target="_blank"&gt;&lt;strong&gt;go-plantuml&lt;/strong&gt;&lt;/a&gt; - 根据Go代码生成结构体的PlantUML图&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/jfeliu007/goplantuml" rel="noopener" target="_blank"&gt;&lt;strong&gt;goplantuml&lt;/strong&gt;&lt;/a&gt; - 另外一种生成PlantUML的工具&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/gmarik/go-erd" rel="noopener" target="_blank"&gt;&lt;strong&gt;go-erd&lt;/strong&gt;&lt;/a&gt; - 不想用PlantUML也可以换这种风格&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://asciiflow.com" rel="noopener" target="_blank"&gt;&lt;strong&gt;asciiflow&lt;/strong&gt;&lt;/a&gt; - 可以画ASCII图，ASCII图好处是可以放在代码里，如果你愿意的话&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://textart.io/sequence" rel="noopener" target="_blank"&gt;&lt;strong&gt;sequence&lt;/strong&gt;&lt;/a&gt; - 嫌弃asciiflow比较原始，做时序图的时候可以用这个&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://mermaid-js.github.io/mermaid-live-editor/" rel="noopener" target="_blank"&gt;&lt;strong&gt;mermaid-js&lt;/strong&gt;&lt;/a&gt; - 方便集成在网页中，也可以导出成图片&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kroki.io/" rel="noopener" target="_blank"&gt;&lt;strong&gt;kroki&lt;/strong&gt;&lt;/a&gt; - 上面没提到的图类型的生成？看看这个&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="iac"&gt;IaC
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.pulumi.com/" rel="noopener" target="_blank"&gt;&lt;strong&gt;pulumi&lt;/strong&gt;&lt;/a&gt; - Terraform业界比较常用，不过要学习HCL比较蛋疼，我个人比较喜欢pulumi，可以选择自己的习惯的语言，tf-cdk目前还比较初级。&lt;/li&gt;
&lt;/ul&gt;</description><author>ipfans's Blog</author><pubDate>Mon, 29 Nov 2021 19:48:00 GMT</pubDate><guid isPermaLink="true">https://www.4async.com/2021/11/awesome-toolkit/</guid></item><item><title>The meditation that keeps capitalism happy...</title><link>https://stop.zona-m.net/2021/11/the-meditation-that-keeps-capitalism-happy.../</link><description>&lt;p&gt;&amp;hellip;would have never happened without social media.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 29 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/the-meditation-that-keeps-capitalism-happy.../</guid></item><item><title>A life-changing trip to Mexico on the Day of the Dead</title><link>https://olshansky.info/posts/a-life-changing-trip-to-mexico-on-the-day-of-the-dead/</link><description>My personal journey into joining the Crypto space full-time at Pocket</description><author>🦉 olshansky 🦁</author><pubDate>Mon, 29 Nov 2021 18:04:52 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/posts/a-life-changing-trip-to-mexico-on-the-day-of-the-dead/</guid></item><item><title>Chet Baker, born to be cool</title><link>https://nicolaiarocci.com/chet-baker-born-to-be-cool/</link><description>&lt;p&gt;A great piece of writing on jazz has recently been posted on The Smart Set. In
&lt;a href="https://www.thesmartset.com/born-to-be-cool/"&gt;Born to be cool&lt;/a&gt;, Matthew Duffus writes about jazz trumpeter Chet Baker,
reporting about his troubled life, musical prowess, influence, and legacy. Some
facts are well known, like the reception and then the competition with trumpet
legends such as Miles Davis and Dizzy Gillespie; other tidbits are less known
(to me at least). On Baker legacy:&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 29 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/chet-baker-born-to-be-cool/</guid></item><item><title>Autonomous Teams</title><link>https://www.craigpardey.com/post/2021-11-29-autonomous-teams/</link><description>&lt;p&gt;One thing that development teams constantly struggle with is striking a balance between building features, fixing bugs, paying down tech debt, and embarking on more ambitious activities like replacing core components in their architecture. It&amp;rsquo;s also something that Agile tries to solve but I&amp;rsquo;ve never seen it solved to everyone&amp;rsquo;s satisfaction.&lt;/p&gt;
&lt;p&gt;And I think it&amp;rsquo;s because many Agile Product Owners are focused on the specifics of &lt;em&gt;what features&lt;/em&gt; they&amp;rsquo;d like to see built instead of &lt;em&gt;setting the direction&lt;/em&gt; for the dev team and letting them figure out how to get there. They allocate some random percentage of the sprint to tech debt just to keep the team happy but it&amp;rsquo;s hard to make significant changes with 10% of the sprint capacity.&lt;/p&gt;</description><author>Craig Pardey</author><pubDate>Mon, 29 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.craigpardey.com/post/2021-11-29-autonomous-teams/</guid></item><item><title>Encanto</title><link>https://blog.yiningkarlli.com/2021/11/encanto.html</link><description>&lt;div style="display: none;"&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;

&lt;div class="tableofcontents"&gt;
    &lt;div class="tableofcontents-row"&gt;
        &lt;div class="tableofcontents-column3"&gt;
            &lt;div class="tableofcontents-content"&gt;
                1. &lt;a href="/2021/11/encanto.html#2021-11-29-work"&gt;Making Encanto&lt;/a&gt;&lt;br /&gt;
                2. &lt;a href="/2021/11/encanto.html#2021-11-29-frames"&gt;Frames from the Film&lt;/a&gt;&lt;br /&gt;
                3. &lt;a href="/2021/11/encanto.html#2021-11-29-references"&gt;References&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column3"&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column3"&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="2021-11-29-work"&gt;&lt;/div&gt;

&lt;p&gt;For the first time since 2016, Walt Disney Animation Studios is releasing not just one animated feature in a year, but two!
The second Disney Animation release of 2021 is &lt;a href="https://movies.disney.com/encanto"&gt;&lt;em&gt;Encanto&lt;/em&gt;&lt;/a&gt;, 
which marks a major milestone as Disney Animation’s 60th animated feature film.
&lt;em&gt;Encanto&lt;/em&gt; is a musical set in Colombia about a girl named Mirabel and her family: the amazing, fantastical, magical Madrigals.
I’m proud of every Disney Animation project that I’ve had the privilege to work on, but I have to admit that this year was something different and something very special to me, because this year we completed both &lt;em&gt;Raya and the Last Dragon&lt;/em&gt; and &lt;em&gt;Encanto&lt;/em&gt;, which are together two of my favorite Disney Animation projects so far.
Earlier this year, I wrote about the &lt;a href="https://blog.yiningkarlli.com/2021/03/raya-and-the-last-dragon.html"&gt;amazing work that went into &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;&lt;/a&gt; and why I loved working on that project; with &lt;em&gt;Encanto&lt;/em&gt; now in theaters, I now get to share why I’ve loved working on &lt;em&gt;Encanto&lt;/em&gt; so much as well!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_039.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_039.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disney Animation feature films take many years and hundreds of people to make, and often the film’s story can remain in a state of flux for much of the film’s production.
All of the above isn’t unusual; large-scale creative endeavors like filmmaking often entail an extremely complex and challenging process.
More often than not, a film requires time and many iterations to really find its voice and gain that spark that makes it a great film.
&lt;em&gt;Encanto&lt;/em&gt;, however, is a film that a lot of my coworkers and I realized was going to be really special very early on in production.
Now obviously, that hunch didn’t mean that making &lt;em&gt;Encanto&lt;/em&gt; was easy by any means; every film requires tons of hard work from the most amazing, inspiring, talented artists and engineers that I know.
But, I think in the end, that initial hunch about &lt;em&gt;Encanto&lt;/em&gt; was proven correct: the finished &lt;em&gt;Encanto&lt;/em&gt; has a story that is bursting with warmth and meaning, has one of Disney Animation’s best main characters to date with a huge cast of charming supporting characters, has the most beautiful, magical animation and visuals we’ve ever done, and sets all of the above to a wonderful soundtrack with a bunch of catchy, really cleverly written new songs.
Both the production process and final film for &lt;em&gt;Encanto&lt;/em&gt; were a strong reminder for me of why I love working on Disney Animation films in the first place.&lt;/p&gt;

&lt;p&gt;From a technical perspective, &lt;em&gt;Encanto&lt;/em&gt; also represents something very special in the history of Disney Animation’s continual advancements in animation technology.
To understand why this is, a very brief history review about Disney Animation’s modern production pipeline and toolset is helpful.
In retrospect, Disney Animation’s 50th animated feature film, &lt;em&gt;Tangled&lt;/em&gt;, was probably one of the most important films the studio has ever made from a technical perspective, because the production of &lt;em&gt;Tangled&lt;/em&gt; required a near-total ground-up rebuild of the studio’s production pipeline and tools that wound up laying the technical foundations for Disney Animation’s modern era.
While every film we’ve made since &lt;em&gt;Tangled&lt;/em&gt; has seen us make enormous technical strides in a variety of eras, the starting point of the production pipeline we’ve used and evolved for every CG film up until &lt;em&gt;Encanto&lt;/em&gt; were put into place during &lt;em&gt;Tangled&lt;/em&gt;.
The fact that &lt;em&gt;Encanto&lt;/em&gt; is Disney Animation’s 60th animated feature film is therefore fitting; &lt;em&gt;Encanto&lt;/em&gt; is the first film made using the USD-based successor &lt;a href="https://dl.acm.org/doi/10.1145/3532836.3536236"&gt;[Miller et al. 2022]&lt;/a&gt; to the production pipeline that was first built for &lt;em&gt;Tangled&lt;/em&gt;, and just like how &lt;em&gt;Tangled&lt;/em&gt; laid the technical foundations for the subsequent ten films that followed, &lt;em&gt;Encanto&lt;/em&gt; lays the technical foundations for many more future films to come!
As presented in the USD Birds of a Feather session at SIGGRAPH 2021, this new production pipeline is built on the open-source Universal Scene Description project and brings massive upgrades to almost every piece of software and every custom tool that our artists use.
Already &lt;em&gt;Encanto&lt;/em&gt;’s upgraded production pipeline has enabled cool new tools that would have been much harder to create previously, such as a new command center tool that gives a bird’s eye unified overview of stats and data across the entire movie &lt;a href="https://doi.org/10.1145/3532836.3536242"&gt;[Tennant et al. 2022]&lt;/a&gt;.
An absolutely monumental amount of work was put into building a new USD-based world at Disney Animation, but I think the effort was extremely worthwhile: thanks to the work done on &lt;em&gt;Encanto&lt;/em&gt;, Disney Animation is now well set up for another decade of technical innovation and another decade of pushing animation as a medium forward.&lt;/p&gt;

&lt;p&gt;Moving to a new production pipeline meant also moving Disney’s Hyperion Renderer to work in the new production pipeline.
To me, one of the biggest advantages of an in-house production renderer is the ability for the renderer development team to work extremely closely with other teams in the studio in an integrated fashion, and moving Hyperion to work well in the new USD-based world exemplifies just how important this collaboration is.
We couldn’t have pulled off this effort without the huge amount of amazing work that engineers and TDs and artists from many other departments pitched in.
However, having to move an existing renderer to a new pipeline isn’t the only impact on rendering that the new USD-based world has had.
One of the most exciting things about the new pipeline is all of the new possibilities and capabilities that USD and Hydra unlocks; one of the biggest projects our rendering team worked on during &lt;em&gt;Encanto&lt;/em&gt;’s production was a new, very exciting next-generation rendering project.
I can’t talk too much about this project yet; all I can say is that we see it as a major step towards the future of rendering at Disney Animation, and that even in its initial deployment on &lt;em&gt;Encanto&lt;/em&gt;, we’ve already seen huge fundamental improvements to how our lighters work every day.
Hopefully we’ll be able to reveal more soon!&lt;/p&gt;

&lt;p&gt;Of course, just because &lt;em&gt;Encanto&lt;/em&gt; saw huge foundational changes to how we make movies doesn’t mean that there weren’t the usual fun and interesting show-specific challenges as well.
&lt;em&gt;Encanto&lt;/em&gt; presented many new, weird, fun problems for the rendering team to think about.
Geometry fracturing was a major effect used extensively throughout &lt;em&gt;Encanto&lt;/em&gt;, and in order to author and render fractured geometry as efficiently as possible, the rendering team had to devise some really clever new geometry-processing features in Hyperion &lt;a href="https://doi.org/10.1145/3532836.3536262"&gt;[Burley and Rodriguez 2021]&lt;/a&gt;.
&lt;em&gt;Encanto&lt;/em&gt;’s cinematography direction called for a beautiful, really colorful look &lt;a href="https://doi.org/10.1145/3512749.3520432"&gt;[Robinson 2022]&lt;/a&gt; that required pushing artistic controllability in our lighting capabilities even further, and to that end our team developed a bunch of cool new artistic control enhancements in Hyperion’s volume rendering and light shaping systems.
One of my favorite show-specific challenges that I got to work on for &lt;em&gt;Encanto&lt;/em&gt; was for the holographic effect in Bruno’s emerald crystal prophecies &lt;a href="https://doi.org/10.1145/3532836.3536269"&gt;[Butler et al. 2021]&lt;/a&gt;.
For a variety of reasons, the artists wanted this effect done completely in-render; coming up with an in-render solution required many iterations and prototypes and experiments carried out over several months through a close collaboration between a number of artists and TDs and the rendering team.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Encanto&lt;/em&gt; also saw continued advancements to Hyperion’s state-of-the-art deep-learning denoiser and stereo rendering solutions and saw continued advancements in Hyperion’s shading models and traversal system.
A particularly notable advancement in our shading model is the addition of a new physically accurate practical multiple-scattering sheen lobe &lt;a href="https://doi.org/10.1145/3532836.3536240"&gt;[Zeltner et al. 2022]&lt;/a&gt; to the Disney BSDF &lt;a href="https://doi.org/10.1145/2776880.2787670"&gt;[Burley 2015]&lt;/a&gt;; I think this new sheen model is going to catch on widely in industry due to its combination of accuracy, ease of implementation, and performance, all of which improves greatly over previously existing sheen models &lt;a href="https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_sheen.pdf"&gt;[Conty and Kulla 2017]&lt;/a&gt;.
These advancements helped us tackle many of the interesting complexity and scaling challenges that &lt;em&gt;Encanto&lt;/em&gt; presented; effects like Isabella’s flowers and the glowing magical particles associated with the Madrigal family’s miracle pushed instancing counts to incredible new record levels &lt;a href="https://doi.org/10.1145/3532836.3536254"&gt;[Finley et al. 2022]&lt;/a&gt;, and for the first time ever on a Disney Animation film, we actually rendered some of the gorgeous costumes in the movie not as displaced triangle meshes with fuzz on top, but as &lt;em&gt;actual woven curves at the thread-level&lt;/em&gt; &lt;a href="https://doi.org/10.1145/3532836.3536269"&gt;[Velasquez et al. 2022]&lt;/a&gt;.
The latter proved crucial to creating the chiffon and tulle in Isabella’s outfit and was a huge part in creating the look of Mirabel’s characteristic custom-embroidered skirt.
My mind was thoroughly blown when I saw those renders for the first time; on every film, I’m constantly amazed and impressed by what our artists can do with the tools we provide them with.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Encanto&lt;/em&gt; also saw rendering features that we first developed for previous films pushed even further and used in interesting new ways.
We first deployed a path guiding implementation &lt;a href="https://doi.org/10.1111/cgf.13227"&gt;[Müller et al. 2017]&lt;/a&gt; in Hyperion back on &lt;em&gt;Frozen 2&lt;/em&gt; &lt;a href="https://cgg.mff.cuni.cz/~jaroslav/papers/2019-path-guiding-course/index.htm"&gt;[Müller 2019]&lt;/a&gt;, but path guiding wound up not seeing too much use on &lt;em&gt;Raya and the Last Dragon&lt;/em&gt; since &lt;em&gt;Raya&lt;/em&gt;’s setting was mostly outdoors, and path guiding doesn’t help as much in direct-lighting dominant scenarios such as outdoor scenes.
However, since a huge part of &lt;em&gt;Encanto&lt;/em&gt; takes place inside of the magical Madrigal casita, indoor indirect illumination was a huge component of &lt;em&gt;Encanto&lt;/em&gt;’s lighting.
We found that path guiding provided enormous benefits to render times in many indoor scenes, and especially in settings like the Madrigal family’s kitchen at night, where lighting was almost entirely provided by outdoor light sources coming in through windows and from candles and stuff.
I think this case was a great example of how we benefit from how closely our lighting artists and our rendering engineers work together on many shows over time; because we had all worked together on similar problems before, we all had shared experiences with past solutions that we were able to draw on together to quickly arrive at a common understanding of the new challenges on &lt;em&gt;Encanto&lt;/em&gt;.
Another good example of how this collaboration continues to pay dividends over time is in the choices of lens and bokeh effects that were used on &lt;em&gt;Encanto&lt;/em&gt;.
For &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;, we learned a lot about creating non-uniform bokeh and interesting lensing effects, and what we learned on &lt;em&gt;Raya&lt;/em&gt; in turn helped further inform early cinematography and lensing experiments on &lt;em&gt;Encanto&lt;/em&gt;.
One more great example can be found in how eyes are shaded in &lt;em&gt;Encanto&lt;/em&gt;- over our last few shows, we’ve been steadily moving our eye shading approach over to a next-generation shading model with advanced, physically accurate iris caustics &lt;a href="https://doi.org/10.1145/3214745.3214751"&gt;[Chiang and Burley 2018]&lt;/a&gt; sampled using manifold next event estimation, and &lt;em&gt;Encanto&lt;/em&gt; is the first show to use this new eye shading model on 100% of characters.
The way we push technology further and further on each film isn’t limited to just rendering either; I mostly write about only lighting/shading/rendering topics here because that’s my home domain, but there are countless other examples in things like rigging, animation, simulation, procedural authoring, interactive visualization, and more about how we each film tech advances on top of the previous film.
A great example published at SIGGRAPH 2022 is the new hair simulation technique that was developed for Mirabel’s bouncy curly hair &lt;a href="https://doi.org/10.1145/3532836.3536238"&gt;[Liu 2022]&lt;/a&gt;; ever since &lt;em&gt;Tangled&lt;/em&gt;, Disney Animation has been great at hair, but with each movie we still keep advancing what we can do!&lt;/p&gt;

&lt;p&gt;In addition to all of the cool renderer development work that I usually do, I also got to take part in something a little bit different on &lt;em&gt;Encanto&lt;/em&gt;.
Every year, the lighting department brings on a handful of trainees, who are put through several months of in-studio “lighting school” to learn our tools and pipeline and approach to lighting before lighting real shots on the film itself.
This year, I got to join in with the lighting trainees while they were going through lighting training; this experience wound up being one of my favorites from the past year.
I think that having to sit down and actually learn and use software the same way that the users have to is an extraordinarily valuable experience for any software engineer that is building tools for users.
Even though I’ve been working at Disney Animation for six years now, and even though I know the internals of how our renderer works extensively, I still learned a ton from having to actually use Hyperion to light shots and address notes from lighting supervisors and stuff!
&lt;em&gt;Encanto&lt;/em&gt;’s lighting style required really leaning on the tools that we have for art-directing and pushing and modifying fully physical lighting, which really changed my perspective on some of these tools.
For most rendering engineers and researchers, features that allow for breaking purely physical light transport are often seen as annoying and difficult to implement but necessary concessions to the artists.
Having now used these features in order to hit artistic notes on short time frames though, I now have a better understanding of just how critical a component these features can be in an artist’s toolbox.
I owe a huge amount of thanks to Disney Animation’s technology department leadership and to the lighting department for having made this experience possible and for having strongly supported this entire “exchange program”; I’d strongly recommend that every rendering engineer should go try lighting some shots sometime!&lt;/p&gt;

&lt;div id="2021-11-29-frames"&gt;&lt;/div&gt;

&lt;p&gt;Finally, here are some stills from the movie pulled from the Blu-ray, 100% created using Disney’s Hyperion Renderer by our amazing artists.
I’ve ordered the frames randomly, to try to prevent spoiling anything important.
These frames showcase just how gorgeous &lt;em&gt;Encanto&lt;/em&gt; looks, but they only represent a small fraction of how breathtakingly beautiful and colorful the total film is.
I highly recommend seeing &lt;em&gt;Encanto&lt;/em&gt; on the biggest screen you can; if you are a computer graphics enthusiast, go see it twice: the first time for the wonderful, magical story and the second time for the incredible artistry that went into every single shot and every single frame!
I love working on Disney Animation films because Disney Animation is a place where some of the most amazing artists and engineers in the world work together to simultaneously advance animation as a storytelling medium, as a visual medium, and as a technology goal.
Art being inspired by technology and technology being challenged by art is a legacy that is deeply baked into the very DNA of Disney Animation, and that approach is exemplified by every single frame in &lt;em&gt;Encanto&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_001.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_001.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_002.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_002.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_003.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_003.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_004.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_004.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_005.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_005.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_006.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_006.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_007.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_007.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_008.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_008.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_009.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_009.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_010.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_010.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_011.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_011.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_077.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_077.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_023.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_023.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_056.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_056.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_091.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_091.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_079.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_079.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_082.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_082.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_086.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_086.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_029.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_029.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_085.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_085.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_052.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_052.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_038.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_038.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_046.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_046.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_065.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_065.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_034.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_034.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_013.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_013.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_053.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_053.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_055.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_055.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_081.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_081.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_090.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_090.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_036.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_036.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_047.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_047.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_024.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_024.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_073.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_073.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_063.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_063.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_083.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_083.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_054.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_054.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_069.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_069.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_084.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_084.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_048.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_048.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_067.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_067.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_062.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_062.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_028.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_028.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_012.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_012.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_070.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_070.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_051.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_051.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_094.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_094.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_061.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_061.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_088.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_088.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_050.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_050.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_020.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_020.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_092.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_092.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_027.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_027.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_089.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_089.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_045.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_045.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_060.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_060.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_058.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_058.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_033.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_033.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_026.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_026.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_064.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_064.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_018.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_018.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_042.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_042.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_037.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_037.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_087.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_087.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_017.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_017.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_041.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_041.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_072.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_072.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_016.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_016.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_015.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_015.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_022.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_022.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_014.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_014.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_044.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_044.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_075.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_075.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_035.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_035.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_021.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_021.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_057.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_057.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_040.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_040.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_025.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_025.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_071.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_071.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_066.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_066.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_049.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_049.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_068.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_068.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_059.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_059.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_076.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_076.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_078.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_078.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_030.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_030.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_019.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_019.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_074.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_074.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_093.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_093.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_031.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_031.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_032.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_032.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_043.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_043.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_080.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_080.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the credits frame for Disney Animation’s rendering and visualization teams! These two teams collectively are responsible for generating all of the pixels at Disney Animation, be it final frames from Hyperion, or interactive viewports using our internal realtime rasterizer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_credits.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Nov/encanto/CASA_credits.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All images in this post are courtesy of and the property of Walt Disney Animation Studios.&lt;/p&gt;

&lt;p&gt;Also, be sure to catch our new short, &lt;em&gt;&lt;a href="/2022/03/far-from-the-tree.html"&gt;Far from the Tree&lt;/a&gt;&lt;/em&gt;, which is accompanying &lt;em&gt;Encanto&lt;/em&gt; in theaters.
&lt;em&gt;Far from the Tree&lt;/em&gt; deserves its own discussion later; all I’ll write here is that I’m sure it’s going to be fascinating for rendering and computer graphics enthusiasts to see!
&lt;em&gt;Far from the Tree&lt;/em&gt; tells the story of a parent and child raccoon as they explore a beach; the short has a beautiful hand-drawn watercolor look that is actually CG rendered out of Disney’s Hyperion Renderer and extensively augmented with hand-crafted elements.
Be sure to see &lt;em&gt;Far from the Tree&lt;/em&gt; in theaters with &lt;em&gt;Encanto&lt;/em&gt;!&lt;/p&gt;

&lt;div id="2021-11-29-references"&gt;&lt;/div&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;

&lt;p&gt;Brent Burley. 2015. &lt;a href="https://doi.org/10.1145/2776880.2787670"&gt;Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2015 Course Notes: &lt;a href="https://blog.selfshadow.com/publications/s2015-shading-course"&gt;Physically Based Shading in Theory and Practice&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Brent Burley and Francisco Rodriguez. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536262"&gt;Fracture-Aware Tessellation of Subdivision Surfaces&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 10.&lt;/p&gt;

&lt;p&gt;Corey Butler, Brent Burley, Wei-Feng Wayne Huang, Yining Karl Li, and Benjamin Huang. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536269"&gt;“Encanto” - Let’s Talk About Bruno’s Visions&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 8.&lt;/p&gt;

&lt;p&gt;Matt Jen-Yuan Chiang and Brent Burley. 2018. &lt;a href="https://doi.org/10.1145/3214745.3214751"&gt;Plausible Iris Caustics and Limbal Arc Rendering&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2018 Talks&lt;/em&gt;. Article 15.&lt;/p&gt;

&lt;p&gt;Alejandro Conty and Christopher Kulla. 2017. &lt;a href="https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_sheen.pdf"&gt;Production Friendly Microfacet Sheen BRDF&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2017 Course Notes: &lt;a href="https://blog.selfshadow.com/publications/s2017-shading-course"&gt;Physically Based Shading in Theory and Practice&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Henrik Dahlberg, David Adler, and Jeremy Newlin. 2019. &lt;a href="https://doi.org/10.1145/3306307.3328150"&gt;Machine-Learning Denoising in Feature Film Production&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2019 Talks&lt;/em&gt;. Article 21.&lt;/p&gt;

&lt;p&gt;Andrew Finley, Jesse Erickson, Peter De Mund, and Ying Liu. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536254"&gt;Modeling Animated Jumbo Floral Display on Disney’s “Encanto”&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 43.&lt;/p&gt;

&lt;p&gt;Haixiang Liu. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536238"&gt;Gravity Preloading for Maintaining Hair Shape Using the Simulator as a Closed-box Function&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 40.&lt;/p&gt;

&lt;p&gt;Tad Miller, Harmony M. Li, Neelima Karanam, Nadim Sinno, and Todd Scopio. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536236"&gt;Making Encanto with USD: Rebuilding a Production Pipeline Working from Home&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 12.&lt;/p&gt;

&lt;p&gt;Thomas Müller. 2019. &lt;a href="https://cgg.mff.cuni.cz/~jaroslav/papers/2019-path-guiding-course/index.htm"&gt;Practical Path Guiding in Production&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2019 Course Notes: &lt;a href="https://cgg.mff.cuni.cz/~jaroslav/papers/2019-path-guiding-course/index.htm"&gt;Path Guiding in Production&lt;/a&gt;&lt;/em&gt;. 37-50.&lt;/p&gt;

&lt;p&gt;Thomas Müller, Markus Gross, and Jan Novák. 2017. &lt;a href="https://doi.org/10.1111/cgf.13227"&gt;Practical Path Guiding for Efficient Light-Transport Simulation&lt;/a&gt;. &lt;em&gt;Computer Graphics Forum (Proc. of Eurographics Symposium on Rendering)&lt;/em&gt; 36, 4 (Jun. 2017), 91-100.&lt;/p&gt;

&lt;p&gt;Michelle Robinson, Michael Woodside, Daniel Rice, Tad Miller, Scott Kersavage, and Tyler Kupferer. 2022. &lt;a href="https://doi.org/10.1145/3512749.3520432"&gt;We Don’t Talk About Bruno - An Encanto Musical Sequence Unveiled&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Production Sessions&lt;/em&gt;. Article 2.&lt;/p&gt;

&lt;p&gt;Justin Tennant, Mitch Counsell, Far Jangtrakool, Salina Ortega, Rajesh Sharma, Tad Miller, and Scott Kersavage. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536242"&gt;Visualizing the Production Process of “Encanto” with the Command Center&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 11.&lt;/p&gt;

&lt;p&gt;Jose Velasquez, Alexander Alvarado, Ying Liu, and Maryann Simmons. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536269"&gt;Embroidery and Cloth Fiber Workflows on Disney’s “Encanto”&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 22.&lt;/p&gt;

&lt;p&gt;Tizian Zeltner, Brent Burley, and Matt Jen-Yuan Chiang. 2022. &lt;a href="https://doi.org/10.1145/3532836.3536240"&gt;Practical Multiple-Scattering Sheen Using Linearly Transformed Cosines&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2022 Talks&lt;/em&gt;. Article 7.&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Mon, 29 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/11/encanto.html</guid></item><item><title>“It’s not peaches and cream either for men”</title><link>https://avikdas.com/2021/11/29/its-not-peaches-and-cream-either-for-men.html</link><description>I spend a lot of time talking about men’s mental health because it’s what I, as a man, know about. And like with everything, the truth is complicated. We live in a patriarchal society that privileges men in certain ways, but also hurts them in other ways. The harmful effects on our heavily gendered society especially show themselves when racial or class oppression enter the picture. But ultimately, I have to listen to the experiences of others to piece together this complicated web of good and bad. So, when I came across Crossing the Divide, four accounts of transgender men who have experienced life being treated as women and now living as men, I was fascinated.</description><author>Avik Das</author><pubDate>Mon, 29 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://avikdas.com/2021/11/29/its-not-peaches-and-cream-either-for-men.html</guid></item><item><title>Today I start a brand new adventure!</title><link>https://swaits.com/starting-a-new-adventure/</link><description>&lt;p&gt;I normally ignore recruiter calls. But when I saw an email with the subject
"Distinguished Engineer at Zwift", I instantly knew I had to look into it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Welcome kit from Zwift!" src="https://swaits.com/starting-a-new-adventure/zwift-first-day-swag.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;I first knew of &lt;a href="https://zwift.com/" rel="external"&gt;Zwift&lt;/a&gt; in early 2016. I tried it, but didn't stick with it. I
was already deep into TrainerRoad and happy with it, and Zwift was still early
days and not nearly as polished as it is today.&lt;/p&gt;
&lt;p&gt;I was really into cycling those days. I worked my way up on the road from
Category 5 to 3, and raced just about every chance I could anywhere in San
Diego or even across SoCal and sometimes Arizona. Eventually, I gave up racing
and dialed my cycling way back. But, I was still aware of Zwift. When the
pandemic hit, all of my cycling buddies instantly moved over to Zwift. And as
things opened up and in-person group riding became okay again, many of them
stuck with Zwift. And though I'm not as active a cyclist as I once was, I'm
still passionate about it. &lt;em&gt;Zwift gives me an opportunity to combine my work
with my passion for cycling.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Before Amazon, I spent 19 years making video games professionally. I chose to
leave the industry in 2015 and never regretted it. But, I did miss some of the
interesting technical challenges. Zwift is basically an MMO video game where
the controller is a connected smart trainer. &lt;em&gt;Working at Zwift gets me closer
to the most interesting technical challenges I know.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I spent the past five years at Amazon learning, growing, and practicing my
skills at broad leadership as a Tech IC. I've worked across hundreds of SDEs on
everything from the tactical minutiae to the strategic tech decisions in very
large businesses. &lt;em&gt;As a Distinguished Engineer at Zwift, I hope to apply what
I've been refining and practicing as a PE at Amazon the past five years.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It's the combination of these three things, a passion for cycling and indoor
fitness, getting closer again to really interesting technical challenges like I
worked on in games, and landing in a role where I can influence a large tech
organization for the better, that makes me extremely excited about this new
job!&lt;/p&gt;</description><author>swaits.com</author><pubDate>Mon, 29 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://swaits.com/starting-a-new-adventure/</guid></item><item><title>Climb your own status Everest</title><link>http://dimitarsimeonov.com/2021/11/28/climb-your-own-status-everest</link><description>&lt;blockquote&gt;
  &lt;p&gt;The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood; who strives valiantly; … and who at the worst, if he fails, at least fails while daring greatly, so that his place shall never be with those cold and timid souls who neither know victory nor defeat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Teddy Roosevelt
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Welcome to part 6 of the series on “How to be a game changer”. You can find the previous posts here &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;1&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;2&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer"&gt;3&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources"&gt;4&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/21/exiting-artificial-scarcity"&gt;5&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Driving a new Land Rover is a flex in most parts of the world. By having, and showing a luxury car to others, its owner signals that they are affluent enough to be able to afford it. It can be a &lt;a href="https://en.wikipedia.org/wiki/Veblen_good"&gt;Veblen good&lt;/a&gt; which separates its owner from the rest, and increases their status. Until it doesn’t. I was fortunate enough to go on a vacation recently in Los Angeles, and booked a modest studio in a nice neighborhood. There I saw the highest concentration of Land Rovers and Range Rovers in my life. One out of every 5 houses seemed to have this status symbol parked in front of it. Which made me think - is it still a status symbol for these neighbors? Or does it merely become a luxury trinket? How many people can have Land Rovers and still claim the status boost? In the luxury car dimension, there are only a limited number of people who can flex. This makes fighting for the status of the owner of the nicest car in the neighborhood a game with limited resources. And you already know my &lt;a href="https://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources"&gt;opinion&lt;/a&gt;, that we should want to opt out of such games.&lt;/p&gt;

&lt;p&gt;However, fighting for status seem similar to fighting over a limited resource, as status may seem limited, but it doesn’t have to be. Status is more abundant than we think. Let’s look one of the most clear cases of status - winning an olympics gold medal, and let’s consider running. It seems like a zero sum - there is a winner, and losers, and only one winner at a time. At least on the surface, the winner gets the majority of the status, while everyone else gets the much lesser status of being a finalist. The other runners are not really worse-off than they would’ve been if they didn’t participate in the running game. They are still getting paid, and they gain fame. Their loss is only relative, and running is a win-win game. The amount of fame and happiness from a single race might seem limited but it is not - it varies with the quality of the performance. For example at the 400m hurdles race at the 2021 Olympics, Karsten Warholm destroyed his previous world record of 46.70s by running it in 45.94s. This is 760 milliseconds faster, three quarters of a second. Beating a world record by so much is wild! But what made the race even more amazing, and Warholm’s win even more impressive is that his main challenger Rai Benjamin also destroyed the previous world record by running 46.17. In order for Warholm to win, he had no choice but to break the world record by a lot. The top two runners each got more glory, fame and status by pushing each other. They both become heroes worthy of praise and both win status, as runners who broke the world record, and creating an unforgettable experience for everyone watching.&lt;/p&gt;

&lt;p&gt;&lt;img alt="size and color of medals" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/06medals.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Let’s now look at a different type of race - the race to be the first person to make a scientific contribution, and the first company to get a certain innovation to the market. Someone might say, with a dose of cynicism, that the scientists in the forefront are engaging in a zero-sum race with each other and that this scientific discovery was going to get made anyway. Yet it will be a boon for society if that discovery happens 10% faster and brought to market 10% faster. New discoveries build up on top of old and over ten years, this gradual speedup will amount to a whole year of advancement. Over 60 years, that’s whole 6 years of advancement. For healthcare, that may be the difference between having the treatment to a cancer or Alzheimer disease before or after someone you love catches it. Even if we look at a much shorter time frame - the race to create and deploy a COVID vaccine saved millions of lives. The outcome of any seemingly trivial science and innovation race can be a matter of life and death eventually. In the meantime, the race resets every time there’s a new discovery, all the scientists can build up on top of it, and win status. The faster the discoveries happen, the faster new status is “mined”. Some scientists become giants, and that allows other to stand on their shoulders.&lt;/p&gt;

&lt;p&gt;In a given dimension, there may be only so much status - one best runner/scientist, one employee promoted to the next level, one president elected. But if we look for more dimensions, we realize that there are more untapped opportunities for gaining status. For example, if two employees are fighting for the same promotion, they might instead build up their areas of ownership to the level that they are providing so much value, and they both get promoted to expand their work. Or, if people want the clout associated with being a president, they can instead focus on achieving what a president would achieve and improving other social and environmental issues such as poverty, pollution, healthcare, etc. If they have a major success there, they might be remembered by the future generations even more than whomever was the president at the time. Martin Luther King Jr was not a president, but has a more important place in history than LBJ, who was president at a time, and even has an official holiday dedicated to him.&lt;/p&gt;

&lt;p&gt;&lt;img alt="climb your own everest" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/06everest.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;In my opinion, the highest status is achievable by each of us, simultaneously, and without detracting from anyone else’s status. That’s the status and confidence of being in internal harmony, and in harmony with Nature. In such state, my internal angels and demons will be dancing together instead of fighting, and my being will be dancing with others instead of fighting them. This is my goal in life, and I wouldn’t feel any less accomplished if others also achieve it - in fact it will make me happier. We are all different, and each of us has a different Everest to climb.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Mon, 29 Nov 2021 01:51:19 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/11/28/climb-your-own-status-everest</guid></item><item><title>The physical Sackur-Tetrode entropy of an ideal gas</title><link>https://bytepawn.com/the-physical-sackur-tetrode-entropy-of-an-ideal-gas.html</link><description>&lt;p&gt;I derive the Sackur-Tetrode equation for entropy of a monatomic ideal gas. &lt;br /&gt;&lt;br /&gt;&lt;img alt="." src="/images/sackur.gif" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Mon, 29 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/the-physical-sackur-tetrode-entropy-of-an-ideal-gas.html</guid></item><item><title>Book Review: Endurance, Shackleton's Incredible Voyage</title><link>https://nicolaiarocci.com/book-review-endurance-shackletons-incredible-voyage/</link><description>&lt;p&gt;Of all the stories of maritime adventures I&amp;rsquo;ve read, that of the Endurance,
masterfully told by Alfred Lansing in this book, is the most incredible and
shocking. Unbelievable to say, given the premise (a crew of 28 men stranded on
the Antarctic pack, camped on floating slabs of ice hundreds of miles from any
human settlement, at the gates of the Antarctic winter), but the story does not
end in tragedy. The original expedition failed, but what followed the capture
of Endurance (never was the name of a ship more prophetic) by the Antarctic ice
borders on the impossible, if not the miraculous.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 28 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-endurance-shackletons-incredible-voyage/</guid></item><item><title>Brief thoughts on crypto</title><link>https://utf9k.net/blog/brief-thoughts-on-crypto/</link><description>It really is what it says on the tin: I have some thoughts about crypto</description><author>utf9k</author><pubDate>Sun, 28 Nov 2021 04:04:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/brief-thoughts-on-crypto/</guid></item><item><title>Emacs bends again</title><link>https://xenodium.com/emacs-bends-again</link><description>&lt;p&gt;While adding more rendering capabilities to &lt;a href="https://plainorg.com"&gt;Plain Org&lt;/a&gt;, it soon became apparent some sort of screenshot/snapshot testing was necessary to prevent regressing existing features. That is, we first generate a rendered snapshot from a given org snippet, followed by some visual inspection, right before we go and save the blessed snapshot (often referred to as golden) to our project. Future changes are validated against the golden snapshot to ensure rendering is still behaving as expected.&lt;/p&gt;
&lt;p&gt;Let's say we'd like to validate table rendering with links, we can write a test as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func testTableWithLinks() throws {
  assertSnapshot(
    matching: OrgMarkupText.make(
      &amp;quot;&amp;quot;&amp;quot;
      | URL                    | Org link    |
      |------------------------+-------------|
      | https://flathabits.com | [[https://flathabits.com][Flat Habits]] |
      | Regular text           | Here too    |
      |------------------------+-------------|
      &amp;quot;&amp;quot;&amp;quot;),
    as: .image(layout: .sizeThatFits))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The corresponding snapshot golden can be seen below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-bends-again/testTableWithLinks.1.png" /&gt;&lt;/p&gt;
&lt;p&gt;This is all done rather effortlessly thanks to &lt;a href="https://twitter.com/pointfreeco"&gt;Point Free&lt;/a&gt;'s wonderful &lt;a href="https://github.com/pointfreeco/swift-snapshot-testing"&gt;swift-snapshot-testing&lt;/a&gt; utilities.&lt;/p&gt;
&lt;p&gt;So what does any of this have to do with Emacs? You see, as I added more snapshot tests and made modifications to the rendering logic, I needed a quick way to visually inspect and override all goldens. All the main pieces were already there, I just needed some elisp glue to &lt;em&gt;bend Emacs my way™.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;First, I needed to run my Xcode builds from the command line. This is already &lt;a href="https://developer.apple.com/library/archive/technotes/tn2339/_index.html"&gt;supported via xcodebuild&lt;/a&gt;. Next, I needed a way to parse test execution data to extract failing tests. &lt;a href="https://twitter.com/davidahouse"&gt;David House&lt;/a&gt;'s &lt;a href="https://github.com/davidahouse/xcodebuild-to-json"&gt;xcodebuild-to-json&lt;/a&gt; handles this perfectly. What's left? Glue it all up with some elisp.&lt;/p&gt;
&lt;p&gt;Beware, the following code snippet is packed with assumptions about my project, it's messy, surely has bugs, can be optimized, etc. But the important point here is that Emacs is such an amazing malleable power tool. Throw some elisp at it and you can to bend it to your liking. After all, it's [your]{.underline} editor.&lt;/p&gt;
&lt;p&gt;And so here we are, I can now run snapshot tests from Emacs using my hacked up &lt;code&gt;plainorg-snapshot-test-all&lt;/code&gt; function and quickly override (or ignore) all newly generated snapshots by merely pressing y/n keys. Oh, and our beloved web browser was also invited to the party. Press &amp;quot;d&amp;quot; to open two browser tabs if you'd like to take a closer look (not demoed below).&lt;/p&gt;
&lt;p&gt;Success. &lt;em&gt;Emacs bends again&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-bends-again/diff.gif" /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;;;; -*- lexical-binding: t; -*-

(defun plainorg-snapshot-test-all ()
  &amp;quot;Invoke xcodebuild, compare failed tests screenshots side-to-side,
and offer to override them.&amp;quot;
  (interactive)
  (let* ((project (cdr (project-current)))
         (json-tmp-file (make-temp-file &amp;quot;PlainOrg_Tests_&amp;quot; nil &amp;quot;.json&amp;quot;))
         (default-directory project))
    (unless (file-exists-p (concat project &amp;quot;PlainOrg.xcodeproj&amp;quot;))
      (user-error &amp;quot;Not in PlainOrg project&amp;quot;))
    (set-process-sentinel
     (start-process
      &amp;quot;xcodebuild&amp;quot;
      (with-current-buffer
          (get-buffer-create &amp;quot;*xcodebuild*&amp;quot;)
        (let ((inhibit-read-only t))
          (erase-buffer))
        (current-buffer))
      &amp;quot;/usr/bin/xcodebuild&amp;quot;
      &amp;quot;-scheme&amp;quot; &amp;quot;PlainOrg&amp;quot; &amp;quot;-target&amp;quot; &amp;quot;PlainOrgTests&amp;quot; &amp;quot;-destination&amp;quot; &amp;quot;name=iPhone 13&amp;quot; &amp;quot;-quiet&amp;quot; &amp;quot;test&amp;quot;)
     (lambda (p e)
       (with-current-buffer (get-buffer &amp;quot;*xcodebuild*&amp;quot;)
         (let ((inhibit-read-only t))
           (insert (format &amp;quot;xcodebuild exit code: %d\n\n&amp;quot; (process-exit-status p)))))
       (when (not (eq 0 (process-exit-status p)))
         (set-process-sentinel
          (start-process
           &amp;quot;xcodebuild-to-json&amp;quot;
           &amp;quot;*xcodebuild*&amp;quot;
           &amp;quot;/opt/homebrew/bin/xcodebuild-to-json&amp;quot;
           &amp;quot;--derived-data-folder&amp;quot; (format &amp;quot;/Users/%s/Library/Developer/Xcode/DerivedData/&amp;quot;
                                           (user-login-name)) &amp;quot;--output&amp;quot; json-tmp-file)
          (lambda (p e)
            (with-current-buffer (get-buffer &amp;quot;*xcodebuild*&amp;quot;)
              (let ((inhibit-read-only t))
                (insert (format &amp;quot;xcodebuild-to-json exit code: %d\n\n&amp;quot; (process-exit-status p)))))
            (when (= 0 (process-exit-status p))
              (with-current-buffer (get-buffer &amp;quot;*xcodebuild*&amp;quot;)
                (let ((inhibit-read-only t))
                  (insert &amp;quot;Screenshot comparison started\n\n&amp;quot;)))
              (plainorg--snapshot-process-json (get-buffer &amp;quot;*xcodebuild*&amp;quot;) json-tmp-file)
              (with-current-buffer (get-buffer &amp;quot;*xcodebuild*&amp;quot;)
                (let ((inhibit-read-only t))
                  (insert &amp;quot;\nScreenshot comparison finished\n&amp;quot;))
                (read-only-mode +1))))))))
    (switch-to-buffer-other-window &amp;quot;*xcodebuild*&amp;quot;)))

(defun plainorg--snapshot-process-json (result-buffer json)
  &amp;quot;Find all failed snapshot tests in JSON and offer to override
 screenshots, comparing them side to side.&amp;quot;
  (let ((hashtable (with-current-buffer (get-buffer-create &amp;quot;*build json*&amp;quot;)
                     (erase-buffer)
                     (insert-file-contents json)
                     (json-parse-buffer))))
    (mapc
     (lambda (item)
       (when (equal (gethash &amp;quot;id&amp;quot; item)
                    &amp;quot;SnapshotTests&amp;quot;)
         (mapc
          (lambda (testCase)
            (when (and (gethash &amp;quot;failureMessage&amp;quot; testCase)
                       (string-match-p &amp;quot;Snapshot does not match reference&amp;quot;
                                       (gethash &amp;quot;failureMessage&amp;quot; testCase)))
              (let* ((paths (plainorg--snapshot-screenshot-paths
                             (gethash &amp;quot;failureMessage&amp;quot; testCase)))
                     (override-result (plainorg--snapshot-override-image
                                       &amp;quot;Expected screenshot&amp;quot;
                                       (nth 0 paths) ;; old
                                       &amp;quot;Actual screenshot&amp;quot;
                                       (nth 1 paths) ;; new
                                       (nth 0 paths))))
                (when override-result
                  (with-current-buffer result-buffer
                    (let ((inhibit-read-only t))
                      (insert override-result)
                      (insert &amp;quot;\n&amp;quot;)))))))
          (gethash &amp;quot;testCases&amp;quot; item))))
     (gethash &amp;quot;classes&amp;quot; (gethash &amp;quot;details&amp;quot; hashtable)))))

(defun plainorg--snapshot-screenshot-paths (failure-message)
  &amp;quot;Extract a paths list from FAILURE-MESSAGE of the form:

failed - Snapshot does not match reference.

@−
\&amp;quot;/path/to/expected/screenshot.1.png\&amp;quot;
@+
\&amp;quot;/path/to/actual/screenshot.1.png\&amp;quot;

Newly-taken snapshot does not match reference.
&amp;quot;
  (mapcar
   (lambda (line)
     (string-remove-suffix &amp;quot;\&amp;quot;&amp;quot;
                           (string-remove-prefix &amp;quot;\&amp;quot;&amp;quot; line)))
   (seq-filter
    (lambda (line)
      (string-prefix-p &amp;quot;\&amp;quot;&amp;quot; line))
    (split-string failure-message &amp;quot;\n&amp;quot;))))

(defun plainorg--snapshot-override-image (old-buffer old new-buffer new destination)
  (let ((window-configuration (current-window-configuration))
        (action)
        (result))
    (unwind-protect
        (progn
          (delete-other-windows)
          (split-window-horizontally)
          (switch-to-buffer (with-current-buffer (get-buffer-create old-buffer)
                              (let ((inhibit-read-only t))
                                (erase-buffer))
                              (insert-file-contents old)
                              (image-mode)
                              (current-buffer)))
          (switch-to-buffer-other-window (with-current-buffer (get-buffer-create new-buffer)
                                           (let ((inhibit-read-only t))
                                             (erase-buffer))
                                           (insert-file-contents new)
                                           (image-mode)
                                           (current-buffer)))
          (while (null result)
            (setq action (read-char-choice (format &amp;quot;Override %s? (y)es (n)o (d)iff in browser? &amp;quot;
                                                   (file-name-base old))
                                           '(?y ?n ?d ?q)))
            (cond ((eq action ?n)
                   (setq result
                         (format &amp;quot;Keeping old %s&amp;quot; (file-name-base old))))
                  ((eq action ?y)
                   (copy-file new old t)
                   (setq result
                         (format &amp;quot;Overriding old %s&amp;quot; (file-name-base old))))
                  ((eq action ?d)
                   (shell-command (format &amp;quot;open -a Firefox %s --args --new-tab&amp;quot; old))
                   (shell-command (format &amp;quot;open -a Firefox %s --args --new-tab&amp;quot; new)))
                  ((eq action ?q)
                   (set-window-configuration window-configuration)
                   (setq result (format &amp;quot;Quit %s&amp;quot; (file-name-base old)))))))
      (set-window-configuration window-configuration)
      (kill-buffer old-buffer)
      (kill-buffer new-buffer))
    result))
&lt;/code&gt;&lt;/pre&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 28 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/emacs-bends-again</guid></item><item><title>How to Receive Audio Messages from Your Users</title><link>https://www.tderflinger.com/en/easily-receive-audio-messages-from-users</link><description>Everybody knows the contact forms on website where users can send text message to the website owner. But how could you design a contact form that allows the recording of an audio message? In this article I want to show you how to do that.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Sun, 28 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/easily-receive-audio-messages-from-users</guid></item><item><title>First Post</title><link>https://specbranch.com/posts/first-post/</link><description>&lt;p&gt;Hello everyone, and welcome to my blog.&lt;/p&gt;
&lt;p&gt;I am an ex-Google senior engineer focused on systems programming and performance optimization. My
past experience includes hardware engineering, numerical analysis, and high-frequency trading.&lt;/p&gt;
&lt;p&gt;Here, we will be talking about software engineering, performance, computer systems and foundations,
interesting math concepts, electrical engineering, hardware acceleration, FPGAs, and more. We may
also branch out to non-technical topics including companies, organizational psychology, and the
stock market.&lt;/p&gt;</description><author>Speculative Branches</author><pubDate>Sat, 27 Nov 2021 20:28:52 GMT</pubDate><guid isPermaLink="true">https://specbranch.com/posts/first-post/</guid></item><item><title>Your Smart Car may spy on your smartphone</title><link>https://stop.zona-m.net/2021/11/your-smart-car-may-spy-on-your-smartphone/</link><description>&lt;p&gt;But how can it be smart, if it spies?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 27 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/your-smart-car-may-spy-on-your-smartphone/</guid></item><item><title>We Should All Be Feminists</title><link>https://apurva-shukla.me/bookshelf/we-should-all-be-feminists/</link><description>⭐ ⭐ ⭐ ⭐ ⭐ Great message!

My key take aways are that the lived experiences of people are real and valid - one cannot dismiss them and try…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Sat, 27 Nov 2021 14:29:48 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/bookshelf/we-should-all-be-feminists/</guid></item><item><title>Local Data Collection and Command Distribution</title><link>https://blog.herlein.com/post/local-data-collection/</link><description>&lt;p&gt;I&amp;rsquo;m revisiting why I don&amp;rsquo;t like MQTT, and what I am doing about it.&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Sat, 27 Nov 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/local-data-collection/</guid></item><item><title>I'm a Moka guy</title><link>https://nicolaiarocci.com/im-a-moka-guy/</link><description>&lt;p&gt;I&amp;rsquo;m a Moka guy, always have been. Admittedly, I also like so-called American
coffee and, of course, espresso. But every day at my place, I&amp;rsquo;ll have
a Moka-brewed coffee. Twice. As I wake up, and then in the afternoon before
getting back to work. I&amp;rsquo;ve been observing the pods frenzy spreading all around
me with curiosity and bewilderment in recent years, with dedicated retailers
opening (and often closing soon after) everywhere in my town. Bialetti&amp;rsquo;s Moka
coffee pots have always been around my life. Not disposable, for they are
pretty expensive even here in Italy, but common, everyday kitchen gadgets?
Absolutely.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 27 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/im-a-moka-guy/</guid></item><item><title>M5 Paper e-ink Calculator</title><link>https://blog.cetinich.net/content/2021/m5-ink-calculator/</link><description>I added an M5
Paper
to my collection of dev boards and decided to make a basic calculator to
teach my son to count. Since he is only a few months old he mostly uses
it as an expensive teething device.</description><author>Brent's Blog</author><pubDate>Sat, 27 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.cetinich.net/content/2021/m5-ink-calculator/</guid></item><item><title>Limits of files you can store in a directory</title><link>https://theblackcat102.github.io/what-limits-you-from-storing-inifinite-files/</link><description>04/03/2022 Update: Instead of using ext4, you should simply consider other file format such as xfs, zfs, btrfs for high file counts storage</description><author>theblackcat102</author><pubDate>Sat, 27 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://theblackcat102.github.io/what-limits-you-from-storing-inifinite-files/</guid></item><item><title>If Trees Could Talk</title><link>https://godsip.club/articles/if-trees-could-talk/</link><description>&lt;h1 id="it-came-from-the-north"&gt;It Came from The North &lt;a class="anchor" href="#it-came-from-the-north"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;More than one thousand years ago, Vikings ruled the North Sea. They weren’t mainly sailors, but they had a great knowledge about building ships and taking long journeys. Legend says that, while travelling to colonise the new Icelandic land, wooden pillars were brought aboard the long ships. When the coast was on sight, the great poles were thrown overboard; wherever they had touched the shoreline, new settlements would be built. In their villages, those posts were put in the center of the farmhouses, with other two lower ones on the sides, because they were believed to have a &lt;em&gt;sacred power&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Norse people had a special connection with woods and trees – and they weren’t the only ones. First of all, they hadn’t actual temples, so they primarily held celebrations outdoor. They had special places, or altars, named &lt;a href="https://en.wikipedia.org/wiki/H%C3%B6rgr" rel="noopener" target="_blank"&gt;hörgar&lt;/a&gt;, where the presence of the &lt;em&gt;otherworld&lt;/em&gt; was stronger. That is a general definition for “all the worlds other than Midgard”, where all the supernatural beings live. In these revered sites, during particular moments –as the start of their year, at the end of the modern October–, the boundary between the worlds was narrower, and strange events could happen.&lt;/p&gt;
&lt;p&gt;Among the pantheon of deities, Thor was undoubtedly the most famous. He was the mighty slayer of the &lt;a href="https://en.wikipedia.org/wiki/J%C3%B6tunn" rel="noopener" target="_blank"&gt;Jötnar&lt;/a&gt;, the giants which wanted to bring &lt;em&gt;chaos&lt;/em&gt; in this world. It was told that, when a lightning was seen, it was Thor hitting the target with his hammer Mjölnir; the thunder, then, was the giant crumbling to the ground. He was the god praised by common people, while the most powerful Odin was worshipped mostly by chiefs or, generally, by “major figures”. So, why was Thor so popular?&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Marvel&amp;rsquo;s Thor is totally different than the Norse god described in the myths (Marvel Studios)" src="https://godsip.club/img/thor-hemsworth.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Marvel&amp;rsquo;s Thor is totally different than the Norse god described in the myths (Marvel Studios)&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;One of the reasons could have been his &lt;strong&gt;temper&lt;/strong&gt;: almost every Norse god had his unique personality, and Thor was particularly stubborn, touchy, and wrathful. That’s pretty much the stereotype of a Viking. More than that, he was also related to important notions as fertility&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; and… trees; &lt;em&gt;oaks&lt;/em&gt;, more accurately. It may seem odd that a slaughterer of giants was associated to a shrub, but maybe it wasn’t so uncommon.&lt;/p&gt;
&lt;h1 id="let-me-stand-next-to-your-fire"&gt;Let Me Stand Next To Your Fire &lt;a class="anchor" href="#let-me-stand-next-to-your-fire"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Fun fact: what is the tree most hit by the lightning? You can guess the answer; or just &lt;del&gt;Google&lt;/del&gt; DuckDuckGo it. In the last few centuries, oaks have been observed as &lt;em&gt;especially tending to&lt;/em&gt; be hit during a thunderstorm. It probably isn’t a coincidence that this is also the tree related to the god of thunder, but the fascinating fact is that the Norse one is &lt;em&gt;one of many&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="See? Told ya! 🌳⚡️ (I didn&amp;rsquo;t use DuckDuckGo just because Google is better known)" src="https://godsip.club/img/google-oak.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;See? Told ya! 🌳⚡️ (I didn&amp;rsquo;t use DuckDuckGo just because Google is better known)&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Perkūnas&lt;/strong&gt; was the thunder god in the Baltic area, and was also related to the same tree. In the Slavic mythology, &lt;strong&gt;Perun&lt;/strong&gt; was its counterpart. The Roman Jupiter and the Greek Zeus were thunder gods, too. Of course, &lt;a href="https://en.wikipedia.org/wiki/List_of_thunder_gods" rel="noopener" target="_blank"&gt;there were many more&lt;/a&gt;, but they all had curiously similar traits. If these already seem cool coincidences… sit back and grab the popcorns.&lt;/p&gt;
&lt;p&gt;Amid cultures from Europe, that due to the same latitude had the same calendar, which depended on harvest or shepherding, it was common to light up &lt;strong&gt;large bonfires&lt;/strong&gt; in specific dates, among which:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First days of Spring&lt;/li&gt;
&lt;li&gt;Summer Solstice&lt;/li&gt;
&lt;li&gt;All Hallows’ Eve (Halloween)&lt;/li&gt;
&lt;li&gt;Winter Solstice&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, each country had its own customs, names and traditions, and obviously the days may have differed. Norse people, for instance, called the midsummer fires “Balder&amp;rsquo;s balefires” (&lt;em&gt;Balder&amp;rsquo;s Balar&lt;/em&gt;), which recalled the god’s funeral pyre. In the Highlands of Scotland, in the first day of May, “Beltane fires” were set on – probably with human sacrifices until some centuries ago. Also, they could have different meanings: Celtic people, for instance, were mostly shepherds and probably their observances were different from the ones of the farmers who lived on the Mediterranean Sea.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Bonfires always have a cozy charm&amp;hellip; but the ones described were MUCH larger than this!" src="https://godsip.club/img/bonfire.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Bonfires always have a cozy charm&amp;hellip; but the ones described were MUCH larger than this!&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;There were some notable common points between all these fires, though. All of them symbolised the power of the sun, that &lt;em&gt;dissolved&lt;/em&gt; all the negative influences and blessed the harvest and the farm animals. Interestingly, fire also was the only weapon known against &lt;em&gt;witches&lt;/em&gt;, that long ago were considered the main (or even the only) cause of illness, curses, etc. In many countries, the last sheaf of corn was called “the Old Woman” or “the Witch” and it was burned; but this is so intriguing that it is worth a further discussion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fertility&lt;/strong&gt; could have been another great mutual peculiarity: the sun had the power to make the vegetation grow, so the fire could have resembled the star’s fertilising strength. This could explain why even the farm animals were driven through the flames, sometimes hurting themselves badly.&lt;/p&gt;
&lt;h1 id="youve-been-thunderstruck"&gt;You’ve Been Thunderstruck &lt;a class="anchor" href="#youve-been-thunderstruck"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;A lot more could be said about these pyres, and surely there will be occasion, but what links them to Thor and his fellow thunder gods… are the gods themselves, in the form of oak trees. In fact, it was an ordinary tradition to burn a big log of oak in these special events. The &lt;a href="https://en.wikipedia.org/wiki/Yule_log" rel="noopener" target="_blank"&gt;Yule log&lt;/a&gt; is probably the best-known heritage from those times, but it was not the only one.&lt;/p&gt;
&lt;p&gt;The log was burned slowly: it was taken off the fire soon and kept safe for the whole year. In case of thunderstorms, it was put again on the fire. It was said to have the power to &lt;em&gt;keep the house safe from thunders&lt;/em&gt; and to protect it from &lt;em&gt;wildfires&lt;/em&gt;. Also, chunks of the burnt wood were put in the animal’s drinker to increase their fertility.&lt;/p&gt;
&lt;p&gt;To conclude this brief trip through European fires, a question: what grows on trees, very noticeable during the winter, while the plant itself seems dead? &lt;strong&gt;Mistletoe&lt;/strong&gt;. Since it is so flourishing through the winter, while the tree itself flounders, from a primitive point of view it could have been normal to associate it to the &lt;em&gt;soul&lt;/em&gt;. It contains its vitality, the life-giving power of the Sun, which will be reborn in the next Spring. And yes, it often can be found on oak trees.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="What if the mistletoe kiss tradition was born to commemorate someone? (Paul Zoetemeijer/Unsplash)" src="https://godsip.club/img/mistletoe.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;What if the mistletoe kiss tradition was born to commemorate someone? (Paul Zoetemeijer/Unsplash)&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Though it is a parasitic plant, that may be why mistletoe is widely picked up during winter, and why it was so important in Europe. It was believed to cure epilepsy because, since it was so tangled to the branches on the tree, a person who had kept it in the pocket surely wouldn’t fall with seizures. Its modern Celtic name in Brittany, Wales or Ireland still means &amp;ldquo;all-healer” because of its healing power – which is &lt;strong&gt;not&lt;/strong&gt; proven by scientific studies. Druids had specific &lt;em&gt;golden tools&lt;/em&gt; to pick the plant, and special rituals: mistletoe had not to touch the ground, otherwise it would have lost its powers, so a white cloth was used to let it fall on it.&lt;/p&gt;
&lt;p&gt;Let’s close the loop. Fire, thunder, trees. Three concepts so distant, yet identical in certain ways. A few paragraphs before, the Norse god Balder was quoted. In one version of the &lt;a href="https://norse-mythology.org/tales/the-death-of-baldur/" rel="noopener" target="_blank"&gt;myth of his death&lt;/a&gt;, he was killed by his brother Hod, who had been tricked by Loki. &lt;em&gt;Classic Loki&lt;/em&gt;. With what object has he been able to kill him, even if the god was invulnerable? With &lt;em&gt;a branch of mistletoe&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So, a god was killed by a plant, which is related to the power of the Sun, which is enclosed in an oak tree, related to a god. From another perspective, we could say that Balder was killed by its own power – or &lt;em&gt;by himself&lt;/em&gt;. An impossible paradox? Not at all, we just have to explore a little deeper our past, and maybe ourselves.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Sif, Thor’s wife, had a lush blonde hair that was affiliated to wheat. His husband was related to rain, so he had the responsibility to fertilise the crop.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Godsip Club</author><pubDate>Sat, 27 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://godsip.club/articles/if-trees-could-talk/</guid></item><item><title>Programming deals</title><link>https://learnbyexample.github.io/programming-deals/</link><description>&lt;p&gt;Hello!&lt;/p&gt;
&lt;p&gt;Here's some exciting programming deals for my own ebooks as well sales details from other creators.&lt;/p&gt;
&lt;span id="continue-reading"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;Offers for my ebooks&lt;/strong&gt; (valid till Nov 30)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learnbyexample.gumroad.com/l/py_projects/blackfriday"&gt;Practice Python Projects&lt;/a&gt; — FREE (normal price $10)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnbyexample.gumroad.com/l/python-bundle/blackfriday"&gt;Learn by example Python bundle&lt;/a&gt; — $2 (normal price $12)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnbyexample.gumroad.com/l/all-books/blackfriday"&gt;All 11 Books Bundle&lt;/a&gt; — $5 (normal price $22)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/learn_byexample/status/1463898778860068868"&gt;Giveaway contest on twitter&lt;/a&gt; — a chance to get a single ebook for free&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;Indie creators&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mathspp.gumroad.com/l/pythonbootcamp/blackfriday"&gt;Python Problem-Solving Bootcamp&lt;/a&gt; — 40% OFF today, 30% OFF tomorrow and so on (boost your Python problem-solving skills)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://driscollis.gumroad.com/"&gt;Python books by Michael Driscoll&lt;/a&gt; — $10 OFF on any book using coupon code &amp;quot;black21&amp;quot; (Python 101/201, Image/PDF/Excel processing, etc)
&lt;ul&gt;
&lt;li&gt;see also author's &lt;a href="https://www.blog.pythonlibrary.org/2021/11/24/python-black-friday-cyber-monday-sales-2021/"&gt;blog post&lt;/a&gt; for links to other Python sales&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonmorsels.com/"&gt;Python Morsels&lt;/a&gt; — 50% OFF until Nov 30 (skill-building service with short videos and hands-on bite-sized Python exercises)&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;Miscellaneous&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mailchi.mp/leanpub/monthly-sale-2021-black-friday"&gt;The Leanpub Monthly Sale for November 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.humblebundle.com/books/code-like-pro-manning-publications-books"&gt;Humble Book Bundle: Code Like a Pro by Manning Publications&lt;/a&gt; — various sale options starting from $1&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=29338976"&gt;Various programming deals discussion on Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devutils.app/pricing/"&gt;DevUtils.app&lt;/a&gt; — 30% OFF this week (Powerful developer tools for your everyday tasks, Native macOS app)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bhavaniravi.gumroad.com/l/AxFMK"&gt;How to Journal to Live your Best Life?&lt;/a&gt; — $1.99 for 30 customers, $3.99 for the next 30 customers and so on (not strictly related to programming, applicable for life events, career, etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;p&gt;Happy learning :)&lt;/p&gt;</description><author>learnbyexample</author><pubDate>Sat, 27 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/programming-deals/</guid></item><item><title>Quantum mechanics for everyone</title><link>https://diffxweyl.wordpress.com/2021/11/26/quantum-mechanics-for-everyone/</link><description>This post explains quantum mechanics (QM) without any advanced math. Unlike most introductions, I will focus on the interpretation of QM: what the objects in the theory mean and how they fit into a broader philosophy of doing physics. Specifically, I explain why the Von Neumann-Wigner interpretation, a variant of the standard Copenhagen interpretation, is &amp;#8230; &lt;a class="more-link" href="https://diffxweyl.wordpress.com/2021/11/26/quantum-mechanics-for-everyone/"&gt;Continue reading&lt;span class="screen-reader-text"&gt; "Quantum mechanics for&amp;#160;everyone"&lt;/span&gt;&lt;/a&gt;</description><author>diff × weyl</author><pubDate>Fri, 26 Nov 2021 23:46:13 GMT</pubDate><guid isPermaLink="true">https://diffxweyl.wordpress.com/2021/11/26/quantum-mechanics-for-everyone/</guid></item><item><title>Renewables are not THE solution</title><link>https://stop.zona-m.net/2021/11/renewables-are-not-the-solution/</link><description>&lt;p&gt;THE ONE solution, that is. Obvious, but never said often enough.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 26 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/renewables-are-not-the-solution/</guid></item><item><title>Red Notice</title><link>https://olshansky.info/movie/red_notice/</link><description>Olshansky's review of Red Notice</description><author>🦉 olshansky 🦁</author><pubDate>Fri, 26 Nov 2021 18:12:09 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/red_notice/</guid></item><item><title>On "Powered by AI / ML" marketing</title><link>https://nadh.in/blog/on-powered-by-ai-marketing/</link><description>&lt;p&gt;An email I had sent in response to a survey on the use of &amp;ldquo;AI / ML&amp;rdquo; and the &amp;ldquo;AI-first mindset&amp;rdquo; in our organisation and in the industry was shared on social media&lt;sup&gt;&lt;a href="https://twitter.com/Nithin0dha/status/1463764077210062853"&gt;[1]&lt;/a&gt; &lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:6869531616264495104"&gt;[2]&lt;/a&gt;&lt;/sup&gt; sparking surprising amounts of interest. I did candidly state the simple fact that we haven&amp;rsquo;t come across any big problems that warrant any specific &amp;ldquo;AI / ML&amp;rdquo; solutions in our organisation yet (&lt;a href="https://zerodha.com"&gt;Zerodha&lt;/a&gt; - stock broker that offers online investment and trading platforms), and that the bulk of the &amp;ldquo;powered by AI&amp;rdquo; claims we have seen across industries and in the numerous startup pitches that we receive, have been cases of hollow marketing. There is also a general expectation (delusion) that any sufficiently large technology company should be using &amp;ldquo;AI / ML&amp;rdquo; somehow, somewhere, for some reason.&lt;/p&gt;</description><author>Blog on Kailash Nadh / Personal homepage</author><pubDate>Fri, 26 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://nadh.in/blog/on-powered-by-ai-marketing/</guid></item><item><title>Employers should not own open-source contributions</title><link>https://www.jviotti.com/2021/11/26/employers-should-not-own-open-source-contributions.html</link><description>This article makes a case on why employers should not attempt to own the IP of the contributions of their employees</description><author>Juan Cruz Viotti</author><pubDate>Fri, 26 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jviotti.com/2021/11/26/employers-should-not-own-open-source-contributions.html</guid></item><item><title>The best Arduino projects are those that look like OLD Legos</title><link>https://stop.zona-m.net/2021/11/the-best-arduino-projects-are-those-that-look-like-old-legos/</link><description>&lt;p&gt;And I mean OLD Legos.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 25 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/the-best-arduino-projects-are-those-that-look-like-old-legos/</guid></item><item><title>Happy Thanksgiving 🦃</title><link>https://ingo-richter.io/post/2021/happy-thanksgiving/</link><description>&lt;h2 id="happy-thanksgiving"&gt;Happy Thanksgiving&lt;/h2&gt;
&lt;p&gt;I wish everybody a great day with their families and friends. I hope you can all meet in person and make up for last year.
I&amp;rsquo;m grateful for having a family and friends, a roof over my head, and a fulfilling job.&lt;/p&gt;
&lt;p&gt;Happy Turkey Day, or whatever suits your mood.
Stay safe wherever you are and get some time for yourself.&lt;/p&gt;</description><author>Ingo Richter on Today is Tomorrow's Past</author><pubDate>Thu, 25 Nov 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ingo-richter.io/post/2021/happy-thanksgiving/</guid></item><item><title>Beaglebone Black - Getting Started</title><link>https://blog.herlein.com/post/beaglebone-getting-started/</link><description>&lt;p&gt;Getting Started with the Beaglebone Black (Again)&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Thu, 25 Nov 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/beaglebone-getting-started/</guid></item><item><title>Book Review: Mathematics is politics</title><link>https://nicolaiarocci.com/book-review-mathematics-is-politics/</link><description>&lt;p&gt;Mathematics as the study of relationships: in this aspect lies the similarity
and affinity with politics. And then the need in both cases to proceed with
stubbornness and trust, without fearing error which, as in all difficult
things, is not only lying in wait but inherent, and often, when it is
discovered, it is the stimulus and engine of new successes and goals. Hence the
need to respect rules and (not or, mind you) the compelling need for
revolutions. The parallel narration of the author&amp;rsquo;s life and the change, over
the years, of her own beliefs and points of view, makes this short essay more
credible and streamlines the story.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 25 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-mathematics-is-politics/</guid></item><item><title>🙏</title><link>https://klinger.io/posts/%F0%9F%99%8F</link><description>Over the years i met several people that left quite an impact on me and in many cases helped me along the way. I tried to summariz...</description><author>Blog posts of Andreas Klinger</author><pubDate>Thu, 25 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://klinger.io/posts/%F0%9F%99%8F</guid></item><item><title>Numeric Palindrome</title><link>https://learnbyexample.github.io/numeric-palindrome/</link><description>&lt;p&gt;I posted a coding challenge in the second issue of &lt;a href="https://learnbyexample.gumroad.com/l/learnbyexample-weekly"&gt;learnbyexample weekly&lt;/a&gt;. I discuss the problem and Python/Perl solutions in this blog post.&lt;/p&gt;
&lt;span id="continue-reading"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;h2 id="problem-statement"&gt;Problem statement&lt;a class="zola-anchor" href="#problem-statement"&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Find numbers from &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;10000&lt;/code&gt; (inclusive) which reads the same in reversed form in both binary and decimal formats. For example, &lt;code&gt;33&lt;/code&gt; in decimal is &lt;code&gt;100001&lt;/code&gt; in binary and both of these are palindromic.&lt;/p&gt;
&lt;br /&gt;
&lt;h2 id="python-solution"&gt;Python solution&lt;a class="zola-anchor" href="#python-solution"&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here's one possible solution for this problem:&lt;/p&gt;
&lt;pre class="language-python " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-python"&gt;&lt;span style="color: #72ab00;"&gt;for &lt;/span&gt;&lt;span&gt;n &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;in &lt;/span&gt;&lt;span style="color: #b39f04;"&gt;range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;10001&lt;/span&gt;&lt;span&gt;):
&lt;/span&gt;&lt;span&gt;    dec_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;= &lt;/span&gt;&lt;span style="color: #668f14;"&gt;f&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'&lt;/span&gt;&lt;span&gt;{n}&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'
&lt;/span&gt;&lt;span&gt;    bin_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;= &lt;/span&gt;&lt;span style="color: #668f14;"&gt;f&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'&lt;/span&gt;&lt;span&gt;{n&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;:b&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'
&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;if &lt;/span&gt;&lt;span&gt;dec_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;== &lt;/span&gt;&lt;span&gt;dec_s[::&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;and &lt;/span&gt;&lt;span&gt;bin_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;== &lt;/span&gt;&lt;span&gt;bin_s[::&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;]:
&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span style="color: #b39f04;"&gt;print&lt;/span&gt;&lt;span&gt;(n)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extending the above solution to include more comparisons is easy with built-in features:&lt;/p&gt;
&lt;pre class="language-python " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-python"&gt;&lt;span style="color: #72ab00;"&gt;for &lt;/span&gt;&lt;span&gt;n &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;in &lt;/span&gt;&lt;span style="color: #b39f04;"&gt;range&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;10001&lt;/span&gt;&lt;span&gt;):
&lt;/span&gt;&lt;span&gt;    dec_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;= &lt;/span&gt;&lt;span style="color: #668f14;"&gt;f&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'&lt;/span&gt;&lt;span&gt;{n}&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'
&lt;/span&gt;&lt;span&gt;    bin_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;= &lt;/span&gt;&lt;span style="color: #668f14;"&gt;f&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'&lt;/span&gt;&lt;span&gt;{n&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;:b&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'
&lt;/span&gt;&lt;span&gt;    oct_s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;= &lt;/span&gt;&lt;span style="color: #668f14;"&gt;f&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'&lt;/span&gt;&lt;span&gt;{n&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;:o&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span style="color: #d07711;"&gt;'
&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;if &lt;/span&gt;&lt;span style="color: #b39f04;"&gt;all&lt;/span&gt;&lt;span&gt;(s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;== &lt;/span&gt;&lt;span&gt;s[::&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;for &lt;/span&gt;&lt;span&gt;s &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;in &lt;/span&gt;&lt;span&gt;(dec_s, bin_s, oct_s)):
&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span style="color: #b39f04;"&gt;print&lt;/span&gt;&lt;span&gt;(n)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As an exercise, extend this program further to include hexadecimal number comparison as well. Can you find out what's the first number that is greater than ten to satisfy all the four numeric formats?&lt;/p&gt;
&lt;br /&gt;
&lt;h2 id="perl-one-liner"&gt;Perl one-liner&lt;a class="zola-anchor" href="#perl-one-liner"&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here's a solution for CLI enthusiasts:&lt;/p&gt;
&lt;pre class="language-bash " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-bash"&gt;&lt;span style="color: #5597d6;"&gt;$&lt;/span&gt;&lt;span&gt; perl&lt;/span&gt;&lt;span style="color: #5597d6;"&gt; -le &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'for (1..10000) { $bn = sprintf(&amp;quot;%b&amp;quot;, $_);
&lt;/span&gt;&lt;span style="color: #d07711;"&gt;                print if ($_ eq reverse) &amp;amp;&amp;amp; ($bn eq reverse $bn) }'
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;1
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;3
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;5
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;7
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;9
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;33
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;99
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;313
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;585
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;717
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;7447
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;9009
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; See my &lt;a href="https://github.com/learnbyexample/learn_perl_oneliners"&gt;Perl One-Liners Guide&lt;/a&gt; ebook if you are interested in learning to use Perl from the command-line.&lt;/p&gt;</description><author>learnbyexample</author><pubDate>Thu, 25 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/numeric-palindrome/</guid></item><item><title>A Very Overengineered Audio Splitter Panel</title><link>http://www.nuke24.net/plog/31.html</link><description>I used up all my #6 hex nuts to make, over 3 days, a thing
			that should have been made in a couple hours using more
			conventional wiring techniques</description><author>TOGoS's Project Log</author><pubDate>Wed, 24 Nov 2021 20:13:37 GMT</pubDate><guid isPermaLink="true">http://www.nuke24.net/plog/31.html</guid></item><item><title>The big paradox of green, recyclable batteries</title><link>https://stop.zona-m.net/2021/11/the-big-paradox-of-green-recyclable-batteries/</link><description>&lt;p&gt;More exactly, of green AND recyclable batteries.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 24 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/the-big-paradox-of-green-recyclable-batteries/</guid></item><item><title>mwcleric, my Python MediaWiki client library</title><link>https://river.me/blog/mwcleric/</link><description>An overview of mwcleric</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 24 Nov 2021 08:21:26 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/mwcleric/</guid></item><item><title>Development Practices</title><link>https://boyter.org/posts/development-practices/</link><description>&lt;h1 id="development-practices"&gt;Development Practices&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Follow a development practice of developer empowerment. Encourage change and experimentation with zero or low penalty for defects allowing them to be found early and resolved with additional tests or automation.&lt;/li&gt;
&lt;li&gt;I embrace the fact that bugs and problems will be found, but by making the penalty for them exceedingly low we can ensure that they are identified and resolved quickly with little to no production impact. All processes from CI/CD and development are built around this idea and ensure that rapid changes can be made, allowing for extensible solutions, with as low a cost of development as possible while keeping quality high. This also has the added benefit of being a developer friendly approach that ensures teams remain engaged with as little buck passing as possible.&lt;/li&gt;
&lt;li&gt;The following development practices are also supported and where appropriate are implemented to ensure the quality of any solution delivered is high and that development remains as easy as possible with minimal blockers.&lt;/li&gt;
&lt;li&gt;Use of source control. Appropriate branches, with progress branches checked in allowing for rapid feedback and quick checks to ensure the wrong direction is not taken.&lt;/li&gt;
&lt;li&gt;Frequent incremental commits using source control.&lt;/li&gt;
&lt;li&gt;Use of static analysis and linting tools prior to committing to ensure that code is consistent and remains of high quality.&lt;/li&gt;
&lt;li&gt;Ability to spin up the environment locally. While difficult with serverless infrastructure with up front effort it is possible to run integrations locally to allow for faster development. This can be achieved by using shims in the code allowing for the use of the local filesystem for S3 components and things such as DynamoDB Local for local development. Docker can be used to make this easier.&lt;/li&gt;
&lt;li&gt;Give developers a sandbox AWS account for validation of code. This is to allow them to set up integrations such as SQS and develop against it locally to ensure code works, saving them having to deploy to validate, which saves on development and debugging costs.&lt;/li&gt;
&lt;li&gt;Build unit and integration tests as appropriate, with the emphasis being on delivering quality code.&lt;/li&gt;
&lt;li&gt;Use security scanners to ensure that secrets and keys are never committed to source control, and in the case they are, remove them. If using git this can be achieved using git-filter-branch or tools such as BFG.&lt;/li&gt;
&lt;li&gt;Where possible apply S.O.L.I.D development principles.&lt;/li&gt;
&lt;li&gt;Either commit vendor dependencies or have a managed place to store them in order to allow control of the version and ensure that development can continue in the case that 3rd party providers are down. This also allows for security management scanning and auditing.&lt;/li&gt;
&lt;li&gt;Everyone is responsible for the quality of the solution. This means that developers own the quality of code they write equally with everyone else on the project, ensuring the highest quality of development.&lt;/li&gt;
&lt;li&gt;Defence in depth solution design. This ensures that system failures should not degrade the performance of the application as a whole. An example would be that queues and other techniques are implemented such that when the target system recovers the system processes as normal.&lt;/li&gt;
&lt;li&gt;Event driven architecture. This applies to defence in depth systems but also ensures minimal cost, as the system scales and grows to meet the needs as required.&lt;/li&gt;
&lt;li&gt;Serverless as a preference. This lowers costs, ensures that the system is generally recoverable and self healing.&lt;/li&gt;
&lt;li&gt;Managed services as a preference. This lowers maintenance costs and ensures backups are performed on an automated basis as well as scaling.&lt;/li&gt;
&lt;li&gt;Encryption by default. Everything is encrypted at rest and in transport. No clear text communication across the stack ever. Where required encrypted memory though the use of secure string implementations can be used.&lt;/li&gt;
&lt;li&gt;Stick to AWS best practices with extensions as required.&lt;/li&gt;
&lt;li&gt;Continually improve processes to resolve issues. If something causes a problem now, fixing it with a test or other tool solves it for everyone.&lt;/li&gt;
&lt;li&gt;Traceable logging. All logs are written to be traceable by transaction, caller and to a specific location within the codebase allowing for prompt investigation where a problem is found.&lt;/li&gt;
&lt;/ul&gt;</description><author>Ben E. C. Boyter</author><pubDate>Wed, 24 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/development-practices/</guid></item><item><title>Game Wizards</title><link>https://ztoz.blog/posts/game-wizards/</link><description>&lt;p&gt;Game Wizards: The Epic Battle for Dungeons &amp;amp; Dragons
by Jon Peterson&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mitpress.mit.edu/books/game-wizards"&gt;Publisher&amp;rsquo;s Website&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Game Wizards&lt;/em&gt; is a business history spanning the creation of Dungeons &amp;amp; Dragons and TSR to the firing of Gary Gygax in 1985. The book has a special focus on the legal battles between Gary Gygax and Dave Arneson around creator and royalty rights for D&amp;amp;D, but also covers the rivalry between GenCon and Origin and the overall growth of TSR. Jon Peterson assembles the story from a broad-range of sources, including media portrayals, fanzines, interviews, legal documents, unexpected artifacts, and &lt;a href="http://playingattheworld.blogspot.com/2021/09/game-wizards-tsr-financials.html"&gt;recreations of financial statements&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Peterson lays out the pivotal and mundane operations by year. This approach allows a very deep recalling of each year&amp;rsquo;s happenings and an &amp;ldquo;uncompressing&amp;rdquo; of history. As D&amp;amp;D takes off in the early 1980s, TSR becomes profligate with spending and bad habits become entrenched. By maintaining a constant velocity through history, the narrative is less of a &amp;ldquo;bubble burst&amp;rdquo; business story and more a nuanced look at the mechanics of selling and producing the games. The book&amp;rsquo;s detail will make it a primary resource for later historians and should be of interest to game entrepreneurs for details on marketing, distribution, and rights management.&lt;/p&gt;
&lt;p&gt;The result is far-less hagiographic than &lt;em&gt;Empire of Imagination: Gary Gygax and the Birth of Dungeons &amp;amp; Dragons&lt;/em&gt; (2015) and showcases more of the personality issues, lousy business practices, and messiness inherit in starting a business within a novel market. The book spends little time on game design or why roleplaying games were successful or fun; for example, non-gamers may be mystified why modules were so lucrative as the reader is assumed to understand the mechanics of hosting a gaming session. However, audience restrictions aside, the book is a very valuable addition to the relatively young field of game studies and should be very useful for comparative studies for how other entertainment industries managed their growth and talent.&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Tue, 23 Nov 2021 20:50:58 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/game-wizards/</guid></item><item><title>Equality needs rituals, NOT flattening</title><link>https://stop.zona-m.net/2021/11/equality-needs-rituals-not-flattening/</link><description>&lt;p&gt;If you care about equality, beware of what flattens society.Especially&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 23 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/equality-needs-rituals-not-flattening/</guid></item><item><title>14 years of game dev</title><link>https://liza.io/14-years-of-game-dev/</link><description>&lt;p&gt;It feels like just yesterday I got my first job in game dev back in Australia, but it&amp;rsquo;s been about &lt;em&gt;14 years&lt;/em&gt;! I feel like I&amp;rsquo;ve done a little bit of everything, and was very fortunate to have gotten to grow as an SE on the job while working with different languages, frameworks, and teams. I guess the main phases of the last 14 years have been:&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Tue, 23 Nov 2021 08:02:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/14-years-of-game-dev/</guid></item><item><title>From the archives [2014]: a speculative interview about public transport</title><link>http://michaelhoney.com/writing/2021/11/23/from-the-archives-2014-a-speculative-interview-about-public-transport</link><description>&lt;p class=""&gt;&lt;em&gt;Interviewer: Kerry Ingram, thanks for talking to us. You’ve been the CEO of Transport for Canberra for five years now, and it’s been a very exciting five years for the organisation and for the city. The first thing that people from outside notice, of course, is that the TfC network is free.&lt;/em&gt;&lt;/p&gt;&lt;p class=""&gt;Ingram: That’s right. In almost every other public transport network, there’s either full or partial cost-recovery via tickets. Its very hard to make public transport profitable, though: the more you charge, the less accessible it is, and the fewer people use the service. Even with pricing set to maximise income, we only used to derive about 20% of our revenue from ticketing. That’s a significant chunk of money, to be sure, but we also &lt;em&gt;spent&lt;/em&gt; a significant amount of money installing and maintaining machines, running the ticketing network, enforcing compliance and so on.&lt;/p&gt;&lt;p class=""&gt;The first and most important thing I wanted to do when I entered this job was to eliminate fares. Money’s strange: a dollar here is exactly the same as a dollar there. But people don’t necessarily see it that way. Some services, like roads, are provided “free” by the government, while others – like the old bus services – were user-pays – except in this case, the user was only paying less than a quarter of the full cost. So it’s always been subsidised to a large percentage – we just made that percentage 100.&lt;/p&gt;&lt;p class=""&gt;Dropping fares cost about $25 million, but what we bought with that is far more valuable. We bought access throughout the city as a &lt;em&gt;right&lt;/em&gt; for citizens, something which they could expect, in the same way as they expect the streets to be lit, garbage to be collected, fires to be extinguished. Put that way, &lt;em&gt;not&lt;/em&gt; making transport free would be perverse.&lt;/p&gt;&lt;p class=""&gt;Collecting fees – the ticketing, the the compliance – itself costs money and time and hassle. Going free means we save that money (none of which helped people travel on our network), but it also means that we make our services faster to board, easier to use, and much more widely available to all members of the community, no matter what their financial status.&lt;/p&gt;&lt;p class=""&gt;&lt;em&gt;But that’s not how everyone saw it.&lt;/em&gt;&lt;/p&gt;&lt;p class=""&gt;Sure. We had a backlash from those those stood to lose money, particularly taxi drivers and even the operators of pay parking in the city. The taxi operators had a point: governments in the past had derived significant revenue from selling taxi licenses as exorbitant rates. We ended up buying back those licenses and folding taxi services into the TfC network. There were also cranky letter writers whose basic objection, when you you boiled it down, was to “subsidising” poor people, as if they themselves had paid directly for all the government services they use.&lt;/p&gt;&lt;p class=""&gt;Once we went free, there was a huge increase in network use: it was pretty hard to meet the demand at first, but people understood there’d be growing pains. Now, we’d never go back to the old way: free transport is a defining characteristic of our city. You’re overseas and mention you’re from Canberra and people ask about it. Going free means it’s normalised: public transport isn’t just for people who can’t drive, it’s for everyone. When we became an independent for-benefit corporation, I didn’t want its direction set by the usual bunch of middle-aged businesspeople, people like myself. Canberra’s citizens are shareholders and stakeholders, and I’m responsible to a board which is demographically proportional to our population: we have schoolchildren, young parents, older Canberrans, students, as well as workers of all kinds. We encourage people to contribute their ideas, their energy, their perspectives to the way we work.&lt;/p&gt;&lt;p class=""&gt;How much is it worth to live in a connected community, one where everyone from every walk of life has the same access to the city? How much is it worth to have residents the outer suburbs able to get to work, to play, to visit their families without having to pay extra? And having a popular transport network also means we can have a &lt;em&gt;pervasive&lt;/em&gt; network. More people use it to go more places every year. We’re continually extending the network, making it better, faster, more accessible.&lt;/p&gt;&lt;p class=""&gt;&lt;em&gt;It’s a very different network now, compared to five years ago.&lt;/em&gt;&lt;/p&gt;&lt;p class=""&gt;Absolutely. I came on board as the head of ACTION:&amp;nbsp;the ACT Internal Omnibus Network. That name specified a mode of transport: naturally, that limited the scope of its ambitions. Changing the name to Transport for Canberra wasn’t just an exercise in rebranding: it reconfigured our thinking around what we’re here for. We’re not in the bus business: we’re in the transport business. We should be mode-agnostic. Today, most users of our network use at least two different vehicles each trip – typically an electric microbus to the nearest local hub, then an express vehicle – tram, train, bus – to a regional centre.&lt;/p&gt;&lt;p class=""&gt;Switching to microbuses for that first five kilometres was a no-brainer. Big buses are great for moving fifty, a hundred people, and above that number, trains excel. But big buses are noisy, they don’t take corners well, and they accelerate and decelerate badly, which is exactly what you don’t want when you’re picking up passengers every few hundred metres. The electric microbuses are cheaper to run and easier to maintain – and they use green energy, so it’s a win all around.&lt;/p&gt;&lt;p class=""&gt;The new network – and the organisation which runs it - is– much more flexible, more responsible, more able to cope with shifting demands. Requesting a microbus via smartphone and knowing the exact moment it’ll be there beats standing in the rain waiting for a big bus to come and pick you up. We track every trip via face-recognition, so we know who’s going where. Better data leads to better decisions: our systems can predict demand and meet it efficiently. We’re seeing people use the network outside of normal times, to do things that they never would have done before. Groups of friends call up a microbus and go out to dinner: picking people up and dropping them home is part of the night.&lt;/p&gt;&lt;p class=""&gt;&lt;em&gt;What’s next, then?&lt;/em&gt;&lt;/p&gt;&lt;p class=""&gt;We’re going to see further diversification in our vehicle fleet. Usage patterns are notoriously lumpy: we get much more traffic at the start and end of the working day, of course, but weather and school holidays and major events all feed into it. Ideally we’ll always be able to meet demand. We’re looking at bringing in casual, on-demand microbuses or even smaller vehicles, owned privately, which will be available when required to meet peak needs. A semiretired person might do a couple of hours at each end of the day, making contacts with their local community and earning some money while they’re at it. A uni student could go out late on a Friday night and be the designated driver for their whole suburb.&lt;/p&gt;&lt;p class=""&gt;We’re seeing some unexpected benefits too, with our shift to electric vehicles. Deciding to build the vehicles ourselves rather than going out to tender has literally electrified our engineering and maintenance teams: we’re getting orders from other cities for vehicles of their own, and there’s a growing cottage industry of people renting the workshops and our peoples’ skills after-hours, to do their own electric vehicle conversions. We’re not only the free-transport city: we’re now the electric vehicle city.&lt;/p&gt;&lt;p class=""&gt;We don’t have the infrastructure for it yet, but I’m also interested in how an electric fleet can help power our homes and our workplaces. Most of our vehicles are still idle for much of the time: they could be feeding power back into the grid, heating our buildings, making our energy network more flexible, more resilient. Further out, I imagine autonomous, self-driving vehicles will make a big impact: salary costs are three-quarters of our budget. If we could save that money, we could move a lot more people…&lt;/p&gt;&lt;p class=""&gt;&lt;em&gt;Interesting times. Thanks for talking to us.&lt;/em&gt;&lt;/p&gt;&lt;p class=""&gt;Thank you.&lt;/p&gt;</description><author>Michael Honey: Writing - michaelhoney.com</author><pubDate>Tue, 23 Nov 2021 07:25:51 GMT</pubDate><guid isPermaLink="true">http://michaelhoney.com/writing/2021/11/23/from-the-archives-2014-a-speculative-interview-about-public-transport</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #47</title><link>https://justingarrison.com/123dev/2021-11-23-123dev-47/</link><description>&lt;img alt="A ball hits glass filmed in super slow motion to see the glass shatter" src="https://justingarrison.com/img/123dev/47-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="moment-of-impact"&gt;Moment of impact&lt;/h2&gt;
&lt;p&gt;If you watch the gif again look at the moment the ball hits the glass. The glass appears to shatter &lt;strong&gt;before&lt;/strong&gt; the ball makes contact. How is that possible?&lt;/p&gt;
&lt;p&gt;It turns out glass often breaks from tension rather than compression. The ball &lt;strong&gt;pushing&lt;/strong&gt; on the glass makes it break from the opposite side from where it hit.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 23 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-11-23-123dev-47/</guid></item><item><title>Implementing zip archiving in Golang: unzipping</title><link>http://notes.eatonphil.com/implementing-zip-in-go-unzipping.html</link><description>&lt;p&gt;&lt;small&gt;All code for this post is &lt;a href="https://github.com/eatonphil/gozip"&gt;available on Github&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Let's take a look at how zip files work. Take a small file for example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;hello.text
Hello!
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let's zip it up.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;zip&lt;span class="w"&gt; &lt;/span&gt;test.zip&lt;span class="w"&gt; &lt;/span&gt;hello.text
adding:&lt;span class="w"&gt; &lt;/span&gt;hello.text&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;stored&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;%&lt;span class="o"&gt;)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-lah&lt;span class="w"&gt; &lt;/span&gt;test.zip
-rw-r--r--&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;phil&lt;span class="w"&gt; &lt;/span&gt;phil&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;177&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Nov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:04&lt;span class="w"&gt; &lt;/span&gt;test.zip
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So a 6 byte text file becomes a 177 byte zip file. That is pretty
small! Parsing 177 bytes sounds like it can't possibly be too
complicated!&lt;/p&gt;
&lt;p&gt;Let's hexdump the zip file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;hexdump&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;test.zip
&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;4b&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;8a&lt;span class="w"&gt; &lt;/span&gt;b8&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;77&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;9e&lt;span class="w"&gt; &lt;/span&gt;d8&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;PK..........wS..&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000010&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;07&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;07&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;1c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;68&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;B.............he&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000020&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;6c&lt;span class="w"&gt; &lt;/span&gt;6c&lt;span class="w"&gt; &lt;/span&gt;6f&lt;span class="w"&gt; &lt;/span&gt;2e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;78&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;09&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;9d&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;llo.textUT...ts.&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000030&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;9d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;75&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;78&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0b&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;eb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;ats.aux.........&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000040&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;eb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;6c&lt;span class="w"&gt; &lt;/span&gt;6c&lt;span class="w"&gt;  &lt;/span&gt;6f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;4b&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;1e&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;....Hello!.PK...&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000050&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;8a&lt;span class="w"&gt;  &lt;/span&gt;b8&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;77&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;53&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;9e&lt;span class="w"&gt; &lt;/span&gt;d8&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;07&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;.........wS..B..&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000060&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;07&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0a&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;................&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000070&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;a4&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;81&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;68&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;6c&lt;span class="w"&gt; &lt;/span&gt;6c&lt;span class="w"&gt; &lt;/span&gt;6f&lt;span class="w"&gt; &lt;/span&gt;2e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;.........hello.t&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000080&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;78&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;74&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;73&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;9d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;61&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;75&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;78&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0b&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;extUT...ts.aux..&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="m"&gt;00000090&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;eb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;eb&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;4b&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;06&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;...........PK...&lt;span class="p"&gt;|&lt;/span&gt;
000000a0&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;4b&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;.......P...K....&lt;span class="p"&gt;|&lt;/span&gt;
000000b0&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;                                                &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;.&lt;span class="p"&gt;|&lt;/span&gt;
000000b1
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can see both the file name and the file contents in there.&lt;/p&gt;
&lt;h3 id="structure"&gt;Structure&lt;/h3&gt;&lt;p&gt;Let's take a look at the zip structure defined
&lt;a href="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT"&gt;here&lt;/a&gt;. Based
on section 4.3.6 it looks like file metadata followed by the file
contents are stored one after another with a final chunk of "central
directory" metadata.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
  &lt;img src="https://www.codeproject.com/KB/cs/remotezip/diagram1.png" style="height: 400px; width: auto;" /&gt;
  &lt;div&gt;
    &lt;small&gt;&lt;a href="https://www.codeproject.com/Articles/8688/Extracting-files-from-a-remote-ZIP-archive"&gt;Image Credit&lt;/a&gt;&lt;/small&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The local header metadata looks like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;local file header signature&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;version needed to extract&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;general purpose bit flag&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compression method&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last mod file time&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;last mod file date&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;crc-32&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compressed size&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uncompressed size&lt;/td&gt;
&lt;td&gt;4 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;file name length&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extra field length&lt;/td&gt;
&lt;td&gt;2 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;file name&lt;/td&gt;
&lt;td&gt;variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extra field&lt;/td&gt;
&lt;td&gt;variable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The header signature is a single integer (&lt;code&gt;0x04034b50&lt;/code&gt;) in
a valid zip file. We'll ignore version, the general purpose flag, and
the checksum. Compression is either &lt;code&gt;0&lt;/code&gt; for no compression
or &lt;code&gt;8&lt;/code&gt; for DEFLATE compression/decompression.&lt;/p&gt;
&lt;p&gt;Last modified time and date is MSDOS-style date/time format which is
&lt;a href="https://groups.google.com/g/comp.os.msdos.programmer/c/ffAVUFN2NbA"&gt;pretty
funky&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let's translate this roughly to Go with some high level flourishes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;os&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;bytes&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;compress/flate&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io/ioutil&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;encoding/binary&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint8&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;noCompression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;iota&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;deflateCompression&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;localFileHeader&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint16&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;bitFlag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint16&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;lastModified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Time&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;crc32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;compressedSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;uncompressedSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;extraField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="main"&gt;main&lt;/h3&gt;&lt;p&gt;Our entrypoint will read a zip file and keep walking through the file
until we stop being able to parse zip file entries.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ioutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lfh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;localFileHeader&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;lfh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parseLocalFileHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errNotZip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lfh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lfh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lfh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="files"&gt;Files&lt;/h3&gt;&lt;p&gt;For each file we'll fail early if the first four bytes are not the magic zip signature.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errNotZip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Not a zip file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parseLocalFileHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;localFileHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x04034b50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errNotZip&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The basic pattern is that one of these read helpers will take an
offset and return a Go value and a new offset. The read helper will do
bounds checking. We'll define the read helpers further down.&lt;/p&gt;
&lt;p&gt;Let's follow the same pattern to the end of the struct:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;bitFlag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;compression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;noCompression&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;compressionRaw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;compressionRaw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;compression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;deflateCompression&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;lmTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;lmDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;lastModified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;msdosTimeToGoTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;lmDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;lmTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;crc32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;compressedSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;uncompressedSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;fileNameLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;extraFieldLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fileNameLength&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;extraField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;readBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;extraFieldLength&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now if the file contents are uncompressed we can just copy bytes after
the file header. If the file contents are compressed though we'll use
Go's builtin DEFLATE support to decompress the bytes after the file
header.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;noCompression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uncompressedSize&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compressedSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errOverranBuffer&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;flateReader&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;flateReader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ioutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flateReader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And return the filled out representation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;localFileHeader&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;bitFlag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bitFlag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;crc32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;crc32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;compressedSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compressedSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;uncompressedSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;uncompressedSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;extraField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;extraField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="read-helpers"&gt;Read helpers&lt;/h3&gt;&lt;p&gt;Now we just define those read helpers with bounds checking, using Go's
builtin libraries for dealing with binary encodings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errOverranBuffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Overran buffer&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readUint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errOverranBuffer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LittleEndian&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readUint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errOverranBuffer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LittleEndian&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Uint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And basically only bounds checking for grabbing bytes and strings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errOverranBuffer&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;readBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="msdos-time"&gt;MSDOS time&lt;/h3&gt;&lt;p&gt;At the time zip was created, MSDOS time format was popular, I
guess. But it's not popular today so it took a bit of work to finally
find &lt;a href="https://groups.google.com/g/comp.os.msdos.programmer/c/ffAVUFN2NbA"&gt;an explanation of the
format&lt;/a&gt;
with some code (in C).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;msdosTimeToGoTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x1F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;minutes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x3F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;hours&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x1F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Month&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x0F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x7F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1980&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="tout-ensemble"&gt;Tout ensemble&lt;/h3&gt;&lt;p&gt;Running it we get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;go&lt;span class="w"&gt; &lt;/span&gt;build
$&lt;span class="w"&gt; &lt;/span&gt;./gozip&lt;span class="w"&gt; &lt;/span&gt;test.zip
&lt;span class="m"&gt;2021&lt;/span&gt;-11-23&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:04:20&lt;span class="w"&gt; &lt;/span&gt;+0000&lt;span class="w"&gt; &lt;/span&gt;UTC&lt;span class="w"&gt; &lt;/span&gt;hello.text&lt;span class="w"&gt; &lt;/span&gt;Hello!
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That looks good! Now let's try zipping more than one file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;bye.text
Au&lt;span class="w"&gt; &lt;/span&gt;revoir!
$&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;test.zip
$&lt;span class="w"&gt; &lt;/span&gt;zip&lt;span class="w"&gt; &lt;/span&gt;test.zip&lt;span class="w"&gt; &lt;/span&gt;*.text
&lt;span class="w"&gt;  &lt;/span&gt;adding:&lt;span class="w"&gt; &lt;/span&gt;bye.text&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;stored&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;adding:&lt;span class="w"&gt; &lt;/span&gt;hello.text&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;stored&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;%&lt;span class="o"&gt;)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;./gozip&lt;span class="w"&gt; &lt;/span&gt;test.zip
&lt;span class="m"&gt;2021&lt;/span&gt;-11-24&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;03&lt;/span&gt;:40:00&lt;span class="w"&gt; &lt;/span&gt;+0000&lt;span class="w"&gt; &lt;/span&gt;UTC&lt;span class="w"&gt; &lt;/span&gt;bye.text&lt;span class="w"&gt; &lt;/span&gt;Au&lt;span class="w"&gt; &lt;/span&gt;revoir!

&lt;span class="m"&gt;2021&lt;/span&gt;-11-23&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:04:20&lt;span class="w"&gt; &lt;/span&gt;+0000&lt;span class="w"&gt; &lt;/span&gt;UTC&lt;span class="w"&gt; &lt;/span&gt;hello.text&lt;span class="w"&gt; &lt;/span&gt;Hello!
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Fab.&lt;/p&gt;
&lt;h3 id="notes"&gt;Notes&lt;/h3&gt;&lt;p&gt;There are many parts of the standard to deal with (e.g. directories)
and many common extensions. I'm ignoring them.&lt;/p&gt;
&lt;p&gt;There's some space left at the end of the file which is probably the
"central directory" metadata but I haven't dug into
that. Understanding those last remaining bits are probably necessary
if I want to be able to &lt;em&gt;create&lt;/em&gt; zip archives.&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;I wrote a new post on building a zip archive reader in Go!&lt;a href="https://t.co/U0Yg2powlP"&gt;https://t.co/U0Yg2powlP&lt;/a&gt; &lt;a href="https://t.co/ns5dF3mjIx"&gt;pic.twitter.com/ns5dF3mjIx&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1463354752675323904?ref_src=twsrc%5Etfw"&gt;November 24, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Tue, 23 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/implementing-zip-in-go-unzipping.html</guid></item><item><title>Refining The Return Type Of Java Methods Without Breaking Backwards-Compatibility</title><link>https://www.morling.dev/blog/refining-return-type-java-methods-without-breaking-backwards-compatibility/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_the_problem"&gt;The Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_bridge_methods_to_the_rescue"&gt;Bridge Methods to the Rescue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_creating_bridge_methods_ourselves"&gt;Creating Bridge Methods Ourselves&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;If you work on any kind of software library,
ensuring backwards-compatibility is a key concern:
if there’s one thing which users really dislike, it is breaking changes in a new version of a library.
The rules of what can (and cannot) be changed in a Java API without breaking existing consumers are well defined in the Java language specification (JLS),
but things can get pretty interesting in certain corner cases.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Eclipse team provides a &lt;a href="https://wiki.eclipse.org/Evolving_Java-based_APIs_2"&gt;comprehensive overview&lt;/a&gt; about API evolution guidelines in their wiki.
When I shared the link to this great resource on Twitter the other day,
I received an &lt;a href="https://twitter.com/lukaseder/status/1462358911072317440"&gt;interesting reply&lt;/a&gt; from Lukas Eder:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="quoteblock"&gt;
&lt;blockquote&gt;
I wish Java had a few tools to prevent some cases of binary compatibility breakages. E.g. when refining a method return type, I’d like to keep the old method around in byte code (but not in source code).
&lt;br /&gt;
&lt;br /&gt;
I think kotlin has such tools?
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In the remainder of this post,
I’d like to provide some more insight into that problem mentioned by Lukas,
and how it can be addressed using an open-source tool called &lt;a href="https://github.com/dmlloyd/bridger"&gt;Bridger&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Mon, 22 Nov 2021 19:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/refining-return-type-java-methods-without-breaking-backwards-compatibility/</guid></item><item><title>NEVER mix your employer and your smartphone</title><link>https://stop.zona-m.net/2021/11/never-mix-your-employer-and-your-smartphone/</link><description>&lt;p&gt;Seriously. Never, ever mix them.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 22 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/never-mix-your-employer-and-your-smartphone/</guid></item><item><title>Butterfly scales tile the plane, expanding spiral</title><link>http://michaelhoney.com/writing/2021/11/22/butterfly-scales-tile-the-plane-expanding-spiral</link><description>&lt;figure class="
              sqs-block-image-figure
              intrinsic
            "&gt;
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                &lt;img alt="" height="4000" src="https://images.squarespace-cdn.com/content/v1/5244d189e4b0136e0a44003d/8490932f-68cc-4041-9707-9292f41fd3c1/concentric-tile-2020-10-5-19-38-30.png?format=1000w" width="4000" /&gt;

            
          
        
          
        

        
          
          &lt;figcaption class="image-caption-wrapper"&gt;
            &lt;p class=""&gt;Butterfly scales tile the plane, expanding spiral. Using p5js&lt;/p&gt;
          &lt;/figcaption&gt;
        
      
        &lt;/figure&gt;</description><author>Michael Honey: Writing - michaelhoney.com</author><pubDate>Mon, 22 Nov 2021 07:26:04 GMT</pubDate><guid isPermaLink="true">http://michaelhoney.com/writing/2021/11/22/butterfly-scales-tile-the-plane-expanding-spiral</guid></item><item><title>Franklin Falls</title><link>https://sam.hooke.me/trip/2021/11/franklin-falls/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Mon, 22 Nov 2021 05:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/11/franklin-falls/</guid></item><item><title>Staff Engineer: Leadership beyond the management track</title><link>https://www.pinecoder.dev/blog/2021-11-22/staff-engineer-leadership-beyond-management/</link><description>This post is a review of (and thoughts about) Will Larson&amp;rsquo;s Staff Engineering book.
Introduction I&amp;rsquo;ve worked at a few successful1 startups, yet rarely one&amp;rsquo;s that had proper engineering roles as such. If your tech career spawned out of IT like me, you might lack a conceptual framework for thinking about engineering leadership. There are hero programmer&amp;rsquo;s and prestige seeker&amp;rsquo;s. If there is an Engineering Manager it might be someone from finance with an MBA.</description><author>Home on Pinecoder blog</author><pubDate>Mon, 22 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.pinecoder.dev/blog/2021-11-22/staff-engineer-leadership-beyond-management/</guid></item><item><title>Art Science Taste And Clean Code</title><link>http://live.julik.nl/2021/11/art-science-taste-and-clean-code</link><description/><author>julik live</author><pubDate>Mon, 22 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://live.julik.nl/2021/11/art-science-taste-and-clean-code</guid></item><item><title>Creeper Host API wrapper in TypeScript</title><link>https://maxleiter.com/blog/creeperhost-api</link><description>&lt;p&gt;I've had a server on &lt;a href="https://creeper.host" rel="noopener noreferrer" target="_blank"&gt;Creeper Host&lt;/a&gt; for around eight years now, and have used it for small side projects and hackathons. Lately, I've been trying to automate more of my life and discovered that the API their in-house control panel uses is &lt;a href="https://creeperhost.docs.apiary.io/" rel="noopener noreferrer" target="_blank"&gt;available for general use&lt;/a&gt;. I'm making this post in case someone else ever wants to interact with their API via JavaScript. Maybe someday I'll create a proper repository, but for now my wrapper is incomplete and it's easier to just include here. It was also a good chance to write some TypeScript from scratch, something I haven't had the opportunty to do very much of.&lt;/p&gt;
&lt;p&gt;To get started, you'll need to generate a Developer Token on the &lt;a href="https://creeperpanel.com" rel="noopener noreferrer" target="_blank"&gt;control panel&lt;/a&gt;. Navigate to the `Sub-accounts` page and generate a new API key at the bottom of the page:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of the Creeper Host control panel with a blue 'Generate Key' button" src="/blog/creeperhost/apiKey.png" /&gt;&lt;/p&gt;
&lt;p&gt;Also, install &lt;a href="https://axios-http.com/" rel="noopener noreferrer" target="_blank"&gt;axios&lt;/a&gt; (or change the below code to your favorite request library, I don't care):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;    yarn add axios
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, incorporate these two files:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-typescript"&gt;import axios, { AxiosRequestConfig } from 'axios'
import { InitializationOptions, MetricResponse, RestartResponse } from './types'

export default class Client {
  private instance: string
  constructor({
    apiKey,
    apiSecret,
    apiUrl = 'https://api.creeper.host',
    instanceId,
  }: InitializationOptions) {
    axios.defaults.baseURL = apiUrl

    axios.defaults.headers.common['key'] = apiKey
    axios.defaults.headers.common['secret'] = apiSecret
    axios.defaults.headers.common['Content-Type'] = 'application/json'

    this.instance = instanceId
  }

  private async request&amp;lt;T = any&amp;gt;({
    method = 'GET',
    apiRoute,
    body,
  }: {
    method: 'GET' | 'POST'
    apiRoute: string
    body?: any
  }) {
    const config: AxiosRequestConfig = {
      method,
      url: apiRoute,
      data: body,
    }

    console.log(
      `[${method}] ${apiRoute}`,
      body ? `\t ${JSON.stringify(body)}` : '',
    )

    const request = await axios.request&amp;lt;T&amp;gt;(config)
    return request.data
  }

  public os = {
    getram: async () =&amp;gt; {
      return this.request&amp;lt;MetricResponse&amp;gt;({
        method: 'GET',
        apiRoute: 'os/getram',
      })
    },
    getssd: async () =&amp;gt; {
      return this.request&amp;lt;MetricResponse&amp;gt;({
        method: 'GET',
        apiRoute: 'os/getssd',
      })
    },
  }

  public minecraft = {
    restartserver: async () =&amp;gt; {
      return this.request&amp;lt;RestartResponse&amp;gt;({
        method: 'POST',
        apiRoute: 'minecraft/restartserver',
        body: { instance: this.instance },
      })
    },
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-typescript"&gt;export type MetricResponse =
  | {
      status: 'success'
      free: number
      used: number
    }
  | {
      status: 'error'
      message: string
    }

export type RestartResponse =
  | {
      status: 'success'
      message: string
    }
  | {
      status: 'error'
      message: string
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, create an instance via `new Client({ apiKey, apiSecret, instanceId });`&lt;br /&gt;You can use it by finding what you want to call on the Creeper Host docs and accessing that on the client; I purposefully used the same naming scheme. For example,&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-typescript"&gt;Client.os.getram(): Promise&amp;lt;MetricResponse&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;corresponds to the &lt;a href="https://creeperhost.docs.apiary.io/#/reference/0/server/get-server-ram-details/200?mc=reference%2F0%2Fserver%2Fget-server-ram-details%2F200" rel="noopener noreferrer" target="_blank"&gt;https://api.creeper.host/os/getram&lt;/a&gt; endpoint.&lt;/p&gt;
&lt;p&gt;I removed some methods from the above to avoid cluttering this page, but it's straight forward to add your own. If you really want to use this, contact me and I can set up a repo for us to collaborate. Also, if you have suggestions on how I can improve this, let me know via &lt;a href="mailto:maxwell.leiter@gmail.com" rel="noopener noreferrer" target="_blank"&gt;email&lt;/a&gt; or on &lt;a href="https://twitter.com/maxleiter" rel="noopener noreferrer" target="_blank"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;</description><author>Max Leiter</author><pubDate>Mon, 22 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://maxleiter.com/blog/creeperhost-api</guid></item><item><title>Exiting Artificial Scarcity</title><link>http://dimitarsimeonov.com/2021/11/21/exiting-artificial-scarcity</link><description>&lt;blockquote&gt;
  &lt;p&gt;He who controls the spice, controls the universe&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Baron Harkonnen, character in “Dune”
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is part 5 of the series on “How to be a game changer”. You can find the last four here: &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;1&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;2&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer"&gt;3&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources"&gt;4&lt;/a&gt;. Today we continue with examples of negative games you don’t want to play - and we’ll discuss artificial scarcity.&lt;/p&gt;

&lt;p&gt;Let’s start with my dog, Euler. Originally he didn’t always want to eat his dog food kibble. So me and my partner started mixing in pieces of meat, or sauce - something to add more real flavor. Euler would then devour it. He wasn’t sick - he was just a picky eater. We were glad we could help him get the nutrition he needs, and enjoy the flavor, but then we realized we were having a hard time attracting his attention, he became bratty, and treats lost their persuasion power. What happened was that Euler expected something better than the treats - meat in his dog food - for granted. We then stopped flavoring his kibble, and at first he didn’t eat much again. But a stomach is a powerful motivator and he of course got back to eating. Not only that, but now he was a lot more interested in treats in other situations, which helped us teach him other behaviors and distract him when others are around.&lt;/p&gt;

&lt;p&gt;&lt;img alt="good boy bad boy" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/05puppy.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;We definitely could afford to put a little bit of meat in his food every meal, heated on the microwave so the juices flavor the dogfood. But we introduced an artificial scarcity for him, and as a result his behavior improved. We didn’t even use the words artificial scarcity when discussing this policy - we just reasoned about the behavior he would exhibit with or without the extra meat in his food.&lt;/p&gt;

&lt;p&gt;So what really is scarcity, and when is it artificial? And how do you escape the artificial scarcity? Scarcity has a simple meaning - some resource is only available in small quantities - fewer than people want. Gold is somewhat scarce on Earth, as we tend to want a lot more gold than we have on Earth, but on a lot of asteroids it may be abundant. When we can mine these asteroids, gold will no longer be scarce for us.&lt;/p&gt;

&lt;p&gt;When something is scarce and people want it, it becomes valuable. And this is the dynamic which causes artificial scarcity - when someone wants an item to be valuable, if they can make this item appear scarce, it will be valuable. They can then sell that item for a large profit, and incentivize behavior in others who want that item. For example, seeing a picture of Mona Lisa is not scarce, as you can just google it, but seeing the original in the Louvre in Paris is scarce.&lt;/p&gt;

&lt;p&gt;Ultimately, artificial scarcity is about control. There is no need for a conspiracy here, as those with the means to control scarcity will simply do so to maximize their wellbeing, profitability, and power.&lt;/p&gt;

&lt;p&gt;The way to exit scarcity is being truthful to yourself what you truly want inside, and not what other people have made you to believe you want. One way to exit scarcity is to not be swung by hype. There was no scarcity of smart phones in 1920, as nobody wanted them, but there was scarcity in 2007 because more people wanted one than there were smartphones. For the first few years Apple made the new model of iPhone scarce by adding some feature which wasn’t available on the old phones. But today they haven’t innovated as much and people are not as hyped up on getting the newest iPhone.&lt;/p&gt;

&lt;p&gt;Another way is to &lt;strong&gt;not buy&lt;/strong&gt; what is artificially scarce, and adopt a mindset of abundance. Realize that &lt;a href="https://dimitarsimeonov.com/2015/01/21/fomo-is-visibility-bias"&gt;FOMO is just visibility bias&lt;/a&gt;, the things that we crave, we crave not because we really need them and they are scarce, but because we see them more often. Monkey see, monkey want. But we can notice that monkey in our mind and kindly tell it about all the other things it doesn’t see. See, the monkey’s got the steering wheel, but we can influence it ourselves, similar to how our environment influence it. We can tell the monkey about the actual abundance available around us. Materially, humanity is the most advanced it has ever been, but we still often look at the world through artificial scarcity glasses.&lt;/p&gt;

&lt;p&gt;&lt;img alt="fighting vs growing" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/05build.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Today California, and especially the Bay Area has artificial scarcity of housing, which creates high real estate and rent prices, and exacerbates homelessness. Buying overpriced housing would mean giving in to the artificial scarcity narrative. We have the technological and economic power to make this area a lot more welcoming and affordable, but that means people who recently bought houses would experience a big loss in paper value. One way to exit is to not buy, because if you buy, one day the artificial scarcity will meet reality, one way or another, and someone will be the “biggest fool”, who bought an ultra expensive house, but lost all paper value. And that reality check might be sooner than you expect - Silicon Valley is no longer the only hub for high tech development, and a lot of the demand for its scarce housing may be redirected to other locations. If you have to be there - rent. That way you’re still playing an artificial scarcity game, but at least the stakes are much lower.&lt;/p&gt;

&lt;p&gt;In a sense, Bay Area homeownership is a proxy for status today. And status is another type of artificial scarcity, but it is so important that it deserves its own article. We’ll discuss status in the next week’s essay.&lt;/p&gt;</description><author>D13V</author><pubDate>Sun, 21 Nov 2021 23:57:55 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/11/21/exiting-artificial-scarcity</guid></item><item><title>rsync on macOS: Matters Arising</title><link>https://ayewo.com/switching-to-a-newer-version-of-rsync-on-macos/</link><description>Setting up your macOS to use rsync v3.2.3 from Homebrew, the right way.</description><author>Misleading Metaphors</author><pubDate>Sun, 21 Nov 2021 13:27:26 GMT</pubDate><guid isPermaLink="true">https://ayewo.com/switching-to-a-newer-version-of-rsync-on-macos/</guid></item><item><title>AWKGo, an AWK-to-Go compiler</title><link>https://benhoyt.com/writings/awkgo/</link><description>A discussion of AWKGo, a simple compiler that translates a useful subset of the AWK text processing language into Go source code.</description><author>Ben Hoyt's technical writing</author><pubDate>Sun, 21 Nov 2021 13:10:00 GMT</pubDate><guid isPermaLink="true">https://benhoyt.com/writings/awkgo/</guid></item><item><title>WFH Day 616</title><link>https://ingo-richter.io/cv19wfh/2021/11/day616/</link><description>&lt;h2 id="wfh-day-616"&gt;WFH Day 616&lt;/h2&gt;
&lt;p&gt;I spent four weeks in Germany with my wife and some of my children. Part of the time, I had some PTO and was able to meet with friends and family. The remaining time I was either working or renovating the new place for my wife. Usually working at my desk using my brain, it was a welcome change of pace to work with my hands and do more manual labor tasks. After seven consecutive days of manual work stripping carpet, removing wallpaper, painting, removing old furniture and crazy wood ceiling decorations, I was looking forward to having a small break. My whole body was aching, and every step hurts :-)
I&amp;rsquo;m on my way back to California before returning to Germany in 4 weeks for Christmas break and to finalize the new place. There is always something to do in a new place&amp;hellip;
I always wanted to say that I&amp;rsquo;m grateful to live in California and periodically work remotely from Germany. Glad my employer gives me that freedom. Especially during these challenging times when traveling is not always straightforward.
I&amp;rsquo;m looking forward to the day that my family will travel to California without any restrictions.
I need to remind myself to stay positive and hope for the best as time goes by. That&amp;rsquo;s the only way to keep my sanity and don&amp;rsquo;t lose sight of better times for all of us.&lt;/p&gt;</description><author>Ingo Richter on Today is Tomorrow's Past</author><pubDate>Sun, 21 Nov 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ingo-richter.io/cv19wfh/2021/11/day616/</guid></item><item><title>How to automatically pull and deploy updated Docker images</title><link>https://nicolaiarocci.com/how-to-automatically-pull-and-deploy-updated-docker-images/</link><description>&lt;p&gt;We want our test and production stacks to be automatically updated every time
something new is pushed to the &lt;code&gt;test&lt;/code&gt; or &lt;code&gt;release&lt;/code&gt; branch. CI builds the docker
image on successful test runs, then stores it in our private registry. But how
do you automatically pull and deploy those updated images?&lt;/p&gt;
&lt;p&gt;I looked into the &lt;a href="https://containrrr.dev/watchtower/"&gt;Watchtower&lt;/a&gt; project, which is interesting. You add
Watchtower to the stack, and it will diligently check for new versions of the
images used by the containers in the stack, pulling, building and deploying as
needed while the stack is up and running. In my experiments, however, I had
&lt;a href="https://github.com/containrrr/watchtower/issues/1113"&gt;little luck&lt;/a&gt; in making it talk with our private registry. Also, I&amp;rsquo;m not too
fond of polluting my stack with foreign containers. I want my docker stack to
be simple, tidy, clean, and single-tasked.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 21 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/how-to-automatically-pull-and-deploy-updated-docker-images/</guid></item><item><title>Only 1% of Software Benefits from SIMD Instructions</title><link>https://ashvardanian.com/posts/simd-popularity/</link><description>Analysis of 2,000+ Linux and macOS binaries reveals less than 1% of instructions use SIMD, despite CPUs dedicating massive die area to vector processing.</description><author>Ash's Blog</author><pubDate>Sun, 21 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ashvardanian.com/posts/simd-popularity/</guid></item><item><title>Munchie and Mangia</title><link>https://solomon.io/munchie-and-mangia/</link><description>We moved into our new house in late September. It felt enormous compared to the studio where Holly, Remi, and I spent most of the pandemic.</description><author>Sam Solomon</author><pubDate>Sun, 21 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://solomon.io/munchie-and-mangia/</guid></item><item><title>Please support every "Polar Bear" like this</title><link>https://stop.zona-m.net/2021/11/please-support-every-polar-bear-like-this/</link><description>&lt;p&gt;Because we need Open Hardware manufacturing. A lot.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 20 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/please-support-every-polar-bear-like-this/</guid></item><item><title>Dune</title><link>https://olshansky.info/movie/dune/</link><description>Olshansky's review of Dune</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 20 Nov 2021 10:47:58 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/dune/</guid></item><item><title>Catastrophic Wealth</title><link>https://greaterdanorequalto.com/catastrophic-wealth/</link><description>Sometimes I worry about becoming rich.</description><author>Greater Dan Or Equal To</author><pubDate>Sat, 20 Nov 2021 04:07:25 GMT</pubDate><guid isPermaLink="true">https://greaterdanorequalto.com/catastrophic-wealth/</guid></item><item><title>You Better Shut Your Mouth (While You Sleep)</title><link>https://godsip.club/articles/better-shut-your-mouth/</link><description>&lt;h1 id="a-tiny-white-mouse"&gt;A Tiny White Mouse &lt;a class="anchor" href="#a-tiny-white-mouse"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Vampires are not the only popular myths from Transylvania: there are other peculiar beliefs, often broaden to other countries – or maybe borrowed from them, who knows. One is about the &lt;em&gt;soul&lt;/em&gt; and it is told by mothers to their children: if you sleep with your mouth open, your soul will escape your body in the form of a tiny white mouse.&lt;/p&gt;
&lt;p&gt;That’s a powerful image and it’s hard to forget – I’d bet that you will recall it next time you’ll see your partner sleeping. ’Silly Europeans, they have such a fantasy!’ one would think, blaming Medieval exaggerations… Well, sorry to tell you, but centuries before, in Guinea, if a person had waken up with bone pain, he would think that his soul was kicked by another one in the sleep.&lt;/p&gt;
&lt;p&gt;It was quite common opinion that, while we are dreaming, all that we see and feel reflects what the soul is doing: if a primitive hunter dreamt about chasing an animal, his soul was actually doing that. In the sleep, something leaves our body and continues living. Because of that, to cover the mouth of a sleeping person would be considered as despicable at least: what would happen if the spirit wouldn’t be able to return to the owner?&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Please note how this tiny kitten keeps his mouth wisely closed while he sleeps." src="https://godsip.club/img/sleeping-kitten.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Please note how this tiny kitten keeps his mouth wisely closed while he sleeps.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This could also be the reason why it is a common rule to avoid to wake up a sleeper abruptly: his soul wouldn’t have time to get back to the body. Someone says that he would get sicker and sicker until it won’t come back. Worst of all, in Bombay it was considered &lt;em&gt;murder&lt;/em&gt; to change a sleeper’s aspect, for instance painting the face. The soul then wouldn’t recognise the person and hence couldn’t return to the body.&lt;/p&gt;
&lt;h1 id="when-in-doubt-close-it"&gt;When In Doubt, Close It &lt;a class="anchor" href="#when-in-doubt-close-it"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;In general, the soul is conceived –literally– as a bird ready to take flight. It’s easy to “scare it” or to lose it, especially when a person is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Asleep&lt;/li&gt;
&lt;li&gt;Sick (or about to die)&lt;/li&gt;
&lt;li&gt;Eating&lt;/li&gt;
&lt;li&gt;Terrified&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In all these cases, the spirit could just exit an orifice and fly out. &lt;em&gt;An orifice&lt;/em&gt;, that’s correct, not necessarily the mouth. Sometimes the nose and the eyes can be a way out, too!&lt;/p&gt;
&lt;p&gt;Note that I don’t want to make fun of ancient beliefs, but sometimes, as a foolish game, I try to imagine what could have meant to live in a society with such great taboos. Just consider that in Celebes, in Indonesia, fishing hooks were fastened to a sick man’s nose, navel and feet to “catch” the soul in case it would escape. &lt;em&gt;Fishing hooks&lt;/em&gt;. Marquesans, from French Polynesia, held the mouth and nose of dying people in order to prevent his spirit to fade away – imagine those poor souls&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;, suffocated while trying to grasp to their life. We really can’t realise how such far and ancient civilisations lived one or two thousand years ago… Even without going too far in time, there are some intriguing examples from recent times.&lt;/p&gt;
&lt;h1 id="look-at-this-photograph"&gt;Look At This Photograph &lt;a class="anchor" href="#look-at-this-photograph"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Shame on you, Nickelback, you shouldn’t expose your spirit so much! You see, people from Andaman archipelago, in the Indian Ocean, thought that the soul is in one person’s &lt;em&gt;reflection&lt;/em&gt;, even in water pools. People are more vulnerable when they walk next to ponds, so they have to keep their eyes low to avoid hurting themselves and the others. Also, it was believed that those were sorcerers’ hunting grounds, who had the power to &lt;em&gt;steal a man’s soul&lt;/em&gt; and therefore make him fall sick. In primitive cultures, a great knowledge meant a great power.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="This man clearly has no idea about the risk he&amp;rsquo;s taking. (Via Marc Olivier-Jodoin/Unsplash)" src="https://godsip.club/img/puddle-reflection.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;This man clearly has no idea about the risk he&amp;rsquo;s taking. (Via Marc Olivier-Jodoin/Unsplash)&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Since the reflection could be harmful, when someone passed away, in the home, all the mirrors had to be covered with a cloth. Otherwise, the alive one’s spirits could be taken away by the one of the dead, which continued to stay there for some days.&lt;/p&gt;
&lt;p&gt;As the title says, spirits could also lie in &lt;strong&gt;photographs&lt;/strong&gt;. Tepehuanes people from Mexico believed that photographers had the power to catch their souls and then devour them as they pleased. Also, a chief of a village by the Yukon River, when he examined an explorer’s camera, after a minute of silence, announced to his people: “He has all of your &lt;em&gt;shades&lt;/em&gt; in this box.”&lt;/p&gt;
&lt;p&gt;Even in &lt;em&gt;shadows&lt;/em&gt; the souls could lie. In Nepal, the Grand Lama was able to defeat &lt;a href="https://en.wikipedia.org/wiki/Adi_Shankara" rel="noopener" target="_blank"&gt;Sankara&lt;/a&gt; while the latter was floating mid-air, by sticking a sword in his shade. Also, in China, at funerals people next to the coffin take a step back when it is about to be closed or when it is about to be lowered into the ground – their shadows could get caught or fall into the grave. Now it’s a little clearer why grave-diggers tie a strip of cloth firmly around their waist: to avoid such accidents.&lt;/p&gt;
&lt;p&gt;One of the most curious facts, though, is the &lt;em&gt;awe&lt;/em&gt; and the &lt;em&gt;dread&lt;/em&gt; that several cultures, especially in Australia, have towards… mothers-in-law. In some tribes of New South Wales, a man could not have any communication with his wife’s mother; it was prohibited to even look in her direction. It’s a bad omen if the mother-in-law’s shade fell on the legs or any part of the man’s body. In the New Britain Island, one of the solemn oaths was: &amp;ldquo;Sir, if I am not telling the truth, I hope I may shake hands with my mother-in-law.&amp;rdquo; Understandable, I guess. 😬&lt;/p&gt;
&lt;h1 id="profit-maximisation"&gt;Profit Maximisation &lt;a class="anchor" href="#profit-maximisation"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Until now I quoted some examples that point out how fragile and somehow volatile the soul was believed to be. The question comes naturally: ‘Are there any positive aspects in all of this?’ Of course! People &lt;em&gt;always&lt;/em&gt; find a way to yield profit of something. In particular, that’s the case of somebody with a greater knowledge who understands how spirits work: sorcerers and shamans.&lt;/p&gt;
&lt;p&gt;Even in the Middle Ages, there were plenty of stories and legends with powerful mages who were invincible because their soul was kept safe somewhere &lt;em&gt;outside&lt;/em&gt; their bodies. Russian &lt;a href="https://en.wikipedia.org/wiki/Koschei" rel="noopener" target="_blank"&gt;Koschei&lt;/a&gt; is one of the many characters who hid their spirit in nested objects and cannot be defeated until the hero of the story is able to find the magical item that holds their life (which usually is tiny and fragile).&lt;/p&gt;
&lt;p&gt;Often, in the lore, the trial to find this treasure is quite absurd. A classic example goes like this: “&lt;em&gt;On a remote island, there is a forest with a circle of trees. In the middle, six buckets of water lie. Under the last one, there is wild boar. In the wild boar’s stomach, there is a goose. Inside the goose, there is an egg. In that egg, lies the soul.&lt;/em&gt;” What can we say? 👀&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Some videogame bosses require atypical strategies to be defeated, too." src="https://godsip.club/img/psycho-mantis.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Some videogame bosses require atypical strategies to be defeated, too.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Despite folk tales, souls depicted as objects or animals are quite common: &lt;em&gt;totems&lt;/em&gt; are a great example of a deep connection between men and certain animals who are treated as &lt;strong&gt;sacred&lt;/strong&gt;. Dyaks of Borneo believed that humans have seven souls; it’s perfectly possible, from a primitive point of view, to trust one of them to a totem animal. The &lt;em&gt;indivisibility&lt;/em&gt; of the spirit is a “modern” religious dogma that never touched many civilisation, even if it was &lt;a href="https://philosophycourse.info/platosite/3schart.html" rel="noopener" target="_blank"&gt;considered by Plato&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="the-little-people-not-those-ones"&gt;The Little People (not &lt;em&gt;those ones&lt;/em&gt;!) &lt;a class="anchor" href="#the-little-people-not-those-ones"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;To close the circle, we could say that soul’s perception is quite… diversified around the globe, but indeed evident. Another form it can take is the same of the host – just smaller. Nootkas Indians believed that we have a small man, one-inch tall, in our head. If it holds a standing position, we are healthy; if it happens to lose its upright stance, the owner loses the senses.&lt;/p&gt;
&lt;p&gt;By combining some of the concepts above, we get interesting &lt;em&gt;cocktails&lt;/em&gt;. Among the Indian tribes of the Lower Fraser River, a person has four souls: the first one has the shape of a mannikin, while the others are the shadows of it. Moreover, until now we didn’t mention &lt;em&gt;trees&lt;/em&gt;! It is a firm conviction that a bond exists between men and woods, and there are so many fascinating stories.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Little People, a concept widely spread across the globe — &amp;ldquo;1Q84&amp;rdquo; by Degdeg on DeviantArt.com" src="https://godsip.club/img/1q84.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Little People, a concept widely spread across the globe — &amp;ldquo;1Q84&amp;rdquo; by Degdeg on DeviantArt.com&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Despite the credence that spirits inhabit the forestland, which is so vast that it will be addressed in a separate article, it is quite common to plant a fruit tree when a baby is born, even in modern times. Since then, the fate of both beings will be connected. Maori people had the same belief and used to place the baby’s umbilical cord and a tiny plant in a sacred field. In England, to cure rupture or rickets, children were passed through cracked ash trees; therefore, a sympathetic connection was established between them.&lt;/p&gt;
&lt;p&gt;Last, but not least, genealogical trees are quite spread. Though, it is told that some families have special affinities with particular physical trees, as the Scottish Edgwells. Nearby Castle of Dalhousie, an old oak tree stands from decades; it was told that every time a branch fell, a member of the family was about to pass away. Even Sir Walter Scott noted it &lt;a href="https://www.online-literature.com/walter_scott/journal-of-scott/39/" rel="noopener" target="_blank"&gt;in his journal&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h4 id="all-of-these-curiosities-have-been-extracted-from-the-golden-bough-by-james-g-frazer"&gt;&lt;em&gt;All of these curiosities have been extracted from&lt;/em&gt; The Golden Bough &lt;em&gt;by James G. Frazer.&lt;/em&gt; &lt;a class="anchor" href="#all-of-these-curiosities-have-been-extracted-from-the-golden-bough-by-james-g-frazer"&gt;#&lt;/a&gt;&lt;/h4&gt;&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Poor… &lt;em&gt;souls&lt;/em&gt;! Ah! 🥶&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Godsip Club</author><pubDate>Sat, 20 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://godsip.club/articles/better-shut-your-mouth/</guid></item><item><title>2021–11–20: Pinephone Pro – audio and modem power up support</title><link>https://xnux.eu/log/#056</link><author>megi's PinePhone Development Log</author><pubDate>Sat, 20 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#056</guid></item><item><title>Where I hope to go (just NOT with SubStack)</title><link>https://stop.zona-m.net/2021/11/where-i-hope-to-go-just-not-with-substack/</link><description>&lt;p&gt;Uncomfortable center, yes. Substack and friends, no.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 19 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/where-i-hope-to-go-just-not-with-substack/</guid></item><item><title>The Jaunt</title><link>https://arunmani.in/library/the-jaunt/</link><description>A future that is scary because of science.</description><author>Arun Mani</author><pubDate>Fri, 19 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/the-jaunt/</guid></item><item><title>Working at Amazon. A life changing experience.</title><link>https://swaits.com/incredible-amazon-experience/</link><description>&lt;p&gt;Today is my last effective day with Amazon - my official last day is next
Friday, November 26. But I'm taking most of next week off in observance of
Thanksgiving in the US. Thus, today is really it!&lt;/p&gt;
&lt;p&gt;Inevitably people will ask "why?" It's no secret that I planned to finish my
career at Amazon; after all, my hair isn't getting thicker or less gray as each
day goes by. And this is a damn good gig!&lt;/p&gt;
&lt;p&gt;But, life is full of unexpected twists and turns. Something in the "oh wow,
I gotta go try this" category landed in my inbox. More on that later. For now,
I want to take a moment to celebrate Amazon.&lt;/p&gt;
&lt;p&gt;This has been the best five years in my career, by far. Amazon was truly
life-changing for me. From getting to work with an amazing Principal Engineer
community, alongside industry luminaries, collaborating with engineering teams
to build distributed systems at unmatched scale, working with hundreds of
amazing, brilliant people in all kinds of roles who taught me new stuff
everyday, realizing the power of Amazon's Leadership Principles and Working
Backwards… I just can't say enough great things about the whole experience.&lt;/p&gt;
&lt;p&gt;Lots of people here greatly impacted me, professionally and personally. So
many, it's impossible to thank everyone by name, but I feel compelled to share
a few.&lt;/p&gt;
&lt;p&gt;I had the three best managers any person in the world could possibly have.
Infinite thanks to &lt;a href="https://www.linkedin.com/in/paulhorvath/" rel="external"&gt;Paul Horvath&lt;/a&gt;,
&lt;a href="https://www.linkedin.com/in/tony-bacos-56b3a5/" rel="external"&gt;Tony Bacos&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/margot-johnson-95b95398/" rel="external"&gt;Margot
Johnson&lt;/a&gt;. They showed me
what truly compassionate, empathetic, people-first leadership looks like. It
turns out you can deeply care about the people you work with, be extremely
respectful and nice to each other, and still get lots of stuff done!&lt;/p&gt;
&lt;p&gt;Along the way, I worked with many fantastic PEs, Sr. PEs, and even DEs. But
there were a handful that were always by my side supporting me. Thank you &lt;a href="https://www.linkedin.com/in/jenndox/" rel="external"&gt;Jenn
Lin&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/porter-schermerhorn-a00b141/" rel="external"&gt;Porter
Schermerhorn&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/seanblakey/" rel="external"&gt;Sean
Blakey&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/simonkjohnston/" rel="external"&gt;Simon
Johnston&lt;/a&gt;. They each taught me so
much more than they probably know.&lt;/p&gt;
&lt;p&gt;And a hat tip to &lt;a href="https://www.linkedin.com/in/natewiger/" rel="external"&gt;Nate Wiger&lt;/a&gt; who
started it all when he called me up a little over five years ago and said, "hey
dude I wrote a six pager and now Amazon's hiring in San Diego, want to check it
out?" Without him, I wouldn't be here today.&lt;/p&gt;
&lt;p&gt;How could any person be so fortunate to experience what I've experienced at
Amazon? I still don't know the answer to that. Everyday at work I want to pinch
myself and verbalize: why me??? Nevertheless, I'm so grateful for it all, and
not just because it's almost Thanksgiving. I'm genuinely, deeply grateful,
humbled, and forever appreciative.&lt;/p&gt;
&lt;p&gt;To everyone I worked with at Amazon, from the bottom of my heart, thank you! ❤️&lt;/p&gt;</description><author>swaits.com</author><pubDate>Fri, 19 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://swaits.com/incredible-amazon-experience/</guid></item><item><title>My approach to problem solving</title><link>https://jay.jvf.cc/posts/problem-solving/</link><description>&lt;p&gt;The last few days, I&amp;rsquo;ve had the opportunity to help a few different people solve problems. Regardless of the problem I
am up against, I use a few basic principles to solve it. I thought I&amp;rsquo;d share them here, in case they are useful to you.&lt;/p&gt;
&lt;h2 id="reduce-your-reproduction"&gt;Reduce your reproduction&lt;/h2&gt;
&lt;p&gt;Reproduce your problem, then reduce your reproduction. What do I mean by reduce? I mean, try to remove as much of the
extra stuff around your problem as possible &amp;ndash; this may be as simple as taking the time to run only the broken test
case instead of all of them (or writing a new test case to exercise your bug).&lt;/p&gt;</description><author/><pubDate>Fri, 19 Nov 2021 01:36:52 GMT</pubDate><guid isPermaLink="true">https://jay.jvf.cc/posts/problem-solving/</guid></item><item><title>2021–11–19: Pinephone Pro LCD panel</title><link>https://xnux.eu/log/#055</link><author>megi's PinePhone Development Log</author><pubDate>Fri, 19 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#055</guid></item><item><title>Entropy of an ideal gas with coarse-graining</title><link>https://bytepawn.com/entropy-of-an-ideal-gas-with-coarse-graining.html</link><description>&lt;p&gt;I show the first steps of how to arrive at a definition of entropy for a monatomic ideal gas modeled as hard billiard balls. &lt;br /&gt;&lt;br /&gt;&lt;img alt="." src="/images/ideal_gas_cg_4.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Fri, 19 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/entropy-of-an-ideal-gas-with-coarse-graining.html</guid></item><item><title>Our URGENT need for simplicity, summarized</title><link>https://stop.zona-m.net/2021/11/our-urgent-need-for-simplicity-summarized/</link><description>&lt;p&gt;It&amp;rsquo;s high time to listen to Einstein, really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 18 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/our-urgent-need-for-simplicity-summarized/</guid></item><item><title>Achieving 10 GB/s Scanning Rate in a Single Cloud VM</title><link>https://tanelpoder.com/posts/achieving-over-10gbytes-scanning-rate-in-the-cloud/</link><description>&lt;p&gt;You may remember my &lt;a href="https://tanelpoder.com/posts/testing-the-silk-platform/"&gt;previous article&lt;/a&gt; about high-performance I/O for cloud databases, where I tested out the Silk Platform and got a single Azure VM to do over &lt;strong&gt;5 GB/s&lt;/strong&gt; (gigaBytes/s) of I/O for my large database query workload. The magic was in using iSCSI over the switched cloud &lt;em&gt;compute network&lt;/em&gt; to multiple &amp;ldquo;data VMs&amp;rdquo; at the virtualized storage layer for high I/O throughput and scalability.&lt;/p&gt;
&lt;p&gt;Well, the Silk folks are at it again and Chris Buckel (&lt;a href="https://twitter.com/flashdba"&gt;@flashdba&lt;/a&gt;) just sent me screenshots of similar test runs using the latest Azure &amp;ldquo;v5&amp;rdquo; instance as the single large database node and they achieved over &lt;strong&gt;10 GB/s scanning rate&lt;/strong&gt; for reads and &lt;strong&gt;6 GB/s&lt;/strong&gt; write rate with large I/Os!&lt;/p&gt;</description><author>Tanel Poder Blog</author><pubDate>Thu, 18 Nov 2021 19:52:00 GMT</pubDate><guid isPermaLink="true">https://tanelpoder.com/posts/achieving-over-10gbytes-scanning-rate-in-the-cloud/</guid></item><item><title>Working in Castle Legacy</title><link>http://evantravers.com/articles/2021/11/18/legacy-software-is-a-tell/</link><description>&lt;p&gt;&lt;img alt="" src="https://images.evantravers.com/articles/2021/11/castle.jpg" /&gt;&lt;/p&gt;&lt;p&gt;Certain terrain lends itself to human flourishing. Food, water, shelter… often next to a source of wealth or trade.&lt;/p&gt;
&lt;p&gt;&lt;img alt="lovely terrain, ready for business" src="https://images.evantravers.com/articles/2021/11/castle-1.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Because the terrain drives demand humans settle, destroy, and resettle in the same desirable locations… often over centuries.&lt;/p&gt;
&lt;p&gt;If we look at the historical record, the prosperity of a good location is often marked by violence. There are still layers of burned ash and melted nails under Rome, and spearheads and arrowheads under most cities.&lt;/p&gt;
&lt;p&gt;&lt;img alt="prosperity is often accompanied by conflict" src="https://images.evantravers.com/articles/2021/11/castle-2.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Conflict doesn't always bring progress. Sometimes a rival civilization makes an example of a city, or their own internal conflict causes them to fail. The ashes and wreckage too find their way into the earth.&lt;/p&gt;
&lt;p&gt;&lt;img alt="unsolved conflict causes ruin" src="https://images.evantravers.com/articles/2021/11/castle-3.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;But if the ground is good, someone will always start again.&lt;/p&gt;
&lt;p&gt;&lt;img alt="and you have to clean up an restart on the ashes" src="https://images.evantravers.com/articles/2021/11/castle-4.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;This cycle can continue in desirable locations for centuries. As successive generations or civilizations build on top of the infrastructure left by the previous occupants, the future settlement is shaped both by the natural contours of the land and by the remaining previous infrastructure. The pile of dirt that contains all these layers of histories is called a “tell.”&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-tell" id="fnref-tell"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="but what you build is informed by what is there before" src="https://images.evantravers.com/articles/2021/11/castle-5.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Many great European cities contain such tells. Many owe their shape and character to medieval or even Roman architecture and decisions: Paris’s 14th century sewers&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-sewer" id="fnref-sewer"&gt;2&lt;/a&gt;&lt;/sup&gt;, or Roman aqueducts&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-aqua" id="fnref-aqua"&gt;3&lt;/a&gt;&lt;/sup&gt; and roads and bridges&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-bridge" id="fnref-bridge"&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="and history will repeat itself on grander scales" src="https://images.evantravers.com/articles/2021/11/castle-6.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The original terrain or the changes people made to that terrain drives any new construction. The new builders will take to jumpstart their progress. It's no surprise that the surviving bits of those old European cities that stay maintained and used are invisible infrastructure. Sometimes the original plan conflicts with our mental image of the construction in surprising ways.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-alley" id="fnref-alley"&gt;5&lt;/a&gt;&lt;/sup&gt; Other times you'll see the same triumphs and mistakes repeated time and time again. This makes sense… there are only a few places to build a port, defend a tower, or build a road.&lt;/p&gt;
&lt;p&gt;&lt;img alt="what is below is just as true and real as what is above ground" src="https://images.evantravers.com/articles/2021/11/castle-9.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Just like the physical construction a legacy software code case contains contains historical layers… where each successive attempt and architecture on the same business domain borrows abstractions and concepts from the previous iterations.&lt;/p&gt;
&lt;p&gt;&lt;img alt="you knew we weren't talking about physical history, right?" src="https://images.evantravers.com/articles/2021/11/castle-8.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Digging through the digital &amp;quot;tell&amp;quot;s that I've worked on before, I've found these layers in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Routers / URLs&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-url" id="fnref-url"&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;Namespaces&lt;/li&gt;
&lt;li&gt;Business Rules (Permissions!)&lt;/li&gt;
&lt;li&gt;Glue Code&lt;/li&gt;
&lt;li&gt;Legacy APIs&lt;/li&gt;
&lt;li&gt;Code Specific to Old Clients or Business Models&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thinking about software as layers of a city&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-metaphor" id="fnref-metaphor"&gt;7&lt;/a&gt;&lt;/sup&gt; has helped me a lot. When working in a legacy software “tell,” I’ll find myself shifting my mindset from an engineer to that of an archaeologist. A digital archaeologist understands that the previous occupants of this terrain might have had similar goals, but a different language, culture, belief system that drove their decisions. They’ll take a human perspective to the remains they find, and use some forensic tools&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-spelunking" id="fnref-spelunking"&gt;8&lt;/a&gt;&lt;/sup&gt; to try and build a picture of the story.&lt;/p&gt;
&lt;p&gt;When working in a digital &amp;quot;tell&amp;quot;, it is more important to learn to dig than to build. It's partly why &lt;a href="http://evantravers.com/articles/tags/vim/"&gt;I love vim&lt;/a&gt;: it's whole paradigm centers around the critical skill of editing what is already there rather than focusing on writing from scratch.&lt;/p&gt;
&lt;p&gt;I've also carried this thinking into writing &amp;quot;new&amp;quot; code. What kind of decisions are going to be so ingrained into this &amp;quot;terrain&amp;quot; that I should really document for future generations.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-tell"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Tell_(archaeology)"&gt;Tell (archaeology) - wikipedia.org&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-tell"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-sewer"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Paris_sewers"&gt;Paris sewer - wikipedia.org&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-sewer"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-aqua"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Aqua_Virgo"&gt;Aqua Virgo - Wikipedia.org&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-aqua"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-bridge"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Ponte_di_Tiberio_(Rimini)"&gt;Ponte di Tiberio (Rimini)&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-bridge"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-alley"&gt;
&lt;p&gt;My favorite example: NYC doesn't really have alleys in Manhattan because of the designed grid. &lt;a href="https://99percentinvisible.org/episode/mini-stories-volume-6/"&gt;Cortland Alley - 99percentinvisible.com&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-alley"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-url"&gt;
&lt;p&gt;I once read about a decision for Twitter URIs that slowed down response time… as the platform grew, they couldn't undo the change without breaking the value of the platform, so they were stuck with the decision. (Trying to find the source.) &lt;a class="footnote-backref" href="#fnref-url"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-metaphor"&gt;
&lt;p&gt;I think I have to credit Vernor Vinge's book &lt;a href="https://en.wikipedia.org/wiki/A_Deepness_in_the_Sky"&gt;A Deepness in the Sky&lt;/a&gt; for planting this image in my head. Thinking about this process in software development as building a continous stack over &lt;em&gt;millenia&lt;/em&gt;… fascinating. Great book. &lt;a class="footnote-backref" href="#fnref-metaphor"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-spelunking"&gt;
&lt;p&gt;&lt;a href="http://evantravers.com/articles/2018/09/28/code-spelunking/"&gt;Spelunking in Git - evantravers.com&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-spelunking"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Thu, 18 Nov 2021 12:53:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/11/18/legacy-software-is-a-tell/</guid></item><item><title>Hosting</title><link>https://liza.io/hosting/</link><description>&lt;p&gt;I&amp;rsquo;ve tried quite a few blogging platforms over the years: Wordpress, Squarespace, even Ghost at some point, I think.&lt;/p&gt;
&lt;p&gt;I opted for self-hosting years ago, always on S3, first via Octopress and now (and for the last few years) Hugo.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Thu, 18 Nov 2021 08:02:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/hosting/</guid></item><item><title>useRainbow()</title><link>https://sonnet.io/posts/use-rainbow/</link><description>&lt;p&gt;Hi there! It's been a long time, I know. I took a break from work and started working on some small, personal projects (toys).&lt;/p&gt;
&lt;p&gt;One of those small projects is &lt;a href="https://potato.horse/"&gt;potato.horse&lt;/a&gt; where I keep all of my doodles, visual short stories and jokes. Check it out!&lt;/p&gt;
&lt;p&gt;However, this post is not about my break from work, other experiments, or the site itself. People seem to like one particular technique I used in the design, notably, the backround effect applied when the user browses the content:&lt;/p&gt;
&lt;p&gt;&lt;video controls="" loop="" src="https://sonnet.io/images/use-rainbow/use-rainbow-scroll-animation.mp4"&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Some asked me how this effect was implemented (including going as far as reading the minified code, which is very flattering).&lt;/p&gt;
&lt;p&gt;So, here's a quick gist, followed up with some context:&lt;/p&gt;
&lt;pre class="language-typescript"&gt;&lt;code class="language-typescript"&gt;&lt;span class="token keyword"&gt;export&lt;/span&gt; &lt;span class="token keyword"&gt;const&lt;/span&gt; &lt;span class="token function-variable function"&gt;useRainbowBg&lt;/span&gt; &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token operator"&gt;=&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="token function"&gt;useEffect&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token operator"&gt;=&gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class="token keyword"&gt;const&lt;/span&gt; &lt;span class="token function-variable function"&gt;cb&lt;/span&gt; &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token operator"&gt;=&gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;br /&gt;            &lt;span class="token keyword"&gt;const&lt;/span&gt; viewportHeight &lt;span class="token operator"&gt;=&lt;/span&gt; window&lt;span class="token punctuation"&gt;.&lt;/span&gt;innerHeight&lt;br /&gt;            &lt;span class="token keyword"&gt;const&lt;/span&gt; contentHeight &lt;span class="token operator"&gt;=&lt;/span&gt; document&lt;span class="token punctuation"&gt;.&lt;/span&gt;body&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;.&lt;/span&gt;height&lt;br /&gt;            &lt;span class="token keyword"&gt;const&lt;/span&gt; viewportsPerRotation &lt;span class="token operator"&gt;=&lt;/span&gt; Math&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;min&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;br /&gt;                &lt;span class="token number"&gt;3&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt;&lt;br /&gt;                contentHeight &lt;span class="token operator"&gt;/&lt;/span&gt; viewportHeight&lt;br /&gt;            &lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="token keyword"&gt;const&lt;/span&gt; from &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token number"&gt;51&lt;/span&gt;&lt;br /&gt;            &lt;span class="token keyword"&gt;const&lt;/span&gt; progress &lt;span class="token operator"&gt;=&lt;/span&gt;&lt;br /&gt;                window&lt;span class="token punctuation"&gt;.&lt;/span&gt;scrollY &lt;span class="token operator"&gt;/&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;viewportHeight &lt;span class="token operator"&gt;*&lt;/span&gt; viewportsPerRotation&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class="token keyword"&gt;const&lt;/span&gt; h &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;from &lt;span class="token operator"&gt;+&lt;/span&gt; &lt;span class="token number"&gt;360&lt;/span&gt; &lt;span class="token operator"&gt;*&lt;/span&gt; progress&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token operator"&gt;%&lt;/span&gt; &lt;span class="token number"&gt;360&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;            document&lt;span class="token punctuation"&gt;.&lt;/span&gt;body&lt;span class="token punctuation"&gt;.&lt;/span&gt;style&lt;span class="token punctuation"&gt;.&lt;/span&gt;backgroundColor &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token template-string"&gt;&lt;span class="token template-punctuation string"&gt;`&lt;/span&gt;&lt;span class="token string"&gt;hsl(&lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;h&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;deg, 100%, 50%)&lt;/span&gt;&lt;span class="token template-punctuation string"&gt;`&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;br /&gt;        window&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;addEventListener&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token string"&gt;'scroll'&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt; cb&lt;span class="token punctuation"&gt;,&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; passive&lt;span class="token operator"&gt;:&lt;/span&gt; &lt;span class="token boolean"&gt;true&lt;/span&gt; &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class="token keyword"&gt;return&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token operator"&gt;=&gt;&lt;/span&gt; window&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;removeEventListener&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token string"&gt;'scroll'&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt; cb&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In short, I map the &lt;em&gt;scroll position&lt;/em&gt; into the &lt;em&gt;hue&lt;/em&gt; in the HSL colour notation. Let's break this down.&lt;/p&gt;
&lt;h3&gt;Colour models&lt;/h3&gt;
&lt;p&gt;There are many ways of describing colours in CSS, with the two most common ones being RGB (left) and HSL (right):&lt;/p&gt;
&lt;div class="embed example-rgb-hsl"&gt;&lt;/div&gt;
&lt;p&gt;RGB is an additive colour palette. This means that mixing 100% of red, green and blue will produce white, mixing 100% red and 100% green but 0% blue, will produce yellow and so on. This is different from, say, using oil paint or the &lt;a href="https://en.wikipedia.org/wiki/CMYK_color_model"&gt;CMYK colour model&lt;/a&gt;, where the resulting tone would be black(-ish)&lt;a class="footnote" href="https://sonnet.io/posts/use-rainbow/#footnote-1"&gt;1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We're used to this approach because it's easy to describe in code but specifying colours in terms of hue, saturation and luminosity seems more natural, especially if you come from a design background, or... you know, are a human being using a human language.&lt;/p&gt;
&lt;p&gt;We've gotten used to RGB as developers, but in spoken language using it would feel unnatural and confusing. &lt;a href="http://facade.photo/"&gt;Façade&lt;/a&gt; would be very hard to use in RGB.&lt;/p&gt;
&lt;p&gt;On the other hand, HSL can often be much more intuitive to work with. For instance if I want to make a colour slightly colder, I can just move the hue slider a bit towards blue and I should get closer to what I have in mind. With RGB if we make the colour appear colder by including more blue, the resulting tone will be a bit brighter as the blue component contributes to the overall lightness. This means that you'd have to lower the red and green values to compensate.&lt;/p&gt;
&lt;p&gt;To see how this works in practice, try maxing out the blue colour in the example below.&lt;/p&gt;
&lt;div class="embed example-hue-shift-rgb"&gt;&lt;/div&gt;
&lt;p&gt;The first thing that stands out is that all tones are shifted towards blue and the overall brightness of the picture is increased. In the case of the effect we're discussing, that would be undesirable.&lt;/p&gt;
&lt;p&gt;Now, let's try to do the same with the HSL colour circle. Drag the slider to the left, by &lt;strong&gt;ca. 90 degrees:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="embed example-hue-shift-hsl"&gt;&lt;/div&gt;
&lt;p&gt;In this scenario using HSL not only &lt;em&gt;turns Susan into a vampire&lt;/em&gt;, but also maintains a similar&lt;a class="footnote" href="https://sonnet.io/posts/use-rainbow/#footnote-2"&gt;2&lt;/a&gt; level of brightness. And that's exactly what I'm looking for.&lt;/p&gt;
&lt;p&gt;So, what did I mean by saying:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;How does this work? In short, I map the &lt;em&gt;scroll position&lt;/em&gt; into the &lt;em&gt;hue&lt;/em&gt; in the HSL colour notation.
&lt;cite&gt;Rafal, 2 days earlier&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I meant that every time we detect a scroll event, I try to map it to an angle on the hue circle:&lt;/p&gt;
&lt;div class="embed example-scroll"&gt;&lt;/div&gt;
&lt;p&gt;I didn't want to start with red as it would make me hungry and the base yellow fits the design a bit better, so I applied a small initial shift, hence &lt;code&gt;const from = 51&lt;/code&gt; set as the initial offset.&lt;/p&gt;
&lt;p&gt;And, as far as the basic implementation goes, that's it!&lt;/p&gt;
&lt;p&gt;Now, there are three other areas of improvement:&lt;/p&gt;
&lt;h3&gt;Performance&lt;/h3&gt;
&lt;p&gt;We're triggering a repaint on every scroll, so I was a bit worried that older mobile devices, or even some hi-end laptops plugged in to 4k screens might not be able to maintain solid 60fps. But, I'm happy with the results so far. Using &lt;a href="https://developers.google.com/web/updates/2016/06/passive-event-listeners"&gt;passive event listeners&lt;/a&gt; provided a bit of a boost, especially on mobile.&lt;/p&gt;
&lt;p&gt;When/if I realise that performance is a problem, especially with more content down the line, I'll probably focus on these areas first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;removing the unnecessary call to &lt;code&gt;getBoundingClientRect&lt;/code&gt; on every scroll handler call&lt;/li&gt;
&lt;li&gt;deferring or throttling background colour changes using &lt;code&gt;requestAnimationFrame&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I expect the first improvement to have some impact, but the benefits of the second one should be negligible.&lt;/p&gt;
&lt;p&gt;Measure before optimising. Obsessing about the performance only makes sense when issues become noticeable, be it through a drop in framerate or battery impact. Your iPhone Pro has more computing power than many low-end laptops, so it's a good idea to test on those devices too. It's good to have a crappy old Android phone exacly for that purpose if you can spare a few quid.&lt;/p&gt;
&lt;h3&gt;Perceptually uniform colour spaces&lt;/h3&gt;
&lt;p&gt;You might've noticed that in the previous illustrations some fully saturated colours seemed darker than the others. That's because the colour spaces we use normally when coding don't reflect the way the human eye works. I'll leave the in-depth explanation to &lt;a href="https://programmingdesignsystems.com/color/perceptually-uniform-color-spaces/"&gt;someone much more experienced than me&lt;/a&gt;, but suffice to say (gross oversimplification alert!) that generally the same amount of red/green/yellow will appear brighter than blue. This means that in some cases the text on the page will be harder to read.&lt;/p&gt;
&lt;div class="use-rainbow__rainbow"&gt;
  &lt;div class="use-rainbow__column use-rainbow__column--hsl"&gt;
    &lt;div class="use-rainbow__column-title"&gt;HSL&lt;br /&gt;⬇&lt;/div&gt;
    &lt;div class="use-rainbow__swatches"&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
   &lt;div class="use-rainbow__column use-rainbow__column--hsluv"&gt;
    &lt;div class="use-rainbow__swatches"&gt;
      &lt;div style="background-color: #f75782;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #eb6a2c;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #c0852b;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #a5902b;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #88992b;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #56a22b;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #2da46b;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #2ea28b;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #2fa09f;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #319db4;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #3498d6;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #7e86f4;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #c16af4;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #f43ae8;"&gt;&lt;/div&gt;
      &lt;div style="background-color: #f64cb6;"&gt;&lt;/div&gt;
    &lt;/div&gt;
      &lt;div class="use-rainbow__column-title"&gt;⬆&lt;br /&gt;HSLUV (perceptually uniform) &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;For now&lt;/em&gt;, this isn't an issue as I've &lt;em&gt;just&lt;/em&gt; put this thing online and titles serve a secondary purpose. But there's a solution to the problem and it's not overly complicated: use a perceptually uniform colour space. There's a bunch of libraries that do it out of the box, both in JS/TS and CSS/SASS/&lt;code&gt;&amp;lt;pick your CSS flavour here&amp;gt;&lt;/code&gt;. &lt;a href="https://www.hsluv.org/"&gt;&lt;code&gt;hsluv&lt;/code&gt;&lt;/a&gt; seems like a good starting point.&lt;/p&gt;
&lt;h3&gt;Accessibility&lt;/h3&gt;
&lt;p&gt;Note that I'll be focusing on the visual effect itself and not discussing the rest of the site (e.g. alt tags, document structure, etc...). I'd like to focus on contrast, colour blindness and people who rely on &lt;code&gt;prefers-reduced-motion&lt;/code&gt;. The site is a living document, there's always so much to improve. For instance, contrast can be an issue in a few, non-critical places. I'm happy to accept feedback and implement it: &lt;a href="https://sonnet.io/posts/hi"&gt;hit me up!&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Colour blindness&lt;/h3&gt;
&lt;p&gt;I wanted to make sure that the effect doesn't break the site completely for people with colour blindness. So I focused on the most common types: Deuteranomaly and Protanomaly (red-green colour blindness), but also ran wider tests. I used Photoshop and &lt;a href="https://github.com/oftheheadland/Colorblindly"&gt;Colorblindly (Chrome extension)&lt;/a&gt; for some rudimentary checks.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;prefers-reduced-motion&lt;/code&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;prefers-reduced-motion&lt;/strong&gt; CSS media feature is used to detect if the user has requested that the system minimize the amount of non-essential motion it uses.
&lt;cite&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion"&gt;MDN&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This site doesn't contain many animations (besides the &lt;em&gt;Little Sausage Angels&lt;/em&gt; you'll see if you hit 'Share'), but I was wondering if people who rely on &lt;code&gt;prefers-reduced-motion&lt;/code&gt; would like the background colour to stay constant.&lt;/p&gt;
&lt;p&gt;The short answer is: I don't know. My intuition is that rotating colours doesn't really qualify as motion, but my experience and understanding of the problem is, to say the least, limited. In situations like this I'd rather depend on user research instead of guesses.&lt;/p&gt;
&lt;p&gt;Luckily, the site had its five minutes of fame on Reddit which proved to be a decent opportunity to collect feedback. None of the users brought up an issue with the background effect so far. I'm also lucky enough to know a bunch of accessibility specialists, such as &lt;a href="https://twitter.com/a_sandrina_p"&gt;Sandrina Pereira&lt;/a&gt;. Her suggestion was that a) background animations definitely qualify as motion, b) perhaps the effect feels natural because it's a direct result of a user interaction.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The late 90s/Geocities web felt playful and weird. It was fun in an uninhibited, somewhat less performative, way. I wanted to incorporate some of this look and feel in the site. But still, I didn't want to make it feel esoteric to the point where you'd need to up your hipsterdom-level to 9000 and browse it exclusively throught Netscape 7. All of that, while listening to the new Nirvana: Unplugged.&lt;/p&gt;
&lt;p&gt;I still wanted decent UX on mobile and desktop, and some space for easter eggs (something you can't do when living in the strange and abusive relationship with social media we've grown so accustomed to).&lt;/p&gt;
&lt;p&gt;As a kid I had build 6 websites before I even got access to the Internet for the first time. Now, after being burned out for 3 years, even considering changing my job, it was the first time I genuinely enjoyed coding. Fuck, I forgot how much fun it was.&lt;/p&gt;
&lt;p&gt;Now, go out, pet your cat and make some shit!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;PS. Check &lt;a href="https://www.cameronsworld.net/"&gt;https://www.cameronsworld.net/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PPS. The code for interactive diagrams can be found &lt;a href="https://github.com/paprikka/use-rainbow-article-examples"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Footnotes&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span id="footnote-1"&gt;&lt;/span&gt;hence the K component in CMYK meaning black. Using B would be confusing as it means blue in other colour models.&lt;/li&gt;
&lt;li&gt;&lt;span id="footnote-2"&gt;&lt;/span&gt;it's not perfect, since the perceptual colour space differs from what's described using RGB/HSL.&lt;/li&gt;
&lt;/ol&gt;</description><author>Rafał Pastuszak</author><pubDate>Thu, 18 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://sonnet.io/posts/use-rainbow/</guid></item><item><title>run-pr.sh</title><link>https://maxleiter.com/notes/run-pr</link><description>&lt;p&gt;Today I found myself once again trying to run a pull request that was created in a different GitHub repository. If it was in the same repo, it would be a simple `git checkout`, however because GitHub insisted on the creation of &amp;quot;forks&amp;quot; I now need to go through the extra steps of fetching that specific ref. Here's what I do:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;git fetch https://github.com/${team}/${respository}.git refs/pull/${prId}/head
git checkout FETCH_HEAD
git rebase master # or main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just replace `team`, `repository` and `prId` and you're all set. &lt;/p&gt;
&lt;p&gt;I picked this script up from The Lounge: &lt;a href="https://github.com/thelounge/thelounge/blob/master/scripts/run-pr.sh" rel="noopener noreferrer" target="_blank"&gt;https://github.com/thelounge/thelounge/blob/master/scripts/run-pr.sh&lt;/a&gt;. It's a more complete example if you want to start using a script like this. Notice the &lt;a href="https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html" rel="noopener noreferrer" target="_blank"&gt;\`&lt;/a&gt; and bash substitution (&lt;a href="https://stackoverflow.com/a/3898681" rel="noopener noreferrer" target="_blank"&gt;\`&lt;/a&gt;) at the end of it —  these allow you to pass in command-line arguments to `yarn start` (or whatever your run script is).&lt;/p&gt;</description><author>Max Leiter</author><pubDate>Thu, 18 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://maxleiter.com/notes/run-pr</guid></item><item><title>Individuals Matter</title><link>https://www.danstroot.com/posts/2021-11-18-individuals-matter</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/people.png" /&gt;&lt;br /&gt;&lt;br /&gt;One of the most common mistakes I see is thinking of people as interchangeable, as if “who” doesn’t matter. Companies will plan out the next few quarters of work and then assign some number of people to it, one person for one quarter to a project, two people for three quarters to another, etc. The roadmap creation and review process perpetuates the polite fiction that people are interchangeable, that people can be generically abstracted over.&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-11-18-individuals-matter"&gt;Individuals Matter&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Thu, 18 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-11-18-individuals-matter</guid></item><item><title>The epic notability battle between pornstars and scientists</title><link>https://stop.zona-m.net/2021/11/the-epic-notability-battle-between-pornstars-and-scientists/</link><description>&lt;p&gt;I wonder if it still going on&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 17 Nov 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/the-epic-notability-battle-between-pornstars-and-scientists/</guid></item><item><title>Improving the code from the official Go RESTful API tutorial</title><link>https://benhoyt.com/writings/web-service-stdlib/</link><description>My re-implementation of the code from the official Go tutorial 'Developing a RESTful API with Go and Gin', using only the standard library, adding tests, and fixing issues.</description><author>Ben Hoyt's technical writing</author><pubDate>Tue, 16 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://benhoyt.com/writings/web-service-stdlib/</guid></item><item><title>Watching the World Bank must become easier</title><link>https://stop.zona-m.net/2021/11/watching-the-world-bank-must-become-easier/</link><description>&lt;p&gt;Especially because now it IS much easier.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 16 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/watching-the-world-bank-must-become-easier/</guid></item><item><title>Funding Not Secured</title><link>https://www.nothingeasyaboutthis.com/funding-not-secured/</link><description>I can't fund an acquisition. This post outlines why this hasn't worked, and shares an investment model that I think someone could take up.</description><author>Nothing Easy About This</author><pubDate>Tue, 16 Nov 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://www.nothingeasyaboutthis.com/funding-not-secured/</guid></item><item><title>VPN Advice (November 2021 Edition)</title><link>https://ivymike.dev/vpn-advice-november-2021-edition.html</link><description>&lt;p&gt;I had a short discussion with a friend about his VPN needs; here's a summary of that discussion.&lt;/p&gt;
&lt;h1&gt;Executive summary:&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Most users don't need a VPN for privacy anymore; the majority of your web traffic is encrypted by default.&lt;/li&gt;
&lt;li&gt;If you need a VPN so you can access servers at …&lt;/li&gt;&lt;/ol&gt;</description><author>IvyMike.dev</author><pubDate>Tue, 16 Nov 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ivymike.dev/vpn-advice-november-2021-edition.html</guid></item><item><title>Learn in public</title><link>https://nicolaiarocci.com/learn-in-public/</link><description>&lt;p&gt;Today I searched the internet for something, and the first result I got from
&lt;a href="https://duckduckgo.com/"&gt;@duckduckgo&lt;/a&gt; was a note I wrote months ago to my future self; how meta is
that.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolaiarocci.com/images/duckduckgo-search.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.swyx.io/learn-in-public/"&gt;Learn in public&lt;/a&gt;, it gives superpowers&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Also, in recent years, adopting
&lt;a href="https://indieweb.org/POSSE"&gt;POSSE&lt;/a&gt; was the best thing I did for my personal development.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I should do better. Post more &lt;a href="https://nicolaiarocci.com/tags/til/"&gt;TILs&lt;/a&gt;, for example.
[rss]: &lt;a href="https://nicolaiarocci.com/index.xml"&gt;https://nicolaiarocci.com/index.xml&lt;/a&gt;
[tw]: &lt;a href="http://twitter.com/nicolaiarocci"&gt;http://twitter.com/nicolaiarocci&lt;/a&gt;
[nl]: &lt;a href="https://buttondown.email/nicolaiarocci"&gt;https://buttondown.email/nicolaiarocci&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 16 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/learn-in-public/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #46</title><link>https://justingarrison.com/123dev/2021-11-16-123dev-46/</link><description>&lt;img alt="A golfer makes a hole in one by skipping the ball over water" src="https://justingarrison.com/img/123dev/46-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="skill"&gt;Skill&lt;/h2&gt;
&lt;p&gt;Some developers are very skilled. They can do things seemingly impossible for others. It might be developing a feature faster than others, debugging a hard problem, or reaching new scale with systems.&lt;/p&gt;
&lt;p&gt;Many times those skills are accompanied by a decent amount of luck. While a skilled developer can replicate something with high accuracy, it&amp;rsquo;s not always skill alone that helped the first time.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 16 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-11-16-123dev-46/</guid></item><item><title>2021–11–16: A bunch of mostly Pinephone Pro news</title><link>https://xnux.eu/log/#054</link><author>megi's PinePhone Development Log</author><pubDate>Tue, 16 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#054</guid></item><item><title>Engagement Insider Threat</title><link>https://ztoz.blog/posts/engagement-insider-threat/</link><description>&lt;p&gt;Warning: Contains spoilers for the season 6, episode 5 &amp;ldquo;Tethics&amp;rdquo; of the show &lt;em&gt;Silicon Valley&lt;/em&gt; which aired in 2019.&lt;/p&gt;
&lt;p&gt;In &amp;ldquo;Tethics&amp;rdquo;, two managers of Pied Piper, Gilfoyle and Monica, are threatened by HR due to their low employee engagement scores &amp;mdash; there&amp;rsquo;s a consensus among the employees that the two managers are jerks. They both promise to raise their scores, but stupidly promise to raise the scores from &amp;ldquo;hate&amp;rdquo; to &amp;ldquo;love&amp;rdquo; within a week. Faced with this impossible task, they hatch a plan to use social engineering to steal employee&amp;rsquo;s passwords and then, using the compromised employee&amp;rsquo;s accounts, modify the engagement scores to show that employees now love them. How realistic is this plan? How can we mitigate these kinds of attacks? Let&amp;rsquo;s use a threat modeling approach to answer both.&lt;/p&gt;
&lt;p&gt;More formally, the attacker&amp;rsquo;s objective is to modify the engagement data to benefit themselves. As managers within the company, they will know:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;what system is being used to collect and analyze the data (we will assume this is commercial, non-self hosted SaaS software),&lt;/li&gt;
&lt;li&gt;who (in their company) would be administering the project,&lt;/li&gt;
&lt;li&gt;who would be providing engagement scores (the respondents), and&lt;/li&gt;
&lt;li&gt;the high-level project plan and process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The data needs to be modified surreptitiously; if the engagement data is deleted or is exposed as doctored, the attackers may have gained a short reprieve but haven&amp;rsquo;t accomplished their goal of appearing as better managers. In addition, we will assume the attackers do not have advanced technical skills to attack the SaaS software directly&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; and the time budget is closer to days than months.&lt;/p&gt;
&lt;p&gt;The proposed attack is two-stage:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Obtain employee passwords by using social engineering/knowledge of the employee&amp;rsquo;s backgrounds to guess passwords&lt;/li&gt;
&lt;li&gt;Leveraging those accounts, change the employee&amp;rsquo;s ratings for the managers in the engagement monitoring system&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the STRIDE threat model&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;, this is a Spoofing threat (obtaining account access) that can then allow a Tampering threat (modifying engagement score data). The asset to be protected in the first stage are passwords and, in the second stage, the employee ratings. Spoofing threats are mitigated via authentication and tampering threats are mitigated via ensuring data integrity.&lt;/p&gt;
&lt;p&gt;(If you find this kind of analysis interesting, I strongly recommend Adam Shostack &lt;em&gt;Threat Modeling&lt;/em&gt;&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; book. Also, Ross Anderson&amp;rsquo;s &lt;em&gt;Security Engineering&lt;/em&gt;&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; covers a broader systems-approach to security; the two books are complementary.)&lt;/p&gt;
&lt;h1 id="spoofing-mitigations"&gt;Spoofing mitigations&lt;/h1&gt;
&lt;p&gt;While the employee engagement responses may be anonymized or otherwise have some sort of privacy protections, the actual act of providing feedback is not anonymous. Instead, the collection system requires some ability to know who the respondent is in order to tie the responses to the respondent&amp;rsquo;s place in the organization. (Engagement studies often include questions specific to certain roles or organizations and results need to be aggregated under the appropriate manager.) Thus, the collection process requires respondent authentication. Within the episode, the authentication is handled via passwords, so that is where we&amp;rsquo;ll focus our model.&lt;/p&gt;
&lt;h2 id="password-policyrecovery-policy"&gt;Password policy/recovery policy&lt;/h2&gt;
&lt;p&gt;Using personal information to aid in guessing a user&amp;rsquo;s password is not a novel attack; it&amp;rsquo;s a standard &lt;a href="https://tvtropes.org/pmwiki/pmwiki.php/Main/ThePasswordIsAlwaysSwordfish"&gt;trope in fiction&lt;/a&gt; but one that still works in the real world. As a straight-forward attack, it relies on users choosing poor passwords and on the systems being vulnerable to guessing attacks. Better passwords can be encouraged via the use of password managers (which reduces the pain of a unique password per site), single sign-on (which increases the value of a strong password), or policies that encourage higher entropy passwords (although onerous policies can encourage users to use their creativity to perversely choose worse passwords).&lt;/p&gt;
&lt;p&gt;Additionally, if the system locks out (even temporarily) an account after a number of bad logins, this will slow the rate an attacker can guess passwords. Within this scenario, the managers do not have a large amount of time to guess passwords and there is no indication they can guess passwords &amp;ldquo;off-line&amp;rdquo;, so a good authentication system can throttle their attack.&lt;/p&gt;
&lt;p&gt;An alternative interpretation of their attack is they are not attacking passwords directly, but rather using personal details to attack the password recovery mechanism. Password recovery mechanisms typically require the user to answer a handful of personal questions and, if they answer correctly, the system will initiate a password change. Recovery questions often suffer from a low number of possible answers and answers can often be sourced with some public background checks. Good password recovery systems will notify the user (ideally through multiple channels) about the password change, so this can cause the attack to become known if the users see the notifications and decide to flag them as improper. However, not all recovery systems include these features, so the attackers could maintain their cover.&lt;/p&gt;
&lt;h2 id="two-factor-authentication"&gt;Two-factor authentication&lt;/h2&gt;
&lt;p&gt;The three-factors are something you know, something you have, and something you are. Two-factor authentication requires two of these factors, normally a password and a key (often via a multi-factor authentication app on a phone or a physical device). If providing an engagement score requires two-factor authentication, then the cost of spoofing dramatically increases because a password, by itself, is not useful. However, this also increases the cost to the respondent (and  the complexity of the collection) since the respondent will need to go through a longer and more error-prone authentication process. If this creates too much hassle, there will be a decrease in the response rate which may invalidate the utility of the engagement project.&lt;/p&gt;
&lt;p&gt;A single sign-on (SSO) system, which may require an onerous authentication process but will re-use the authentication across multiple systems, can spread the pain sufficiently to mitigate the friction concerns above. However, SSO is more complex to setup and not all vendors support it.&lt;/p&gt;
&lt;h2 id="alternative-authentication-approaches"&gt;Alternative authentication approaches&lt;/h2&gt;
&lt;p&gt;Could we mitigate the attack by not using passwords at all? In &lt;em&gt;The quest to replace passwords&lt;/em&gt;&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;, the authors review three dozen approaches against two dozen criteria and find that none of the proposed alternatives to passwords is in-arguably superior across usability, deployability, and security dimensions. There are trade-offs in all approaches and all examined approaches have at least one feature that is &amp;ldquo;worse&amp;rdquo; than passwords.&lt;/p&gt;
&lt;p&gt;For example, take the &amp;ldquo;One Time Password over Email&amp;rdquo; approach&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt;. In this approach, users submit their email address to a server which persists one-half of a split token as a cookie and sends the other half to the user&amp;rsquo;s email. The user supplies the email token and the server combines and verifies the two tokens. Versus passwords, this scheme has the advantages that the user does not need to remember any long-term tokens, is resilient to someone watching the user type, is resilient to both throttled and unthrottled guessing, resilient against release of information from other parties, and resilient to phishing. However, this scheme has the disadvantages of being inefficient for use &lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt;, is incompatible with existing password-based systems, is immature versus other schemes, requires trusting a third-party (email system and intermediaries), and allows linking of accounts between disparate systems.&lt;/p&gt;
&lt;p&gt;For the employee engagement application, the fact that emails are required is likely a non-issue due to the system likely knowing quite a bit more about the employee; it&amp;rsquo;s already a PII-sensitive application. Adding friction to the login process is non-ideal as the project needs to maintain response rates, although employee engagement projects tend to have motivated respondents. The immaturity of the scheme &lt;em&gt;may&lt;/em&gt; be acceptable for the respondent use case. Respondents don&amp;rsquo;t need typical user accounts as they are only supplying data to the system. However, using a novel system will require more development effort to implement, may incur a larger user training cost, and will have a higher cost in security audits.&lt;/p&gt;
&lt;h1 id="tampering-mitigations"&gt;Tampering mitigations&lt;/h1&gt;
&lt;p&gt;Tampering attacks involved modifying the asset under protection, in this case, the response data. (Tampering could also involve modifying software or hardware within the data flow, but those kinds of attacks would be beyond this kind of attacker.) Maintaining the integrity of the data relies on either preventing improper modification or having a path to restore the proper data after an attack.&lt;/p&gt;
&lt;h2 id="enforce-deadline-on-updates"&gt;Enforce deadline on updates&lt;/h2&gt;
&lt;p&gt;In a standard employee engagement study, there will be a collection period followed by an analysis period where the project teams assembles and disseminates the reports. If updates to responses are restricted to the collection period, this reduces the window of opportunity for attacks.&lt;/p&gt;
&lt;h2 id="measure-trends-rather-than-moments"&gt;Measure trends rather than moments&lt;/h2&gt;
&lt;p&gt;In the episode, they only need to have good scores on the next review date. This means that actions will be taken on anomalous readings; the attackers only need to adjust scores for one review. If the project instead looked at trends over time, the attackers would need to adjust scores over multiple phases, which would greatly increase the chance that their attack would be noticed.&lt;/p&gt;
&lt;h2 id="report-back-to-the-respondents-on-updates"&gt;Report back to the respondents on updates&lt;/h2&gt;
&lt;p&gt;If changes in data are notified to multiple systems, it can make tampering more apparent. However, notifications and logs are only useful if people look at them as there is a cost to monitoring, particularly if updates are expected in the normal course of business (i.e. false positive rate is significant). One way to counter the economic cost is to notify the respondent, as their volume of notifications will be small. Any additional notifications seen by them will likely be treated as suspicious. Additionally, some respondents will like a copy for their own records, making a security feature a user feature as well. Sending the respondent an email copy of whatever is submitted is an easy mitigation to improve detection of tampering. However, this approach also allows a new vector of information disclosure, since attackers can now access the response data from other data sources. An employee&amp;rsquo;s company email account is generally more accessible to management and IT staff than an engagement response database.&lt;/p&gt;
&lt;h2 id="use-robust-aggregate-statistics"&gt;Use robust aggregate statistics&lt;/h2&gt;
&lt;p&gt;The attacker&amp;rsquo;s goal is to increase their engagement score, which is an aggregate of individual scores. Which aggregate is used can greatly influence how the attack must be performed. If individual responses include a score, typically aggregates include mean and median. Alternatively, the program may use a system like Net Promoter Score® (where responses are classified into the three categories of promoter, detractor, and passive and the derived score is the delta of the percentage of promoters versus percentage of detractors) or some linear regression model. The choice of the aggregate determines how robust the calculation is towards outliers and the more &lt;a href="https://en.wikipedia.org/wiki/Robust_statistics"&gt;robust the statistic&lt;/a&gt;, the more expensive the attack. (The use of &amp;ldquo;outlier&amp;rdquo; here is informal; the SaaS software presumably restricts value to a valid range, so someone can&amp;rsquo;t break the calculation by setting an individual&amp;rsquo;s score to a million.)&lt;/p&gt;
&lt;p&gt;For example, let&amp;rsquo;s say the score is based on the average (arithmetic mean) of individual employee&amp;rsquo;s scores. The sample includes 15 employees who have given the scores {1,1,1,2,2,3,3,3,3,3,4,4,5,5,6}. The mean of these scores is 3.1 and the median is 3. The attackers have a good idea of who will be giving the worst scores. By changing the scores to {10,10,10,2,2,10,3,3,3,3,10,4,5,5,6}, they&amp;rsquo;ve managed to improve the mean to 5.7 and the median to 5. As the mean is less robust, the attackers can improve it (here by 2.6) more easily by modifying one-third of the scores. The median is more robust and has only increased by two.&lt;/p&gt;
&lt;p&gt;If clamping is not enforced, then the attackers just need to modify one score to allow them to set the mean score to any arbitrary value. To modify the median to an arbitrary value, they would need to modify one half of all scores.&lt;/p&gt;
&lt;h2 id="retain-historical-data-and-report-on-it"&gt;Retain historical data (and report on it)&lt;/h2&gt;
&lt;p&gt;There are two vectors for a data change: an updated response from the respondent and an administrative edit. Particularly for this kind of study, respondents may naturally update their responses if they remember some new detail or tempers cool from a rash response. Thus, collection systems often allow updates to a response, even after the respondent has hit &amp;lsquo;submit&amp;rsquo;. On the administrative side, project administrators may edit the data to correct errors introduced in the project setup, may perform some data clean-up such as re-scaling values, or may edit the data for broader release&lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt; or upon request by the respondent. Reports and analytics resulting from the project should reflect the &amp;ldquo;latest&amp;rdquo; data and incorporate all edits.&lt;/p&gt;
&lt;p&gt;For both vectors the appropriate security feature that the SaaS developers and project administrators should maintain is &amp;ldquo;non-repudiation&amp;rdquo;. This feature means that someone can say &amp;ldquo;I didn&amp;rsquo;t do that&amp;rdquo; and the system can prove them wrong (or at least evidence that they did). In bank applications, this means someone can&amp;rsquo;t withdraw some money and then protest they didn&amp;rsquo;t do the withdrawal and should get the money back; the bank&amp;rsquo;s records will identify who performed the withdrawal, where it was done, and likely include photographic evidence of the act.&lt;/p&gt;
&lt;p&gt;Non-repudiation requires both data to be logged or persisted appropriately but the details of the changes to be available and evident to the project administrators. On the persistence side, developers will often use an immutable data design or event log to provide that audit trail, with some sort of &amp;ldquo;latest&amp;rdquo; snapshot to make analytical processing efficient. On the presentation side, this can be a challenge since there is often a &amp;ldquo;natural&amp;rdquo; amount of updates within a project that can obscure nefarious activity. Highlighting altered data within a data grid or showing the number of versions of each row are two straight-forward ways of exposing this data, as long as the volume of changes is small. In the case of a high volume of change, there is an opportunity for a system to provide anomaly or fraud detection to help administrators identify odd patterns.&lt;/p&gt;
&lt;h1 id="next-best-alternative-threats"&gt;&amp;ldquo;Next Best Alternative&amp;rdquo; Threats&lt;/h1&gt;
&lt;p&gt;Attackers are rational and will attempt to use the least risky and least expensive attacks available to them. Defenders, faced with their own resource limitations, should invest to raise the costs of attacks beyond the attack&amp;rsquo;s acceptable threshold, but not over-invest such that the investment exceeds the risk. If the above mitigations are successful against the manager&amp;rsquo;s attack, what would the managers do instead?&lt;/p&gt;
&lt;p&gt;First, instead of attacking respondent&amp;rsquo;s accounts, they could instead attack a project administrator&amp;rsquo;s account. Using the admin account, they could then edit the data or delete responses. Administrator accounts are more likely to include additional authentication protections than respondent&amp;rsquo;s accounts, so this approach is more risky, although once the account is accessed they have greater power to accomplish their goals. Protecting the data against administrative modifications have similar strategies as in &amp;ldquo;retain historical data&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Secondly, they could try to discredit the study or process itself. This is not a typical &amp;ldquo;security&amp;rdquo; attack, but if sufficient doubt is placed on the accuracy of the study, then the engagement scores will be ignored or treated with little weight. In the context of the episode, though, they are well-known for being anti-social, so they would need to argue that although the results match expectations, they are somehow invalid. Within the episode, the attackers use an alternative form of this attack by making the HR lead&amp;rsquo;s engagement scores terrible. The HR lead then cancels the study.&lt;/p&gt;
&lt;p&gt;Thirdly, an alternative form of discrediting could be accomplished through a denial of service attack. If the software becomes unavailable, the schedule for the study can be disrupted or random failures could shed doubt on the SaaS vendor&amp;rsquo;s ability to collect all the relevant data or present the complete picture. While a denial of service attack launched directly against the SaaS vendor would be very public and risky, a company&amp;rsquo;s network can be configured (perhaps through http proxies) to randomly drop packets or requests to the vendor&amp;rsquo;s sites, thus making it seem like the symptoms of overloaded or unreliable software. Based on our profile of the attackers, this would be outside their ability unless they are part of an IT group or could gain support from someone managing the network.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;By incorporating standard security controls and incorporating data integrity controls into their software, the cost and difficulty of the attack can be increased beyond the attacker&amp;rsquo;s abilities. Since the Pied Piper company is run so shabbily, it is believable that their in-house engagement software could suffer from such an attack, but since these styles of attacks are well-studied and have off-the-shelf mitigations, they should not work against a professional SaaS vendor&amp;rsquo;s solution.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This is in contrast to the show, in which one of the managers, Gilfoyle, is an extremely skilled engineer. The episode does not explain why Gilfoyle does not attempt to change the data within the database as the episode implies their collection systems are in-house rather than hosted by a vendor.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Shostack, Adam. &lt;em&gt;Threat modeling: Designing for security.&lt;/em&gt; John Wiley &amp;amp; Sons, 2014.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;ibid.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Anderson, Ross. &lt;em&gt;Security engineering: a guide to building dependable distributed systems.&lt;/em&gt; John Wiley &amp;amp; Sons, 2020.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Bonneau, Joseph, et al. &amp;ldquo;The quest to replace passwords: A framework for comparative evaluation of web authentication schemes.&amp;rdquo; &lt;em&gt;2012 IEEE Symposium on Security and Privacy.&lt;/em&gt; IEEE, 2012. &lt;a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/QuestToReplacePasswords.pdf"&gt;pdf&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:5"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;Van Der Horst, Timothy W., and Kent E. Seamons. &amp;ldquo;Simple authentication for the web.&amp;rdquo; &lt;em&gt;2007 Third International Conference on Security and Privacy in Communications Networks and the Workshops-SecureComm 2007.&lt;/em&gt; IEEE, 2007. &lt;a href="https://isrl.byu.edu/pubs/saw_TechReport%20(revised%20March%202007).pdf"&gt;pdf&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:6"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;Every time a user attempts to login, they will need to wait for an email (or some similar transmission).&amp;#160;&lt;a class="footnote-backref" href="#fnref:7"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>ℤ→ℤ</author><pubDate>Mon, 15 Nov 2021 20:38:16 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/engagement-insider-threat/</guid></item><item><title>Of course, even NATO is doing Artificial Intelligence</title><link>https://stop.zona-m.net/2021/11/of-course-even-nato-is-doing-artificial-intelligence/</link><description>&lt;p&gt;Trouble is, with whom?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 15 Nov 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/of-course-even-nato-is-doing-artificial-intelligence/</guid></item><item><title>Interactive Gradient Descent Demo</title><link>https://blog.skz.dev/gradient-descent</link><description>&lt;h4 id="what-is-this-special-algorithm"&gt;&lt;em&gt;What is this special algorithm?&lt;/em&gt;&lt;/h4&gt;

&lt;p&gt;Gradient descent is an optimization algorithm for finding the (local) minimum of a function.&lt;/p&gt;

&lt;h2 id="why-is-that-useful"&gt;Why is that useful?&lt;/h2&gt;

&lt;p&gt;Say you have some function \(f(x,y)\). This function may represent a cost to you, and \(x\) and \(y\) are some inputs that vary the cost. Naturally, you are going to try to find the values of \(x\) and \(y\) that minimize the cost. This is a problem of finding the minimum of \(f(x,y)\). For example, we may want to find the minimum of this function, which is plotted below:&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
f(x,y)=3(1 - x)^2 e^{- x^2 - (y + 1)^2} - 10(\frac{x}{5} - x^3 - y^5) e^{-x^2 - y^2} - \frac{1}{3} e^{-(x+1)^2 - y^2} \tag{0}  \label{eq:cost_func}
\end{align}

    $$
&lt;/div&gt;

&lt;div id="plot-0"&gt;&lt;/div&gt;

&lt;p&gt;This is where gradient descent can help.&lt;/p&gt;

&lt;h2 id="why-gradient-descent"&gt;Why gradient descent?&lt;/h2&gt;

&lt;p&gt;From elementary calculus, you may remember a procedure for finding the minimum of a function. You could find the first derivative, set it to zero, and solve that equation:&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
f'(x,y)=0 \tag{1}  \label{eq:first_deriv}
\end{align}

    $$
&lt;/div&gt;

&lt;p&gt;This will reveal the locations of the extrema (maxima and minima) of the function. The sign of the second derivative \(f''(x,y)\) can reveal whether the extrema are local maxima or minima. However, solving the first derivative equation \(\eqref{eq:first_deriv}\) can be non-trivial, and often require numeric methods. And as your cost function involves higher and higher dimensions, e.g. \(f(x,y,z,w,q,u,v,...)\) the problem can become unmanageable. Gradient descent provides an efficient procedure for locating minima by utilizing the first derivative in a clever way.&lt;/p&gt;

&lt;h2 id="the-gradient-descent-algorithm"&gt;The gradient descent algorithm&lt;/h2&gt;

&lt;p&gt;The motivation behind gradient descent is intuitive, and I bet you would arrive at the same procedure in an analogous physical situation. Imagine you find yourself in a thick fog, and you can only see a few feet in front of you and feel the slope of the ground beneath you. You are trying to get to the bottom of a valley as quickly as possible. Surely, you won’t waste your time walking along a flat slope - or worse, head uphill. To descend quickly, you will inspect the slope at your location, and proceed in the direction where the slope is steepest downwards. Gradient is a synonym for slope, hence, you would be performing gradient descent. Conversely, if you were trying to find the top of the hill, you would follow the steepest part of the surface up, wherever you are. This is gradient &lt;em&gt;ascent&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id="gradient"&gt;Gradient&lt;/h2&gt;
&lt;p&gt;Gradient is essentially another word for slope. Most people will be familiar with finding a slope in a 1-dimensional situation. In 1-D, since one can only proceed either forward or backward, the slope is clearly defined. However, in higher dimensions - imagine you find yourself on a hike - the slope may have a different value depending on which direction you go. How does one define the slope at this point?&lt;/p&gt;

&lt;p&gt;In vector calculus, one uses the gradient operator \(\nabla\), which is a vector of the slopes (or first derivatives) along each dimension.&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
\nabla f(x,y)= \begin{bmatrix}
           \frac{\partial f}{dx} (x,y) \\
           \frac{\partial f}{dy} (x,y)
         \end{bmatrix} \tag{2}  \label{eq:gradient}
\end{align}

    $$
&lt;/div&gt;

&lt;p&gt;On a 2D surface defined by \(f(x,y)\), this would be the slope along the x-axis and the slope along the y-axis. The slope in any particular direction (which is important, since we are not constrained to move only along either the x-axis or the y-axis) can be found by summing the corresponding x and y components of the slope in that direction. In other words, the dot product of a direction vector with the gradient produces the slope in that direction.&lt;/p&gt;

&lt;p&gt;Another interesting property of the gradient is that it gives the direction of the steepest slope. One can see this mathematically - since the slope in any particular direction can be found by dotting that direction vector with the gradient, the dot product is maximized when the gradient is dotted with itself!&lt;/p&gt;

&lt;p&gt;Below, I plot the negative gradient of \(f(x,y)\) with Mathematica. This gives us the direction of steepest descent at every point on \(f\).&lt;/p&gt;

&lt;p&gt;&lt;img alt="Gradient of f" src="/assets/img/gradient-descent/gradient.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Gradient of f plotted as a vector field.
&lt;/p&gt;

&lt;h2 id="follow-the-steepest-part-down"&gt;Follow the steepest part down&lt;/h2&gt;
&lt;p&gt;Earlier we agreed that if you were blindly navigating down a valley, you would follow the steepest direction of the ground down if you wanted to get to the bottom as quickly as possible. Mathematically, you are traversing down the valley along the gradient at each point, since the gradient vector at each point is the direction of the steepest descent. Looking at the image above, we can see that means following the arrows will lead us to a minimum (but be wary - each minimum has its own basin of attraction! More on that later).&lt;/p&gt;

&lt;p&gt;Thus we can define a procedure. It may go something like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Inspect the slope at your position and determine the direction of steepest descent&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Walk 5 steps in that direction&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you are still descending, go to 1. Otherwise, you’re at the bottom!&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You may be wondering how I arrived at 5 steps. Well, it was totally arbitrary. But we can let it be a parameter of our procedure. Let’s call it a descent rate, and assign it the symbol \(\alpha\). The larger the descent rate, the less often we’ll have to inspect the slope around us. Computationally, this is more efficient - but as you’ll see, this can lead to pitfalls.&lt;/p&gt;

&lt;p&gt;Furthermore, we can make another optimization. When the local slope is still steep, we are probably far from the bottom of the valley. But as the slope gets flatter, we can figure that we are probably approaching the bottom. Hence we may want to descend at a rate proportional to the gradient. Let’s define the gradient with the symbol \(\bar{g}\), and redefine our gradient descent procedure:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Inspect the slope at your position and determine the direction of steepest descent&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Walk \(\alpha \times \|\bar{g}\|\) steps in that direction&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you are still descending, go to 1. Otherwise, you’re at the bottom!&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, how do we determine if we are still descending? One approach is to check how much we’ve descended on every iteration. If we’re barely changing altitude, then we’re probably essentially at the bottom already.&lt;/p&gt;

&lt;p&gt;Mathematically, our procedure looks like:&lt;/p&gt;

&lt;div style="width: 100%;"&gt;
    $$
        
\begin{align}
\bar{p}_{n+1} = \bar{p}_n - \alpha \nabla f(x,y) \tag{3}  \label{eq:grad_descent}
\end{align}

    $$
&lt;/div&gt;

&lt;p&gt;where \(p_n\) is our position at the \(n^{th}\) iteration of our descent. Try it out on the demo below. Varying the rate \(\alpha\) varies the rate of the descent, while varying \(X\) and \(Y\) vary the initial starting point on the surface. What do you notice?&lt;/p&gt;

&lt;div id="plot-1"&gt;&lt;/div&gt;

&lt;p&gt;&lt;span class="slider-container"&gt;
    &lt;span class="slider-label"&gt;X&lt;/span&gt;
    &lt;span&gt;
        &lt;input class="slider" id="x-slider" max="3" min="-3" step="0.05" type="range" value="0" /&gt;
    &lt;/span&gt;
    &lt;span class="slider-value" id="x-slider-value"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="slider-container"&gt;
    &lt;span class="slider-label"&gt;Y&lt;/span&gt;
    &lt;input class="slider" id="y-slider" max="3" min="-3" step="0.05" type="range" value="0" /&gt;
    &lt;span class="slider-value" id="y-slider-value"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="slider-container"&gt;
    &lt;span class="slider-label"&gt;α&lt;/span&gt;
    &lt;input class="slider" id="alpha-slider" max="0.2" min="0" step="0.001" type="range" value="0" /&gt;
    &lt;span class="slider-value" id="alpha-slider-value"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="observations"&gt;Observations&lt;/h2&gt;

&lt;p&gt;You may notice that gradient descent is sensitive to the initial position. It will pull you towards a local minimum, which may not be the global minimum. The area of the domain that causes gradient descent to converge to a particular minimum is known as that minima’s basin of attraction.&lt;/p&gt;

&lt;p&gt;You may also notice that increasing the descent rate \(\alpha\) reduces the number of iterations required to arrive at the minimum. But making it too large causes the algorithm to overshoot, and possibly diverge.&lt;/p&gt;

&lt;h2 id="machine-learning"&gt;Machine learning&lt;/h2&gt;

&lt;p&gt;You’ve probably heard of gradient descent in the context of machine learning. In that context, the cost function represents the error of our machine learning model. For example, it may be the error of classifying images, and the inputs to the cost function are the weights of the model we are varying. Hence, we can use gradient descent to find the optimal configuration of weights that minimize the classification error of the neural network! In this field, the descent rate \(\alpha\) has a special name: it is known as the &lt;em&gt;learning rate&lt;/em&gt;. Maybe it’s what our brains have been doing all along.&lt;/p&gt;</description><author>Sasha Kuznetsov's Blog</author><pubDate>Mon, 15 Nov 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.skz.dev/gradient-descent</guid></item><item><title>Develop Google scraper in Python with Scraper API</title><link>https://blog.adnansiddiqi.me/developer-google-scraper-in-python-with-scraper-api/</link><description>&lt;p&gt;This is another post in ScrapeTheFamous, in which I will be parsing some famous websites and will discuss my development process. The posts will be using Scraper API for parsing purposes which makes me free from all worries about blocking and rendering dynamic sites since Scraper API takes care of everything. So this post is about scraping Google search results, the script will accept a keyword and would return results across multiple pages. The data will be stored in a text file in JSON format. The code that is parsing the result is pretty straightforward and given below: def google_scraper(query, start=0): records = [] try: URL_TO_SCRAPE = "http://www.google.com/search?q=" + query.replace(' ', '+') + "&amp;#38;start=" + str(start * 10) \ + '&amp;#38;num=10&amp;#38;pws=0' print(URL_TO_SCRAPE) print("Checking on page# " + str(start + 1)) payload = {'api_key': API_KEY, 'url': URL_TO_SCRAPE, 'render': 'false'} r = requests.get('http://api.scraperapi.com', params=payload, timeout=60) soup = BeautifulSoup(r.text, 'lxml') results = soup.select('.yuRUbf &amp;#62; a') for result in results: heading = result.select('h3') records.append({'URL': result['href'], 'TITLE': heading[0].text}) print(heading[0].text, ' ', result['href']) except requests.ConnectionError as e: print("OOPS!! Connection Error. Make sure you are connected to Internet. Technical Details given below.\n") print(str(e)) except requests.Timeout as e: print("OOPS!! Timeout Error. Technical Details given below.\n") print(str(e)) except requests.RequestException as e: print("OOPS!! General Error. Technical Details given below.\n") print(str(e)) finally: return records It goes from one page to the next, keeping the search keyword intact. It then parses the H3 and a tag and stores in a JSON structure. Conclusion In this post, you learned how you can create a Google Search parser in Python using Scraper API. You do not have to worry about Proxy IPs either nor do you have to pay hundreds of dollars, especially when you are an individual or working in a startup. The company I worked with spends 100s of dollars on a monthly basis just for the proxy IPs. Oh if you sign up here with my referral link or enter promo code adnan10, you will get a 10% discount on it. In case you do not get the discount then just let me know via email on my site and I’d sure help you out. The code is available on Github If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/developer-google-scraper-in-python-with-scraper-api/"&gt;Develop Google scraper in Python with Scraper API&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Mon, 15 Nov 2021 09:42:23 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/developer-google-scraper-in-python-with-scraper-api/</guid></item><item><title>Ankibutton</title><link>https://voussoir.net/writing/ankibutton</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a class="tag_link" href="/writing/tags/3dprinting"&gt;[3dprinting]&lt;/a&gt; &lt;a class="tag_link" href="/writing/tags/electronics"&gt;[electronics]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Ankibutton&lt;/h1&gt;&lt;ol id="table_of_contents"&gt;Table of contents&lt;li&gt;&lt;a href="#glamour_shots"&gt;Glamour shots&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#the_problem"&gt;The problem&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#false_starts"&gt;False starts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#true_starts"&gt;True starts&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#the_fourth_button"&gt;The fourth button&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#download"&gt;Download&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#appendix_installing_seeeduino_xiao_into_the_arduino_ide"&gt;Appendix: Installing Seeeduino Xiao into the Arduino IDE&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id="glamour_shots"&gt;Glamour shots&lt;a class="header_anchor_link" href="#glamour_shots"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/glamour_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/glamour_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/glamour_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/glamour_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/glamour_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/glamour_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;a class="header_anchor_link" href="#introduction"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the most effective ways to procrastinate on a task is to spend time working on something else that will supposedly help you do the original task more efficiently, but in actuality will never recoup the invested time. You can make a new tool, or design a new process, or rearrange your work environment, just as long as you spend more time on it than it could ever possibly pay you back. Whatever you do, never consult &lt;a href="https://xkcd.com/1205/"&gt;this chart&lt;/a&gt;. This technique will ensure that you come out behind on your goals while giving you the defense of having worked hard the entire time.&lt;/p&gt;
&lt;p&gt;In this article, I'll show you a new tool I made.&lt;/p&gt;
&lt;h2 id="the_problem"&gt;The problem&lt;a class="header_anchor_link" href="#the_problem"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Over the past three years, I've done almost 100% of my &lt;a href="https://apps.ankiweb.net/"&gt;Anki&lt;/a&gt; studying on my &lt;a href="https://f-droid.org/en/packages/com.ichi2.anki/"&gt;smartphone&lt;/a&gt;, simply because I don't enjoy the ergonomics of using Anki on the computer. I don't like having to sit with my hands on the keyboard for a long time, pressing the spacebar. I'd rather be able to lean back or sit farther away.&lt;/p&gt;
&lt;p&gt;At the same time, the ergonomics of the smartphone are not ideal either. The hands are most comfortable at the lap but this is bad for the neck. Researching words and copy-pasting unicode is slow and cumbersome on mobile.&lt;/p&gt;
&lt;p&gt;I thought it might be nice to have a handheld controller that I can use with my computer from a distance. I already have an Xbox controller and &lt;a href="https://joytokey.net/en/"&gt;joytokey&lt;/a&gt;, but if I used those things then I'd be done procrastinating already.&lt;/p&gt;
&lt;p&gt;So there I was, with no choice but to make my own.&lt;/p&gt;
&lt;h2 id="false_starts"&gt;False starts&lt;a class="header_anchor_link" href="#false_starts"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I assembled my &lt;a href="/writing/kb_nearer"&gt;keyboard&lt;/a&gt; I used a microcontroller that cost $18 plus $5 shipping, which is more than I wanted to spend on a silly thing like the ankibutton. I happened to have an old rubber-dome style keyboard that was broken and useless, so I thought I'd take the controller out of that and wire up some of my leftover keyboard switches onto it. By following the sheets of electrical contacts you can learn which pins you'd need to solder switches to to make a keypress. I never did solder anything, but I used a piece of wire to connect some pins and figure out where the keys I want are.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/harvested_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/harvested_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/harvested_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/harvested_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/harvested_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/harvested_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I designed a case for this controller and three cherry switches.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/oldmodel.png"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/oldmodel.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/harvested_4.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/harvested_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I decided not to use that controller for two reasons. Mainly because it's not possible to reprogram the firmware on it, so if I ever got tired of using it as an ankibutton I'd be unable to change any of the keys without unsoldering and resoldering it in a new configuration. Secondly because I want to include a Ctrl+Z button on the ankibutton, and I don't think soldering a single switch onto the pads for both Ctrl and Z would work properly.&lt;/p&gt;
&lt;h2 id="true_starts"&gt;True starts&lt;a class="header_anchor_link" href="#true_starts"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I set this idea aside for a few months until one day I got an idea for a different project and started researching for other microcontrollers. I discovered the Seeeduino Xiao, and was able to get a two-pack for $14. Seven dollars for an arduino-compatible board with USB-C is a pretty good price.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I also decided not to use the gateron keyboard switches, because for a handheld device these switches were just too big and would require lots of thumb movement during normal use. Instead, I harvested some small pushbuttons from a plate that had come out of a fax machine.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/buttons_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/buttons_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/buttons_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/buttons_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/buttons_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/buttons_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I redesigned the enclosure around the Seeeduino and 6mm pushbuttons.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/model.png"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/model.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_fitting_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_fitting_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_fitting_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_fitting_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_fitting_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_fitting_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/button_fitting_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/button_fitting_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/button_fitting_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/button_fitting_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/button_fitting_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/button_fitting_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I used superglue to hold the buttons in place, and double-sided tape to fix the controller in its recess. I used doubled-sided tape to attach a piece of felt on the underside of the lid, so that when the lid is firmly shut it provides more support to the controller.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/controller_fitting_4.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/controller_fitting_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The wiring was straightforward, though I'm still not very good at soldering. The idea is the same as a full-sized keyboard matrix. One wire comes out of the controller and visits all four switches, providing what I am calling the "outbound" signal. Then, the other leg of each switch is wired to a separate pin on the controller so that there are four distinct "inbound" signals. The controller simulates a keypress whenever an inbound signal is received.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/soldering_1.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/soldering_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/soldering_2.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/soldering_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I was surprised to find that the Seeeduino does not support QMK firmware like I used on my keyboard. Or at least, I was not able to make QMK Toolbox discover the device even after switching the MCU type to atmega328p. I had to write the firmware myself as Arduino code which took me a while but was overall not too difficult.&lt;/p&gt;
&lt;p&gt;Programming for hardware is slower than programming computer software. Normally when I write a program that crashes I can just read the traceback, but in hardware there are no tracebacks. It just stops working. And I have to keep bisecting the code until I find the part that does the least amount of wrongness, each time using a piece of wire to short RESET to GROUND to go back to bootloader mode because regular mode crashes before Arduino can reprogram it.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/testing.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/testing.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/soldering_3.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/soldering_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/soldering_4.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/soldering_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Even though the first several prints I made were able to snap shut with a bit of friction, for some reason the final one didn't hold itself together very well. But I was tired of printing it so I wrapped it with some blue tape instead of doing it again.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/iterations.jpg"&gt;&lt;img alt="" src="/writing/ankibutton/thumbs/iterations.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="the_fourth_button"&gt;The fourth button&lt;a class="header_anchor_link" href="#the_fourth_button"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My original plan was for three buttons: Spacebar (right answer), 1 (wrong answer), and Ctrl+Z (go back). After switching to the little buttons it made more sense to have four. It would be nice to map it to Anki's "easy" answer, but that's not straightforward since easy is sometimes 3 and sometimes 4. I am not quite sure what I'll do with it yet.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="header_anchor_link" href="#conclusion"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The finished product functions very well. With a long USB cable I can lean back and review my flashcards comfortably. I added code to debounce the button presses and they seem reliable.&lt;/p&gt;
&lt;p&gt;Having said that, I will probably use the ankibutton a few times and then forget about it. The burden of plugging it in, or worse, leaving it plugged in and cluttering my desk space, is simply higher than the value it provides. Mission accomplished!&lt;/p&gt;
&lt;h2 id="download"&gt;Download&lt;a class="header_anchor_link" href="#download"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/ankibutton.scad"&gt;ankibutton.scad&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/ankibutton/ankibutton.ino"&gt;ankibutton.ino&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="appendix_installing_seeeduino_xiao_into_the_arduino_ide"&gt;Appendix: Installing Seeeduino Xiao into the Arduino IDE&lt;a class="header_anchor_link" href="#appendix_installing_seeeduino_xiao_into_the_arduino_ide"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open File &amp;gt; Preferences, and into the "Additional Boards Manager URLs", paste this: &lt;code&gt;https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Open Tools &amp;gt; Board &amp;gt; Boards Manager, and download "Seeed SAMD Boards", which should appear in search results for "xiao".&lt;/li&gt;
&lt;li&gt;Select Tools &amp;gt; Board &amp;gt; Seeed SAMD &amp;gt; Seeeduino Xiao.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the Seeeduino is in normal operating mode, it will occupy one COM port. If you use a piece of wire to short RESET to GROUND twice in a row, it will enter bootloader mode and occupy a different COM port. During bootloader mode it will also appear in Windows as an external storage device.&lt;/p&gt;
&lt;p&gt;When uploading the code from Arduino IDE to the board, it will automatically go from normal mode to bootloader mode and back again. But if you need to reset it manually due to a crash, you need to go back to Tools &amp;gt; Ports and pick the bootloader port before uploading again.&lt;/p&gt;
&lt;p&gt;I found that uploading the code would often make my computer reset all of its USB connections, including my external hard drives, which was problematic for me. I did the remainder of the programming on a separate laptop to avoid this.&lt;/p&gt;
&lt;p&gt;At first, my laptop was not able to recognize the device at all. I had to download &lt;a href="https://github.com/Seeed-Studio/Signed_USB_Serial_Driver/blob/master/seeed_usb_serial.inf"&gt;seeed_usb_serial.inf&lt;/a&gt;, and then tell Device Manager to install a driver from that file. When Device Manager asks for a driver location it only allows you to select folders, not files, so just select the folder into which you downloaded the inf file.&lt;/p&gt;
&lt;p&gt;After these steps I did not encounter any more issues with programming the controller.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/ankibutton/ankibutton.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/e5b415e3b8c8f1438379c7381682501c16a8223c"&gt;&lt;time datetime="2021-11-14T16:55:54-08:00"&gt;2021-11-14&lt;/time&gt; Add ankibutton.md.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Mon, 15 Nov 2021 02:55:54 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/ankibutton</guid></item><item><title>Shortn</title><link>https://june.kim/shortn/</link><author>june.kim</author><pubDate>Mon, 15 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/shortn/</guid></item><item><title>Salesloft Pause Cadences</title><link>https://solomon.io/pause-cadences/</link><description>Cadences are at the core of what makes Salesloft such a fantastic tool. It is a workflow that groups like tasks together to reduce context switching.</description><author>Sam Solomon</author><pubDate>Mon, 15 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://solomon.io/pause-cadences/</guid></item><item><title>Installing a fast Linux server on an M1-based Mac</title><link>https://jasoneckert.github.io/myblog/installing-linux-server-on-mac-m1/</link><description>&lt;p&gt;One of the biggest worries that IT users have regarding Apple&amp;rsquo;s new ARM-based M1 platform is the ability to run &lt;em&gt;&lt;strong&gt;virtual machines (VMs)&lt;/strong&gt;&lt;/em&gt; of other operating systems using hypervisor software. And while it&amp;rsquo;s possible to run virtual machines of both Windows and Linux operating systems made for the Intel platform on the M1, the performance cost of translating Intel instructions to ARM is high (even with the optimizations Apple has within their M1 CPU). Luckily, the most common VM that an IT systems administrator or developer will install is Linux, and ARM versions of Linux have been mainstream for well over a decade now.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Mon, 15 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/installing-linux-server-on-mac-m1/</guid></item><item><title>A guy at work asked, where's your blog?</title><link>https://swaits.com/a-guy-asked-where-my-blog-was/</link><description>&lt;p&gt;✅ here's my blog&lt;/p&gt;
&lt;p&gt;A guy at work said, "where's your blog?" So, here's a quickly hacked together
site using &lt;a href="https://www.getzola.org" rel="external"&gt;zola&lt;/a&gt; with the
&lt;a href="https://github.com/carpetscheme/lightspeed" rel="external"&gt;lightspeed&lt;/a&gt; theme. It's hosted in
a private repo on &lt;a href="https://sr.ht/" rel="external"&gt;sourcehut&lt;/a&gt; and, at least for now, built and
deployed to &lt;a href="https://srht.site/" rel="external"&gt;sourcehut pages&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I blogged many years ago. Like, decades ago. Don't know what I might put here,
but I'll give it a shot!&lt;/p&gt;</description><author>swaits.com</author><pubDate>Mon, 15 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://swaits.com/a-guy-asked-where-my-blog-was/</guid></item><item><title>Games Not Worth Playing: Limited Resources</title><link>http://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources</link><description>&lt;blockquote&gt;
  &lt;p&gt;He who has learned to disagree without being disagreeable has discovered the most valuable secret of negotiation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Chris Voss, Never Split the Difference
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the fourth post in a series. You can find the last three here: &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;1&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;2&lt;/a&gt;, &lt;a href="https://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer"&gt;3&lt;/a&gt;. There we set the basics of &lt;strong&gt;opting out of negative games,&lt;/strong&gt; and now we can start diving into examples.&lt;/p&gt;

&lt;p&gt;A classic example of a negative game is the adversarial distribution of limited resources. In such situations, you might be fighting with others over the same set of stuff, whether it is the proverbial pie, or that promotion which only one person will get, the custody of a child, or even who gets to speak in a conversation.&lt;/p&gt;

&lt;p&gt;One way to exit such game, is to change the vibe from competition to cooperation, to increase these limited resources.&lt;/p&gt;

&lt;p&gt;For example, if we consider a divorcing couple which fights over the custody of their child, we can see that what they’re really fighting for is the satisfaction of their own egos, and not the best outcome for the child. By treating that child as a limited resource to fight over, instead of a person, which both want to support, and instead of discussing how to make the transition less traumatizing for the kid, they end up creating more trauma for the child, as the child will lose one parent. However, if the parents care more about their children’s future than about their own egos, they might find an arrangement where the kids experience more love from both parents, and grow up to be more fulfilled. The parents can opt out of bickering by realizing they still have a shared vested interest in their kid’s wellbeing, and change the “&lt;a href="https://dimitarsimeonov.com/2021/07/16/rhythm-and-vibe"&gt;vibe&lt;/a&gt;” of the conversation.&lt;/p&gt;

&lt;p&gt;&lt;img alt="fighting vs growing" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/04growing.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;More generally, in any conversation you may try to convince the others of something, and think that the way to do it is to spend more time talking. You might see the time during the conversation as a limited resource and try to capture most of that time by speaking more, and speaking louder than others. And doing so, you might make it harder for others to express themselves, creating a competitive argument. A shouting match of loudly saying “Me, me, me!” As a result, you’re not going to be effective at convincing, and will not even contribute to information transfer. When you’re trying to talk, you’re not trying to listen. If you instead try to play the game of increasing information transfer, regardless of the direction, you stand a better chance of persuading others, and you are also allowing yourself to have your worldview improved.&lt;/p&gt;

&lt;p&gt;In the last example today, we look at the Red Queen’s race. A company entering a crowded market may be fighting for market share with other companies, selling to the same customers. That company may even be successful if the incumbent are not providing a good service. And if these companies are fighting over the same market share, they end up both cutting costs and price, and undermining each other’s profit. A way out for a company is to figure out how to grow the market and capture that new growth, by serving an underserved population or use case which wasn’t in the market previously. Focusing on beating the competition is zero-sum thinking, but focusing on the customer is a win-win thinking.&lt;/p&gt;

&lt;p&gt;Next week, we will discuss artificial scarcity.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Mon, 15 Nov 2021 01:44:14 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/11/14/games-not-worth-playing-limited-resources</guid></item><item><title>Backups</title><link>https://mfashby.net/posts/2021-11-14-backups/</link><description/><author>Home on mfashby.net</author><pubDate>Sun, 14 Nov 2021 13:12:44 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-11-14-backups/</guid></item><item><title>My ASP.NET 5 migration to .NET 6</title><link>https://nicolaiarocci.com/my-asp.net-5-migration-to-.net-6/</link><description>&lt;p&gt;I spent the last few days migrating our ASP.NET REST services, MVC web
applications and Blazor server apps to .NET 6. Overall the process was pretty
straightforward. The few issues I went through were easy to solve and well
documented. Things got more involved with the EF Core 6 transition, especially
with the Npgsql Entity Framework Core Provider.&lt;/p&gt;
&lt;p&gt;The official &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60"&gt;ASP.NET Core 5.0 to 6.0 migration guide&lt;/a&gt; was my first stop. It
offers the perfect entry point, rich with in-depth links. At this stage, I am
not interested in switching to the new .NET 6 minimal hosting model (aka
Minimal APIs). I think it&amp;rsquo;s a significant improvement, and we will likely adopt
it for new projects, but our production projects aren&amp;rsquo;t going to be refactored
right away. Should minimal APIs also prove to be remarkably performant, we&amp;rsquo;ll
reconsider them&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 14 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/my-asp.net-5-migration-to-.net-6/</guid></item><item><title>Keep secrets in python</title><link>https://yasha.solutions/posts/python-keeping-secrets/</link><description>Keeping Secrets Secret in Python
Based on an article by originally published by Jason Goldberger but unfortunately the original is not online anymore so I have rewritten a short version, since this is something I have been looking for a while.
Fernet has one classmethod called generate_key() and two instance methods encrypt(plaintext_binary) and decrypt(cipher_binary).
Step 1 : Generate a key Generate a key and save it to the OS’s environment variables:</description><author>Yasha Solutions</author><pubDate>Sun, 14 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/python-keeping-secrets/</guid></item><item><title>Three Editor Use Cases</title><link>https://duncanlock.net/blog/2021/11/13/three-editor-use-cases/</link><description>&lt;p class="lead"&gt;I have three different text editors, that I use for three distinct use&amp;nbsp;cases.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m a software developer, so I basically edit text files for a living, and the editing software that I use for this is fairly important to&amp;nbsp;me.&lt;/p&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_projects"&gt;Projects&lt;/h2&gt;&lt;p&gt;For any substantial editing, either for work, or for personal projects, I currently use &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; - generally known as &amp;#8220;vscode&amp;#8221;. It&amp;#8217;s good enough: it&amp;#8217;s &lt;em&gt;extremely&lt;/em&gt; actively developed, so always up to date, and it has all the plug-ins you could ever want. Performance is &lt;em&gt;good enough&lt;/em&gt;, once it&amp;#8217;s started&amp;nbsp;up.&lt;/p&gt;
&lt;p&gt;I tend to just start my project editor once and leave it open&amp;nbsp;permanently.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m still aggrieved that &lt;a href="https://github.blog/2022-06-08-sunsetting-atom/"&gt;Microsoft killed the Atom editor&lt;/a&gt; when they bought GitHub, despite promising that they wouldn&amp;#8217;t. It was a better editor - better &lt;span class="caps"&gt;UX&lt;/span&gt;, &lt;a href="https://tree-sitter.github.io/tree-sitter/"&gt;Tree-sitter&lt;/a&gt;,&amp;nbsp;etc&amp;#8230;&amp;#8203;&lt;/p&gt;
&lt;aside class="sidebar"&gt;&lt;p&gt;I used to use Atom for this, until Microsoft killed it - and &lt;a href="https://www.sublimetext.com/"&gt;SublimeText&lt;/a&gt; before that. I have a paid license for SublimeText and still try it out occasionally. The fact that it&amp;#8217;s closed source, coupled with literally years of radio silence from the developer between updates - and the sparse/non-existent documentation - made it hard for the plugin ecosystem to get off the ground, despite the editor being great. I think they&amp;#8217;ve improved this now, but too little too late for&amp;nbsp;me.&lt;/p&gt;&lt;/aside&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_occasional_or_one_off_editing"&gt;Occasional or One-Off&amp;nbsp;Editing&lt;/h2&gt;&lt;p&gt;If I&amp;#8217;m just doing a quick edit of a config file or something - I generally don&amp;#8217;t use the project editor. It&amp;#8217;s too heavy, too slow to start and will remember all the previous open stuff - which I want in project mode, but not for one-off&amp;nbsp;editing.&lt;/p&gt;
&lt;p&gt;Because I have less baggage and attachment to this editor, this is where I experiment with new&amp;nbsp;editors.&lt;/p&gt;
&lt;p&gt;I have used &lt;a href="https://docs.xfce.org/apps/mousepad/start"&gt;Mousepad&lt;/a&gt;, &lt;a href="https://cudatext.github.io/"&gt;CudaText&lt;/a&gt;, &lt;a href="https://lite-xl.com/"&gt;Lite &lt;span class="caps"&gt;XL&lt;/span&gt;&lt;/a&gt; - but currently mostly using &lt;a href="https://lapce.dev/"&gt;Lapce&lt;/a&gt;. Really fast startup, cross-platform, tree-sitter, good&amp;nbsp;enough.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_cli_editing"&gt;&lt;span class="caps"&gt;CLI&lt;/span&gt;&amp;nbsp;Editing&lt;/h2&gt;&lt;p&gt;I mostly use a desktop &lt;span class="caps"&gt;GUI&lt;/span&gt;, but always have terminals open all over the place and often do some editing there. I&amp;#8217;ve never learned to sing the song of vi - and I don&amp;#8217;t feel the need, at this point. I use the excellent &lt;a href="https://github.com/zyedidia/micro"&gt;micro editor&lt;/a&gt; for all my terminal editing needs. I have this set in my &lt;code&gt;.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;micro
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MICRO_TRUECOLOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Micro is &lt;em&gt;excellent&lt;/em&gt;, open source and written by &lt;a href="https://duncanlock.net/blog/2023/05/25/thanks-zachary-yedidia-zyedidia/"&gt;Zachary Yedidia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For me, editing in the &lt;span class="caps"&gt;CLI&lt;/span&gt; is generally one-off editing too. If it turns into a project, I switch to using my project&amp;nbsp;editor.&lt;/p&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Sat, 13 Nov 2021 21:14:33 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/11/13/three-editor-use-cases/</guid></item><item><title>We HAD been warned about coming digital stupidity</title><link>https://stop.zona-m.net/2021/11/we-had-been-warned-about-coming-digital-stupidity/</link><description>&lt;p&gt;Another of those obvious things that are never repeated too much.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 13 Nov 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/we-had-been-warned-about-coming-digital-stupidity/</guid></item><item><title>git gc - You're not going to need it</title><link>https://donatstudios.com/yagni-git-gc</link><description>&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;I recommend every developer turn off automatic garbage collection in git. It's one of the &lt;a href="https://github.com/donatj/dotfiles/blob/20afd7528810bb7ed4046ca869b84391841dbcba/git/.gitconfig#L17-L18"&gt;first things I do&lt;/a&gt; setting up a new machine - and you should do so too.&lt;/p&gt;
&lt;p&gt;For a happier life, go to your terminal right now and run:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;$ git config --global gc.auto 0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I promise you won't regret it.&lt;/p&gt;
&lt;h2&gt;The full story&lt;/h2&gt;
&lt;p&gt;Turn off garbage collection? Surely, I must be crazy, right? Automatic garbage collection is important, right? They wouldn't turn it on by default if it wasn't!? Eh, not really. Not for you. I'm not being sarcastic. You just probably don't work at Meta or Microsoft scale.&lt;/p&gt;
&lt;p&gt;Garbage collection for git in most every use case just isn't worth it. That is unless you're very regularly committing &lt;strong&gt;very large files&lt;/strong&gt;, and then orphaning those commits. I don't see that happening in most uses cases; garbage just doesn't build up that quickly in normal usage.&lt;/p&gt;
&lt;h2&gt;Don't believe me?&lt;/h2&gt;
&lt;img src="https://donatstudios.com/assets/75/git-info.png" style="float: right; width: 372px;" /&gt;
&lt;p&gt;I have been working with &lt;em&gt;the same&lt;/em&gt; git repo every day for &lt;em&gt;ten solid years&lt;/em&gt; with gc turned off. My &lt;code&gt;.git&lt;/code&gt; folder is currently &lt;code&gt;554 MB&lt;/code&gt; whereas a freshly cloned copy is &lt;code&gt;112 MB&lt;/code&gt;. I could be saving a whole &lt;code&gt;442 MB&lt;/code&gt; - whoop de doo.&lt;/p&gt;
&lt;p&gt;I don't know about you, but I'll happily pay &lt;code&gt;442 MB&lt;/code&gt; for 10 years of improved data safety.&lt;/p&gt;
&lt;h2&gt;But why turn it off?&lt;/h2&gt;
&lt;p&gt;Having it on means detached commits could disappear at any time. It's a roll of the dice.&lt;/p&gt;
&lt;p&gt;Turning off automatic GC means that anything you once committed will remain restorable, forever. If a rebase goes badly, you can &lt;strong&gt;always&lt;/strong&gt; safely get back to the previous state of a now-orphaned commit. There is zero chance of it ever disappearing.&lt;/p&gt;
&lt;p&gt;Basically, turning automatic GC off means any amount of history rewriting is always undoable. &lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;A simple call to &lt;code&gt;git reflog&lt;/code&gt; will show you all recent commits, including ones that are no longer on any branch.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;$ git reflog
98a9b6b HEAD@{1}: commit (amend): Increase footer font-size and line-height
edc192c HEAD@{2}: commit: Increase footer line height
0360c78 HEAD@{3}: commit: Update footer color to quiet lighthouse
9231f91 HEAD@{4}: commit: Burn down a bunch of old css
…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, commit &lt;code&gt;edc192c&lt;/code&gt;  became &lt;code&gt;98a9b6b&lt;/code&gt; when it was amended. &lt;/p&gt;
&lt;p&gt;To undo that &lt;em&gt;amend&lt;/em&gt;, I can reset the HEAD of my current branch back to  &lt;code&gt;edc192c&lt;/code&gt;.  I have no fear of &lt;code&gt;edc192c&lt;/code&gt; ever randomly disappearing before I get around to it.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Turning off GC is a safety net that has saved me many times.&lt;/p&gt;
&lt;p&gt;Turning off automatic GC means you could safely work on a detached HEAD if you're wild like that. You probably shouldn't do that unless you know exactly what you're doing, but you certainly could.&lt;/p&gt;
&lt;h2&gt;And of course…&lt;/h2&gt;
&lt;p&gt;Keep in mind, you can always just run GC manually if you feel like a lot of garbage has piled up.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;$ git gc&lt;/code&gt;&lt;/pre&gt;</description><author>Donat Studios</author><pubDate>Sat, 13 Nov 2021 12:43:37 GMT</pubDate><guid isPermaLink="true">https://donatstudios.com/yagni-git-gc</guid></item><item><title>Things You Are Allowed To Do, Academic Edition</title><link>https://bastian.rieck.me/blog/2021/things/</link><description>&lt;p&gt;Since it&amp;rsquo;s application season again, I thought that I would share some
things that you are &amp;lsquo;allowed&amp;rsquo; to do in academia but may not be aware of.
This post is inspired by
&lt;a href="https://milan.cvitkovic.net/"&gt;Milan Cvitkovic&amp;rsquo;s&lt;/a&gt;
post on &lt;a href="https://milan.cvitkovic.net/writing/things_youre_allowed_to_do"&gt;&amp;lsquo;Things you&amp;rsquo;re allowed to do&amp;rsquo;&lt;/a&gt; and &lt;a href="https://veronikach.com/"&gt;Veronika Cheplygina&amp;rsquo;s&lt;/a&gt; posts
on advice for Ph.D. students, in particular her post &lt;a href="https://veronikach.com/phd-advice/7-things-i-wish-i-had-done-during-my-phd/"&gt;&amp;lsquo;7 things I wish I had done during my PhD&amp;rsquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Interpolating between the two posts, I thus present you a list of seven
items that you might want to try out if you are somehow connected to
academia.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Give someone feedback on a preprint or a paper you liked&lt;/strong&gt;. Academics
are mostly used to negative feedback loops: reviewers criticising
a paper, evaluators criticising a proposal, etc. Why not flip it
around for a change and provide positive feedback? It does not have
to be long, but a brief, genuine message along the lines of &amp;lsquo;Your
paper on &lt;code&gt;X&lt;/code&gt; was really inspiring.&amp;rsquo; may go a long way, in particular
if you are writing it to an early-career researcher!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ask someone for access to a paper or its materials&lt;/strong&gt;. If you want
to read a paper but cannot find an accessible version, most authors
will be more than happy to accommodate your request. We are not paid
anything by the publishers when you buy our paper from them, by the
way.&lt;/p&gt;
&lt;p&gt;(This piece of advice also pertains to other materials in a paper,
such as code or even the original sources for figures. In the worst
case, your request will be ignored.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; I have shared a lot of my own
materials in the past and I am always happy to see stuff I created
being actually useful to people.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ask someone for a potential collaboration&lt;/strong&gt;. This one might require
more guts, but can be even more rewarding. If you know that someone
has a specific set of skills that would come in handy for your
project, why not try to include them and ask them for
a collaboration?&lt;/p&gt;
&lt;p&gt;When you do this, however, make sure that your request is &lt;em&gt;as
specific as possible&lt;/em&gt;. The chances that you will receive a response
will go up immediately. At the same time, you should also be prepared
to receive &lt;em&gt;no&lt;/em&gt; response; especially the &amp;lsquo;big shots&amp;rsquo; in every
field are probably inundated  by both external and internal
collaboration requests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ask someone to mentor you&lt;/strong&gt;. Similar to the previous point, this
one might not get you a direct response. But if you are looking for
some advice and  think you know exactly the person that should give
it to you, reach out to them and &lt;em&gt;tell them&lt;/em&gt;. Again, be aware that
mentoring is a large time commitment, so you might not always get
a positive response&amp;mdash;but it&amp;rsquo;s worth a try!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Invite someone for a workshop or another event&lt;/strong&gt;. If you are
planning an event of any sort, why not consider inviting someone
whose work you always admired? You would be surprised about the
response rate. There are already programmes such as &lt;a href="https://www.skypeascientist.com/"&gt;Skype
a Scientist&lt;/a&gt; that aim to connect
researchers with the general public, but if you want to have
a specific person at your event, why not invite them directly?
You could invite them to be speakers for your reading group, your
workshop, your lab meeting, &amp;hellip;&lt;/p&gt;
&lt;p&gt;Previously, this used to be much more problematic as it required
a large amount of logistics, but now that many meetings are of a virtual
or hybrid nature, there is a higher probability that people can
easily join your event. Use that to your advantage!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Contribute to someone&amp;rsquo;s research project&lt;/strong&gt;. Did you find an error
in a paper? Were you able&amp;mdash;or unable&amp;mdash;to reproduce certain results?
Did you like the code? Even if something has been published, most
authors will be happy to learn that someone is using their project or
thinking more deeply about it.&lt;/p&gt;
&lt;p&gt;Contributing to someone&amp;rsquo;s (research) project, even if it&amp;rsquo;s only
a seemingly small contribution, can be a nice way to brighten their
day. Personally, I am delighted when someone opens a pull request on
one of my projects, because that implies that the project was useful
to at least one other person!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Acknowledge someone&lt;/strong&gt;. If someone had a positive impact on you in
any way, why not take the time to acknowledge that person? Academia
currently only &amp;lsquo;permits&amp;rsquo; acknowledgements in dissertations or papers,
but that should not be stopping you!&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; You could have &lt;a href="https://bastian.rieck.me/acknowledgments/"&gt;a separate
page&lt;/a&gt; or write a nice personalised
message, for instance. Even if you do not receive a response, being
aware of the positive things in your life makes you more grateful and
more balanced in general!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hope you try out a few of these things and find them useful!
&lt;a href="https://bastian.rieck.me/about"&gt;Let me know&lt;/a&gt; if I missed your personal favourite &amp;lsquo;permission&amp;rsquo;
in the list. Until next time!&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Probably due to time reasons or because we lost control of our
inbox.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Moreover, since academia consists of people like you and me, we
should also get a vote about what &lt;em&gt;is&lt;/em&gt; and &lt;em&gt;is not&lt;/em&gt; allowed.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Sat, 13 Nov 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/things/</guid></item><item><title>Empty Handshakes</title><link>https://nevesnunes.github.io/blog/2021/11/13/Empty-Handshakes.html</link><description/><author>onKeyPress</author><pubDate>Sat, 13 Nov 2021 11:07:23 GMT</pubDate><guid isPermaLink="true">https://nevesnunes.github.io/blog/2021/11/13/Empty-Handshakes.html</guid></item><item><title>Eurovision Song Contest: The Story of Fire Saga</title><link>https://olshansky.info/movie/eurovision_song_contest_the_story_of_fire_saga/</link><description>Olshansky's review of Eurovision Song Contest: The Story of Fire Saga</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 13 Nov 2021 09:57:13 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/eurovision_song_contest_the_story_of_fire_saga/</guid></item><item><title>for vs list comprehension</title><link>https://blog.yelinaung.com/posts/things-i-read-13th-nov-2021/</link><description>&lt;p&gt;This is me writing down my discussion with someone from work regarding &lt;code&gt;for&lt;/code&gt; loop vs list comprehension in Python.&lt;/p&gt;
&lt;p&gt;During a code review, I was commenting on a chunk of code to change from &lt;code&gt;for&lt;/code&gt; loop to a list comprehension.
We also talked about &lt;em&gt;why&lt;/em&gt; I wanted it that way.
One reason is list comprehension could be (arguably) more readable than a &lt;code&gt;for&lt;/code&gt; loop. For a simple loop, that might be the case.
Another reason is list comprehension is &lt;em&gt;faster&lt;/em&gt; than the for loop. How fast? I have never tried comparing them before.
So I made a few examples to showcase that. These are from the excellent &lt;a href="https://switowski.com/blog/for-loop-vs-list-comprehension"&gt;For Loop vs List Comprehension article&lt;/a&gt;.&lt;/p&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Sat, 13 Nov 2021 06:07:13 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/posts/things-i-read-13th-nov-2021/</guid></item><item><title>A brief hypothesis about "normies"</title><link>https://www.marginalia.nu/log/39-normie-hypothesis/</link><description>&lt;p&gt;The phenomenon of &amp;ldquo;normies&amp;rdquo; is an interesting one. The term itself is a bit problematic and not one I&amp;rsquo;d typically use, but as a phenomenon they are still worth investigating.&lt;/p&gt;
&lt;p&gt;Their perhaps biggest distinguishing feature is that they don&amp;rsquo;t get &amp;ldquo;it&amp;rdquo;, whatever it is. It&amp;rsquo;s tempting to think that these are an especially mindless type of person with no personality and little in terms of thought going on.&lt;/p&gt;
&lt;p&gt;I have a theory that normies may not actually exist. That is, you can&amp;rsquo;t actually show me a person that is a normie. They are a mirage.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sat, 13 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/39-normie-hypothesis/</guid></item><item><title>Systems, Scale, Value</title><link>https://www.evalapply.org/posts/systems-scale-value/index.html</link><description>Creating things is a delicate endeavour, fraught with peril. People struggle forward through crazy marketplace and environmental complexities just to get from one day to the other. Yet I can't shake off the feeling that we make it harder for ourselves than it should be. I've been trying to work out why. There's a lot to unpack. This post is a start at thinking about it in public.</description><author>Aditya Athalye's Eval/Apply Blog</author><pubDate>Sat, 13 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.evalapply.org/posts/systems-scale-value/index.html</guid></item><item><title>The Golden Bough</title><link>https://godsip.club/articles/the-golden-bough/</link><description>&lt;h1 id="an-unexpected-journey"&gt;An Unexpected Journey &lt;a class="anchor" href="#an-unexpected-journey"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;First of all, &lt;em&gt;thank you&lt;/em&gt; if you are reading this. It will be different from the other blog articles I have in mind: as an introduction, I want to let you know how the idea about this website was born and what it is meant to be – in my mind, at least.&lt;/p&gt;
&lt;p&gt;Now, I’d like to point out that I usually didn’t like talking about religion — or generally, that &lt;em&gt;I didnt’ like religions&lt;/em&gt;.
Despite that, in the summer of 2021, a friend of mine, a neighbor, while chatting about religion(!) and myths, recommended a text: “You should read &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Golden_Bough" rel="noopener" target="_blank"&gt;The Golden Bough&lt;/a&gt;&lt;/em&gt; by Frazer. Therein, there is &lt;strong&gt;everything&lt;/strong&gt;”.
I never heard about Frazer before, so I immediately searched on Wikipedia. The book abstract looked interesting, but not great: starting from the legend behind a painting, the author explores the reasons of a brutal yet methodical ritual that was committed in central Italy, in the woods near Aricia (&lt;em&gt;Ariccia&lt;/em&gt; in modern Italian).&lt;/p&gt;
&lt;figure&gt;&lt;img alt="&amp;ldquo;The Golden Bough&amp;rdquo; by J. M. W. Turner, which ingnited James G. Frazer&amp;rsquo;s curiosity." src="https://godsip.club/img/golden-bough-painting.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;&amp;ldquo;The Golden Bough&amp;rdquo; by J. M. W. Turner, which ingnited James G. Frazer&amp;rsquo;s curiosity.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;‘Cool, I know that area,’ I thought while my mind immediately went to the famous &lt;a href="https://it.wikipedia.org/wiki/Porchetta_di_Ariccia" rel="noopener" target="_blank"&gt;porchetta&lt;/a&gt;. Since it was very cheap, I immediately bought the volume; only the next day, when I could hold it in my hands, I realised how massive it was. In that pocket edition, it is still a 840-page bold compendium about lore. Nevertheless, I wasn’t discouraged and slowly started flipping through its pages.&lt;/p&gt;
&lt;h1 id="a-new-perspective"&gt;A New Perspective &lt;a class="anchor" href="#a-new-perspective"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;After a while, it was clear to me that it was necessary to take notes about that &lt;em&gt;enormous&lt;/em&gt; amount of information. Until then, I used a paper &lt;em&gt;moleskine-like&lt;/em&gt; notebook filled with bad writings, but in this case it would have been a mess. Luckily, in those days I became aware of &lt;a href="https://obsidian.md/" rel="noopener" target="_blank"&gt;Obsidian&lt;/a&gt;, a powerful tool which allows you to link easily one note with another, so I immediately started writing down annotations.&lt;/p&gt;
&lt;p&gt;The first hundreds of pages introduce the story of Aricia’s King of the Wood (a high-priest) and the mysterious ritual regarding his succession: he was killed violently using a blade, every year. To simplify, the question is: why using such violence in the Classical era (Greek and Roman), when barbarian traditions should have been abandoned? It was a very interesting question, but the book rhythm is very – VERY – slow. To do a comparison, it was like asking: ‘After how much time should a pancake be flipped on the pan?’ and answering: ‘Well, I’ll first explain why Earth is round’. To be honest, my interest grew very slowly through the first half of the book.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;It was like asking: ‘After how much time should a pancake be flipped on the pan?’ and answering: ‘Well, I’ll first explain why Earth is round’.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My enthusiasm was dropping quicker than I expected, but then one of the great strong points of the tome came out: &lt;em&gt;logical connections&lt;/em&gt;. Some terrific observations started to make me think and the interest was kept alive with examples and hypotheses: one above all, the &lt;strong&gt;evolution of magic into religion&lt;/strong&gt;. I began to really appreciate the author’s style, but anyway it was a very time-consuming read.&lt;/p&gt;
&lt;h1 id="therein-there-is-everything"&gt;Therein, There Is Everything &lt;a class="anchor" href="#therein-there-is-everything"&gt;#&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;Work duties needed attention, Summer passed and Fall came quickly. Leaves were already on the ground when I opened again the tome; but something had changed in the meanwhile. During these last few weeks –or better, days– I finished the book with a sprint. In the last two-hundred pages, as the end of the journey was approaching, I realised what the big change was: the author’s talent as teacher erupted like a volcano.&lt;/p&gt;
&lt;p&gt;I was aware that Frazer’s intention was to explain the common roots of traditions of civilisations from all the globe since the first pages, but only towards the end I became conscious of his great ability to gradually prepare the reader to accept his hypotheses. Of course, he humbly repeats that he could be wrong through his passages; but while the two of them (author and reader) finally close the loop in the very final chapters, everything makes perfect sense. For me, the last pages were almost emotional after such a long trip through time and space!&lt;/p&gt;
&lt;figure&gt;&lt;img alt="A little spoiler about the book – meme edition." src="https://godsip.club/img/sun-always-has-been.jpg" /&gt;&lt;figcaption&gt;
      &lt;p&gt;A little spoiler about the book – meme edition.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Now, despite the huge pleasure that it gave me, I don’t know if I would ever suggest everyone to read &lt;em&gt;The Golden Bough&lt;/em&gt;. I’d only make them angry because it can be &lt;strong&gt;so heavy&lt;/strong&gt;. But that’s when the idea for this website came it. Wouldn’t a collection of cool findings about folklore be awesome? An explanation (not to be taken too seriously) about, for instance, why the &lt;em&gt;oak tree&lt;/em&gt; is so important for many civilisations? Why Vampirism was accepted in all the continents? Or, simply, a fun fact about why the ocean’s low tide is considered a bad omen?&lt;/p&gt;
&lt;p&gt;Out there, in the internet wilderness, it’s full of blogs like this. But this is &lt;em&gt;mine&lt;/em&gt;, and it will be replenished of interesting notes as long as I’ll keep doing –in my opinion– remarkable discoveries in my spare time. I &lt;em&gt;LOVE&lt;/em&gt; reading and writing about these topics, so I hope to shake up your interest.&lt;/p&gt;
&lt;p&gt;Last thing: if I got your attention, you could consider reading or downloading &lt;em&gt;The Golden Bough&lt;/em&gt; for free on &lt;a href="https://www.gutenberg.org/ebooks/3623" rel="noopener" target="_blank"&gt;Project Gutenberg&lt;/a&gt; – I hope you will be hooked as I was.&lt;/p&gt;</description><author>Godsip Club</author><pubDate>Sat, 13 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://godsip.club/articles/the-golden-bough/</guid></item><item><title>Benchmarking esbuild, swc, tsc, and babel for React/JSX projects</title><link>http://notes.eatonphil.com/benchmarking-esbuild-swc-typescript-babel.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-11-13-benchmarking-esbuild-swc-typescript-babel.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Sat, 13 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/benchmarking-esbuild-swc-typescript-babel.html</guid></item><item><title>Living longer with THESE social media? NOT good</title><link>https://stop.zona-m.net/2021/11/living-longer-with-these-social-media-not-good/</link><description>&lt;p&gt;I mean, just look at the Spacers.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 12 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/living-longer-with-these-social-media-not-good/</guid></item><item><title>JDK Flight Recorder Events in GraalVM Native Binaries</title><link>https://www.morling.dev/blog/jfr-events-in-graalvm-native-binaries/</link><description>&lt;div class="paragraph"&gt;
&lt;p&gt;If you have followed this blog for a while,
you’ll know that I am a big fan of &lt;a href="https://openjdk.java.net/jeps/328"&gt;JDK Flight Recorder&lt;/a&gt; (JFR),
the low-overhead diagnostics and profiling framework built into the HotSpot Java virtual machine.
And indeed, until recently, this meant &lt;em&gt;only&lt;/em&gt; HotSpot:
Folks compiling their Java applications into &lt;a href="https://www.graalvm.org/reference-manual/native-image/"&gt;GraalVM native binaries&lt;/a&gt; could not benefit from all the JFR goodness so far.&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Fri, 12 Nov 2021 19:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/jfr-events-in-graalvm-native-binaries/</guid></item><item><title>Old and New</title><link>https://www.marginalia.nu/log/38-old-and-new/</link><description>&lt;p&gt;I&amp;rsquo;ve been thinking recently about the emphasis put on &amp;ldquo;new&amp;rdquo;, specifically for search engines, but the discussion has some merit even in a wider context. I will start wide and narrow down.&lt;/p&gt;
&lt;p&gt;It is common to conflate new with good, and most being young sometime between 1950-2000 will indeed have seen marvellous improvements in quality of life and technology with each passing year. In the light of that, it&amp;rsquo;s at least easy to explain how one might confuse the two.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 12 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/38-old-and-new/</guid></item><item><title>Italia IV - Pompeii &amp;amp; Positano</title><link>https://faingezicht.com/photos/2021/11/12/positano/</link><description>After a few days in Rome, we drove South. We stopped at Pompeii on our way to the Amalfi Coast, where we spent a few days in Positano.

_This is the last in a series shared throughout Fall 2021. When you're done here, make sure to also check out parts &lt;a href="/photos/2021/10/14/umbria/"&gt;one&lt;/a&gt;, &lt;a href="/photos/2021/10/19/florence/"&gt;two&lt;/a&gt;, &lt;a href="/photos/2021/10/31/roma/"&gt;three&lt;/a&gt;, and &lt;a href="/photos/2021/12/02/italia-revisited/"&gt;five&lt;/a&gt;._</description><author>Avy Faingezicht</author><pubDate>Fri, 12 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/11/12/positano/</guid></item><item><title>Making technology choices</title><link>https://sebinsua.com/making-technology-choices</link><description>&lt;p&gt;Back in 2010, the adage “the right tool for the job” was &lt;a href="https://hn.algolia.com/?dateEnd=1293753600&amp;amp;dateRange=custom&amp;amp;dateStart=1225238400&amp;amp;page=1&amp;amp;prefix=false&amp;amp;query=%22use%20the%20right%20tool%20for%20the%20job%22&amp;amp;sort=byPopularity&amp;amp;type=comment"&gt;prevalent within the developer community&lt;/a&gt;, but by 2015 it was seen by some as motivated reasoning and in general harmful advice. Nowadays it’s common to hear that we should &lt;a href="https://mcfunley.com/choose-boring-technology"&gt;“Choose Boring Technology”&lt;/a&gt; and &lt;a href="https://twitter.com/dorkitude/status/1446170171694010373"&gt;“optimize our engineering decisions for iteration speed”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My view is that the adage “the right tool for the job” suffered from being transmitted by engineers in an almost folkloric manner. It’s a truism (“&lt;em&gt;obviously&lt;/em&gt; you want to pick the right tool!”) but in practice was often wrong since shoe-horning an old bit of common sense advice into the domain of software engineering gave licence to the self-interests of software engineers who could avoid thinking about its costs (operational complexity, training/ramp-up costs of employees, etc).&lt;/p&gt;
&lt;p&gt;On the other hand, “Choose Boring Technology” treats technology choices as if the only system they interact with is a technical one and human incentives can be disregarded — it pays no attention to why “the right tool” was so-often endorsed, and how this related to the polyglot movement (both were, in a sense, marketing that allowed startups and employees that wanted to chart uncharted waters to find each other). Boring is also a misnomer — what is meant is “well-understood” but the word choice is being used to frame the domain as one in which technologies can either be “shiny” or “boring” — which isn’t the primary frame that I’d use to evaluate technology.&lt;/p&gt;
&lt;p&gt;While reducing operational complexity, using technologies that are flexible and avoiding unknown-unknowns are all important and suggest that “Choose Boring Technology” is a sensible approach, we should also pay careful attention to whether our choices result in happy developers. Retention is important to the long-term maintenance of knowledge within a company, and developers that don’t feel like they’re learning anything new or that feel stifled by centralised decision-making over the tools they’re allowed to use can end up leaving due to feeling disrespected and disenchanted. On the other hand, there’s also “retention risk” in attracting developers that seek shiny technologies as once these technologies have lost their shine they can leave just as eagerly as they joined. Additionally, in many cases there are technologies that offer an order of magnitude faster/better ways of doing things, and so the best teams will make judgments over &lt;em&gt;when&lt;/em&gt; to let their engineers invest their time seeking improvements.&lt;/p&gt;
&lt;p&gt;Unchecked restraint in technology choices can eventually lead towards obsolescence and brain drain. Following &lt;a href="https://charity.wtf/2018/12/02/software-sprawl-the-golden-path-and-scaling-teams-with-agency/"&gt;“The Golden Path”&lt;/a&gt; and creating a team to centralise technical decisions is a good idea when a company reaches a particular scale, but you must also work out how you’ll create affordances for developers outside of that team to go above the predefined baseline of quality (for a similar idea read &lt;em&gt;Lethain’s&lt;/em&gt; &lt;a href="https://lethain.com/pierceable-abstractions/"&gt;“Providing pierceable abstractions”&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;There are also differences between individuals and differences due to scale (personal project, team, company-wide, etc):&lt;/p&gt;
&lt;p&gt;At the individual level, while I might reach into my toolbox and find that my most well-worn tool is JavaScript and use that, another might reach into their toolbox and pick Ruby. We should use what we know best. But what if you’re just starting out? If I was a beginner I would pick up-and-coming general-purpose technologies that are growing in usage. While I might benefit from prior knowledge in an older ecosystem, I will learn more by being part of a highly-engaged community of people trying to achieve state-of-the-art approaches.&lt;/p&gt;
&lt;p&gt;But at the scale of a company, there are further problems to be aware of due to the difficulty of migrating software stacks and a tendency for policies and processes that don’t work well to be left unfixed.&lt;/p&gt;
&lt;p&gt;What should we recommend to a company that has always written both their application and database logic using SQL Server Stored Procedures? The boring choice might be for them to continue extending and creating new software in this way, but since there is very little beginner interest in learning that stack eventually they will run into hiring problems and brain drain caused by an ageing workforce (institutional knowledge loss). To survive, they will need to rework their system in a way that gives them access to a more fungible workforce, and they will need to do this without interrupting the business.&lt;/p&gt;
&lt;p&gt;Another common problem at large companies is for there to be policies and processes around evaluating and recording technical decisions but for these to not achieve anything other than getting developers to copy-and-paste the marketing claims of a new technology into a document without consideration of its context/impact within the organisation. These policies can often be empty ceremonies with no impact on the actual decision-making process that allow developers to rubber-stamp their desire to learn new technologies.&lt;/p&gt;
&lt;p&gt;So how would I make technology choices?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use well-understood tools. Either those you personally know well or those that are known well within the broader technology community.&lt;/li&gt;
&lt;li&gt;Use the smallest set of tools you can to solve your problem(s).&lt;/li&gt;
&lt;li&gt;Pay close attention to the happiness of your developers. I’m not suggesting that you should always let the whims of developers dictate a company’s overall technology choices, but you should be aware of the market incentives of your employees. In any case, an important job of engineering leaders is to find ways to carve out interesting work from within boring/legacy systems.&lt;/li&gt;
&lt;li&gt;Benchmark yourself against you competitors but if a problem is core or complementary to your business look for ways to use technology to gain advantage.&lt;/li&gt;
&lt;li&gt;Try to find the line between policies that are too onerous for developers and those that are not rigorous enough to provide any benefit to the organisation. Non-rigorous decision making processes are really common and generally when a policy isn’t providing its stated aim it should be gotten rid of. However, if we want to improve decision-making within a large company this can probably be done with structure, asynchronous collaboration and good guiding questions (e.g. Can this be achieved with technologies we already use? What if true could invalidate the benefit given by this technology? Will this require a migration? Who will operate this? Etc).&lt;/li&gt;
&lt;/ul&gt;</description><author>@sebinsua</author><pubDate>Fri, 12 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://sebinsua.com/making-technology-choices</guid></item><item><title>An idea whose time may be close: battery-powered TRAINS</title><link>https://stop.zona-m.net/2021/11/an-idea-whose-time-may-be-close-battery-powered-trains/</link><description>&lt;p&gt;Even better than electric cars, I&amp;rsquo;d say.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 11 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/an-idea-whose-time-may-be-close-battery-powered-trains/</guid></item><item><title>A Jaunt Through Keyword Extraction</title><link>https://www.marginalia.nu/log/37-keyword-extraction/</link><description>&lt;p&gt;Search results are only as good as the search engine&amp;rsquo;s ability to figure out what a page is about. Sure a keyword may appear in a page, but is it the topic of the page, or just some off-hand mention?&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t really know anything about data mining or keyword extraction starting out, so I&amp;rsquo;ve had to learn on the fly. I&amp;rsquo;m just going to briefly list some of my first naive attempts at keyword extraction, just to give a context.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Thu, 11 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/37-keyword-extraction/</guid></item><item><title>Do The Scariest Thing First</title><link>http://live.julik.nl/2021/11/do-the-scariest-thing-first</link><description/><author>julik live</author><pubDate>Thu, 11 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">http://live.julik.nl/2021/11/do-the-scariest-thing-first</guid></item><item><title>Why did Facebook really shut down face recognition?</title><link>https://stop.zona-m.net/2021/11/why-did-facebook-really-shut-down-face-recognition/</link><description>&lt;p&gt;Probably because they have better replacement for it.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 11 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/why-did-facebook-really-shut-down-face-recognition/</guid></item><item><title>Aksel Waldemar Johannessen</title><link>https://blog.vfiles.no/posts/aksel-waldemar-johannessen/</link><description>&lt;p&gt;Aksel Waldemar Johannessen (1880-1922) was a Norwegian expressionist, painter, and graphic designer. In Norway he is often referred to as &amp;ldquo;the forgotten artist&amp;rdquo; and was only praised by critics after his death. Johannessens art was noticed by &lt;a href="https://en.wikipedia.org/wiki/Edvard_Munch"&gt;Edvard Munch &lt;/a&gt;and two famous art critics at the time, Dagbladet&amp;rsquo;s &lt;a href="https://en.wikipedia.org/wiki/Jappe_Nilssen"&gt;Jappe Nilssen&lt;/a&gt; and the cultural profile &lt;a href="https://en.wikipedia.org/wiki/Olav_Dalgard"&gt;Olav Dalgard&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="center"&gt;
&lt;img alt="Painting of Aksel Waldemar Johannessen." src="https://blog.vfiles.no/media/Aksel_Waldemar_Johannessen.jpg" width="450" /&gt;
&lt;figcaption&gt;
&lt;p&gt;
Self-portrait of Aksel W. Johannessen.
&lt;a href="https://commons.wikimedia.org/wiki/File:Aksel_Waldemar_Johannessen_%28self-portrait%29.jpg"&gt;
Public domain, CC0.
&lt;/a&gt;
&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="early-life-and-education"&gt;Early life and education&lt;/h2&gt;
&lt;p&gt;Aksel Waldemar Johannessen was born October 26, 1880 in Christiania (today &lt;a href="https://en.wikipedia.org/wiki/Oslo"&gt;Oslo&lt;/a&gt;), Norway. He grew up at &lt;a href="https://en.wikipedia.org/wiki/Kampen,_Norway"&gt;Kampen&lt;/a&gt;, where his father, Julius Johannesen, was a shoemaker and auxiliary policeman. He was connected early to the artist community at Hammersborg behind the &lt;a href="https://en.wikipedia.org/wiki/Trinity_Church_%28Oslo%29"&gt;Trinity Church&lt;/a&gt;. In the period of 1900 and 1907, he become educated as an artist, including seven semesters at the &lt;a href="https://en.wikipedia.org/wiki/Norwegian_National_Academy_of_Craft_and_Art_Industry"&gt;Norwegian National Academy of Craft and Art Industry&lt;/a&gt;, the last years with &lt;a href="https://no.wikipedia.org/wiki/Lars_Utne"&gt;Lars Utne&lt;/a&gt; in the sculptor class. He learned the art of woodcarving in the workshop of the internationally famous &lt;a href="https://nkl.snl.no/John_Borgersen"&gt;Johan Borgersen&lt;/a&gt;.&lt;/p&gt;</description><author>Posts on A blog</author><pubDate>Wed, 10 Nov 2021 13:44:38 GMT</pubDate><guid isPermaLink="true">https://blog.vfiles.no/posts/aksel-waldemar-johannessen/</guid></item><item><title>Plain Org has joined the chat (iOS)</title><link>https://xenodium.com/plain-org-has-joined-the-chat</link><description>&lt;p&gt;The App Store is a crowded space when it come to markdown apps. A quick search yields a wonderful wealth of choice. Kinda overwhelming, but a great problem to have nonetheless.&lt;/p&gt;
&lt;p&gt;For those of us with org as our markup of choice, the App Store is far less crowded. I wish we could fill more than a screen's worth of search results, so you know… I could show you another pretty gif scrolling through org results. For now, we'll settle on a single frame showcasing our 4 org options.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-has-joined-the-chat/store-side-comparison-mid.gif" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://beorg.app/"&gt;Beorg&lt;/a&gt;, &lt;a href="http://mobileorg.github.io/"&gt;MobileOrg&lt;/a&gt;, &lt;a href="https://flathabits.com/"&gt;Flat Habits&lt;/a&gt;, and &lt;a href="https://orgro.org/"&gt;Orgro&lt;/a&gt; are all great options. Each with strengths of their own. &lt;a href="https://organice.200ok.ch/"&gt;Organice&lt;/a&gt;, while not on the App Store, is another option for those looking for a web alternative. Of these, I had already authored one of them. More on that in a sec… You see, about a year ago I wanted to play with Swift, SPM, and lsp itself. Also, having Swift code completion in Emacs via &lt;a href="https://github.com/emacs-lsp/lsp-sourcekit"&gt;lsp-sourcekit&lt;/a&gt; sounded like a fun thing to try out, so I started using it while writing a Swift org parser.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-has-joined-the-chat/magit.png" /&gt;&lt;/p&gt;
&lt;p&gt;While working on the parser, I happened to be reading &lt;a href="https://jamesclear.com/atomic-habits"&gt;Atomic Habits&lt;/a&gt; (awesome book btw)… It was also a great time to play around with SwiftUI, which by the way, is pretty awesome too. With Atomic Habits fresh in mind, org parser in one hand, and SwiftUI in the other, I built &lt;a href="https://flathabits.com"&gt;Flat Habits&lt;/a&gt;: a lightweight habit tracker powered by org.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/frictionless-org-habits-on-ios/flat_habits.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I love being able to save habit data to plain text and easily track on my iPhone (via Flat Habits) or laptop (via Emacs). I wanted to &lt;a href="https://xenodium.com/org-habits-on-ios-check-tasks-youre-next/"&gt;extend similar convenience to org tasks&lt;/a&gt;, so I built &lt;a href="https://plainorg.com"&gt;Plain Org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My previous &lt;a href="https://xenodium.com/org-habits-on-ios-check-tasks-youre-next/"&gt;post&lt;/a&gt; mentioned &lt;em&gt;quickly adding new tasks and searching existing ones&lt;/em&gt; as Plain Org's driving goals. Of course, neither of those are as useful without automatic cloud syncing, so pluging into &lt;a href="https://support.apple.com/en-gb/HT206481#thirdparty"&gt;iOS's third party cloud support&lt;/a&gt; was a must-have.&lt;/p&gt;
&lt;p&gt;With these baseline features in place, I &lt;a href="https://www.reddit.com/r/orgmode/comments/p5bonn/ios_plain_org_alpha_builds_now_on_testflight_dm/"&gt;started an alpha/beta group&lt;/a&gt; via &lt;a href="https://testflight.apple.com/"&gt;TestFlight&lt;/a&gt;. Early Plain Org adopters have been wonderfully supportive, given lots of great feedback, and helped shape the initial feature set you see below.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;There's plenty more that can be supported, but hey let's get v1 out the door. Gotta start somewhere.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Plain Org v1 features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;View and edit your org mode tasks while on the go.&lt;/li&gt;
&lt;li&gt;Beautifully rendered org markup.&lt;/li&gt;
&lt;li&gt;Sync your org files using your favorite cloud provider.&lt;/li&gt;
&lt;li&gt;Create new files.&lt;/li&gt;
&lt;li&gt;Outline-style editing with toolbar
&lt;ul&gt;
&lt;li&gt;Keywords&lt;/li&gt;
&lt;li&gt;Indent&lt;/li&gt;
&lt;li&gt;Priority&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Formatting: bold, italic, underline, strikethrough, verbatim, and code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add links from Safari via share extension.&lt;/li&gt;
&lt;li&gt;Add new tasks via Spotlight.&lt;/li&gt;
&lt;li&gt;Reorder headings via drag/drop.&lt;/li&gt;
&lt;li&gt;Checkboxes
&lt;ul&gt;
&lt;li&gt;Interactive toggling.&lt;/li&gt;
&lt;li&gt;Quickly reset multiple checkboxes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Follow local links.&lt;/li&gt;
&lt;li&gt;Show inline images.&lt;/li&gt;
&lt;li&gt;File-local keywords and visibility.&lt;/li&gt;
&lt;li&gt;Filter open/closed tasks.&lt;/li&gt;
&lt;li&gt;Show/hide stars.&lt;/li&gt;
&lt;li&gt;Edit raw text.&lt;/li&gt;
&lt;li&gt;Light/dark mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Plain Org joins the chat&lt;/h2&gt;
&lt;p&gt;Today Plain Org joins the likes of &lt;a href="https://beorg.app/"&gt;Beorg&lt;/a&gt;, &lt;a href="http://mobileorg.github.io/"&gt;MobileOrg&lt;/a&gt;, &lt;a href="https://flathabits.com/"&gt;Flat Habits&lt;/a&gt;, and &lt;a href="https://orgro.org/"&gt;Orgro&lt;/a&gt; on the App Store.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-has-joined-the-chat/intro.png" /&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;center&gt;
  &lt;a href="https://apps.apple.com/app/id1578965002"&gt;
    &lt;img alt="download-on-app-store.png" src="https://xenodium.github.io/images/flat-habits-for-ios/download-on-app-store.png" width="180px" /&gt;
  &lt;/a&gt;
&lt;/center&gt;
&lt;center&gt;
  This post was written in   &lt;a href="https://orgmode.org"&gt;org mode&lt;/a&gt;.
&lt;/center&gt;</description><author>xenodium.com @alvaro</author><pubDate>Wed, 10 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/plain-org-has-joined-the-chat</guid></item><item><title>In the beginning, was the domain name</title><link>https://www.evalapply.org/posts/hello-world/index.html</link><description>How this blog came to be is a minor miracle. Long story short, I conned myself into believing nobody will find /and/ read it. But you're here, aren't you? And you're reading this. Aren't You? Confucamus. Well, here's how you got here.</description><author>Aditya Athalye's Eval/Apply Blog</author><pubDate>Wed, 10 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.evalapply.org/posts/hello-world/index.html</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #45</title><link>https://justingarrison.com/123dev/2021-11-10-123dev-45/</link><description>Some weeks I love the structure of this newsletter. It helps me not dump a ton of information but focus on just a few things each week. Other weeks—like this week—I have a ton of cool tools and articles to share but can't if I want to follow my own rules.</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 10 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-11-10-123dev-45/</guid></item><item><title>Longboard 2</title><link>https://mfashby.net/posts/2021-11-09-longboard-2/</link><description/><author>Home on mfashby.net</author><pubDate>Tue, 09 Nov 2021 23:56:41 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-11-09-longboard-2/</guid></item><item><title>Styleable Inline SVG Icons, with Caching &amp;amp; Fallback</title><link>https://duncanlock.net/blog/2021/11/09/styleable-inline-svg-icon-sprite-system-with-caching-fallback/</link><description>&lt;figure class="image-block"&gt;&lt;img alt="Tag Icon in the shape of a luggage tag" height="300px" src="https://duncanlock.net/images/icons/fa/solid/tag.svg" width="300px" /&gt;
&lt;figcaption&gt;Figure 1. This pages tag list is using &lt;a href="https://fontawesome.com/v5.15/icons/tag?style=solid"&gt;this tag icon from FontAwesome&lt;/a&gt;. This is a 550 byte &lt;span class="caps"&gt;SVG&lt;/span&gt; file, 346 bytes gzipped.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;If you want to use &lt;span class="caps"&gt;SVG&lt;/span&gt; icons on a website &lt;em&gt;and&lt;/em&gt; style them with &lt;span class="caps"&gt;CSS&lt;/span&gt; - then the &lt;span class="caps"&gt;SVG&lt;/span&gt; needs to be &lt;em&gt;inline&lt;/em&gt; - i.e. the &lt;span class="caps"&gt;SVG&lt;/span&gt; markup needs to be included with the rest of the pages &lt;span class="caps"&gt;HTML&lt;/span&gt; markup.&lt;/p&gt;
&lt;p&gt;Unfortunately putting things inline means that they can’t be cached. In this article I’ll show one way to get around this - and get the best of both worlds: inline styleable &lt;span class="caps"&gt;SVG&lt;/span&gt; icons, &lt;em&gt;with caching!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If we take the tag list in the sidebar in this site as an example, the basic markup looks like this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Tags:&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Tag icon goes here --&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/howto.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;howto&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Tag icon goes here --&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/javascript.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;javascript&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When we put the &lt;span class="caps"&gt;SVG&lt;/span&gt; for the icon in, it looks like this:&lt;/p&gt;
&lt;div class="listing-block scrollable"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Tags:&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 512 512"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M0 252.118V48C0 21.49 21.49 0 48 0h204.118a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882L293.823 497.941c-18.745 18.745-49.137 18.745-67.882 0L14.059 286.059A48 48 0 0 1 0 252.118zM112 64c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/howto.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;howto&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 512 512"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M0 252.118V48C0 21.49 21.49 0 48 0h204.118a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882L293.823 497.941c-18.745 18.745-49.137 18.745-67.882 0L14.059 286.059A48 48 0 0 1 0 252.118zM112 64c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48z"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/javascript.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;javascript&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_styling_inline_svg_with_css"&gt;Styling Inline &lt;span class="caps"&gt;SVG&lt;/span&gt; with &lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;As you can see, this &lt;span class="caps"&gt;SVG&lt;/span&gt; markup is fully inline with the rest of the &lt;span class="caps"&gt;HTML&lt;/span&gt;. This means that we can write &lt;span class="caps"&gt;CSS&lt;/span&gt; styles that will style the icon, just like we can for anything else in the &lt;span class="caps"&gt;DOM&lt;/span&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Set the SVGs fill color to match the parent elements color: */&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Set some other properties on the SVG element */&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;vertical-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.125em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Set different icon colours in different places, using CSS variables: */&lt;/span&gt;
&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--icon-color-blueprint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--icon-color-content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/* Have icons in the header &amp;amp; footer change colour on hover: */&lt;/span&gt;
&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;.active&lt;/span&gt; &lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--blueprint-bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class="sidebar"&gt;&lt;h6 class="block-title"&gt;You can also style individual parts of the &lt;span class="caps"&gt;SVG&lt;/span&gt;, if you want, just like you would &lt;span class="caps"&gt;HTML&lt;/span&gt;:&lt;/h6&gt;&lt;p&gt;You can also give parts of your &lt;span class="caps"&gt;SVG&lt;/span&gt; classes &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; IDs, and the style these just like you would an &lt;span class="caps"&gt;HTML&lt;/span&gt; element with a class or &lt;span class="caps"&gt;ID&lt;/span&gt;. Imagine this example markup:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 120 120"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;rect&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"smallRect"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this example &lt;span class="caps"&gt;CSS&lt;/span&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="nt"&gt;rect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="nf"&gt;#smallRect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;the rectangle would be blue.&lt;/p&gt;&lt;/aside&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_problems_with_inline_svg"&gt;Problems with Inline &lt;span class="caps"&gt;SVG&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;There are some obvious problems with doing &lt;span class="caps"&gt;SVG&lt;/span&gt; icons this way:&lt;/p&gt;
&lt;div class="dlist"&gt;&lt;dl&gt;&lt;dt&gt;It’s more markup&lt;/dt&gt;&lt;dd&gt;Instead of keeping your &lt;span class="caps"&gt;SVG&lt;/span&gt; images in separate files like you would with other image files, you need to include them in your page markup.&lt;/dd&gt;&lt;dt&gt;It’s repetitive&lt;/dt&gt;&lt;dd&gt;Every time you want to use the same icon again, you have to include its markup, again. In the example list above, this is a list of tags - so it repeats the tag icon’s markup for each item in the list.&lt;/dd&gt;&lt;dt&gt;None of this is cached&lt;/dt&gt;&lt;dd&gt;Unlike external files, the browser can’t cache the actual page that’s requested, which means that the &lt;span class="caps"&gt;SVG&lt;/span&gt;’s aren’t cached.&lt;/dd&gt;&lt;/dl&gt;&lt;/div&gt;
&lt;p&gt;The first two aren’t really a problem if you’re using some kind of templating system to generate your markup - but they both make the last one - caching, even more of a problem. The biggest drawback to inline &lt;span class="caps"&gt;SVG&lt;/span&gt; is that it’s not cached by the browser, it’s loaded every single time, along with the rest of the page’s markup.&lt;/p&gt;
&lt;p&gt;Even if each icon is only 500 bytes, with our simple tag list which uses that icon only four times, you’re already up to 2 &lt;span class="caps"&gt;KB&lt;/span&gt; of extra markup. Including the header &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; footer there are currently 22 icons on this page, which even at only 500 bytes each, makes 11 &lt;span class="caps"&gt;KB&lt;/span&gt; of uncached extra markup on every page load. This isn’t &lt;em&gt;huge&lt;/em&gt; - but it’s not ideal either. Can we do better?&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_using_svg_sprites_still_uncached_but_better"&gt;Using &lt;span class="caps"&gt;SVG&lt;/span&gt; Sprites - Still Uncached, but Better&lt;/h2&gt;&lt;p&gt;You can improve this by using an &lt;span class="caps"&gt;SVG&lt;/span&gt; “sprite sheet”:&lt;a class="footnote-ref" href="#_footnote_1" id="_footnoteref_1" title="View footnote 1"&gt;[1]&lt;/a&gt;. This means putting all the &lt;span class="caps"&gt;SVG&lt;/span&gt; markup for all your icons into &lt;em&gt;one&lt;/em&gt; &lt;span class="caps"&gt;SVG&lt;/span&gt; file, adding &lt;em&gt;that&lt;/em&gt; inline in the top of the page somewhere, and then referencing the icons where you want to use them.&lt;/p&gt;
&lt;p&gt;The sprite sheet looks like this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Sprite Sheet, loaded inline somewhere at the top, but hidden: --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: none;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;symbol&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"home"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 512 512"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/symbol&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;symbol&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 512 512"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/symbol&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you want to use one of the icons, just reference it with an &lt;span class="caps"&gt;SVG&lt;/span&gt; &lt;code&gt;use&lt;/code&gt; statement, and it’ll be pulled from the sprite sheet:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, using the sprite sheet, we only include the markup for each icon once, then each time we want to show that icon, we include a tiny &lt;span class="caps"&gt;SVG&lt;/span&gt; use statement &lt;a class="footnote-ref" href="#_footnote_2" id="_footnoteref_2" title="View footnote 2"&gt;[2]&lt;/a&gt;. This means that our example tag list now looks like this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/howto.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;howto&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;svg&amp;gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/javascript.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;javascript&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is obviously much less repetitive and less markup overall - we’re only including each icons markup once now.&lt;/p&gt;
&lt;p&gt;However, the sprite sheet &lt;em&gt;still&lt;/em&gt; needs to be inline if we want to be able to style the icons with &lt;span class="caps"&gt;CSS&lt;/span&gt; and &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; them, so it’s &lt;em&gt;still&lt;/em&gt; loaded every time and &lt;em&gt;still&lt;/em&gt; not cached.&lt;/p&gt;
&lt;p&gt;Can we do better?&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_caching_inline_svg_using_svg_injection"&gt;Caching Inline &lt;span class="caps"&gt;SVG&lt;/span&gt; using &lt;span class="caps"&gt;SVG&lt;/span&gt; Injection&lt;/h2&gt;&lt;p&gt;If we want caching, we have to load the &lt;span class="caps"&gt;SVG&lt;/span&gt; sprite sheet as an external resource, like we would a normal image - but if we do that, it’s not inline anymore, so we lose the ability to style the &lt;span class="caps"&gt;SVG&lt;/span&gt; with &lt;span class="caps"&gt;CSS&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Is there a way to get the best of both worlds, somehow?&lt;/p&gt;
&lt;p&gt;Yes there is, if you use some JavaScript to put your externally loaded &lt;span class="caps"&gt;SVG&lt;/span&gt; &lt;em&gt;back into the &lt;span class="caps"&gt;DOM&lt;/span&gt; and re-inline it&lt;/em&gt;. Doing that looks like this:&lt;/p&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_1_preload_the_sprite_sheet"&gt;1. Preload the sprite sheet&lt;/h3&gt;&lt;p&gt;Preload &lt;a class="footnote-ref" href="#_footnote_3" id="_footnoteref_3" title="View footnote 3"&gt;[3]&lt;/a&gt; the sprite sheet in your site’s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; somewhere, ensuring that it’s loaded early and will be in the cache, ready for the next stage. This reduces jank and makes things more reliable. You can preload the sprite sheet like this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/images/icons/icon_sheet.svg"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_2_load_the_javascript"&gt;2. Load the JavaScript&lt;/h3&gt;&lt;p&gt;Load this JavaScript, somewhere in your site’s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convertSVG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the SVG file from the cache&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;force-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Parse the SVG text and turn it into DOM nodes&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DOMParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/svg+xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;// Pass along any class or IDs from the parent &amp;lt;img&amp;gt; element&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;

      &lt;span class="c1"&gt;// Replace the parent &amp;lt;img&amp;gt; with our inline SVG&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This converts an &lt;span class="caps"&gt;SVG&lt;/span&gt; image element into inline &lt;span class="caps"&gt;SVG&lt;/span&gt;. This allows the inline &lt;span class="caps"&gt;SVG&lt;/span&gt; to function normally, be styled, etc…​ but to &lt;em&gt;also be loaded async and cached by the browser as an external resource!&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Notice this line: &lt;code&gt;fetch(image.src, { cache: 'force-cache' })&lt;/code&gt; - this performs an &lt;span class="caps"&gt;AJAX&lt;/span&gt; load of the &lt;span class="caps"&gt;SVG&lt;/span&gt; file in JavaScript - but it &lt;em&gt;won’t be loaded twice&lt;/em&gt; - it’ll already be in the cache and the &lt;code&gt;cache: 'force-cache'&lt;/code&gt; option makes &lt;code&gt;fetch&lt;/code&gt; just pull it from there.&lt;/p&gt;
&lt;p&gt;Including all the comments, this is 934 bytes of JavaScript. Minified &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; gzipped, this is about 250 bytes.&lt;/p&gt;
&lt;aside class="admonition-block note"&gt;&lt;h6 class="block-title label-only"&gt;&lt;span class="title-label"&gt;Note: &lt;/span&gt;&lt;/h6&gt;&lt;p&gt;I didn’t invent this &lt;span class="caps"&gt;SVG&lt;/span&gt; injection technique, I found it here: &lt;a class="bare" href="https://github.com/iconfu/svg-inject"&gt;https://github.com/iconfu/svg-inject&lt;/a&gt; This is much more capable, has more features and is more compatible with older browsers than my script above. It’s also 4.5 &lt;span class="caps"&gt;KB&lt;/span&gt; of JavaScript - and I didn’t want any of the extra stuff, so I cribbed a very stripped down version, which is what you see here. If you want the full version, use their script instead.&lt;/p&gt;&lt;/aside&gt;&lt;/section&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_3_load_the_sprite_sheet_as_a_regular_image"&gt;3. Load the Sprite Sheet as a Regular Image&lt;/h3&gt;&lt;p&gt;Now, we can load the sprite sheet as a regular image, using an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element, hide it, and run the &lt;code&gt;convertSVG()&lt;/code&gt; function on it:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/icons/icon_sheet.svg"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: none;"&lt;/span&gt; &lt;span class="na"&gt;onload=&lt;/span&gt;&lt;span class="s"&gt;"convertSVG(this)"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is all the essential parts - we get externally loaded and cached &lt;span class="caps"&gt;SVG&lt;/span&gt; icon sprites, inline, with caching - the holy grail!&lt;/p&gt;
&lt;p&gt;The only real downside of this technique are that it requires JavaScript. Can we have a fallback, so that this works &lt;span class="caps"&gt;OK&lt;/span&gt; &lt;em&gt;without&lt;/em&gt; JavaScript?&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_fallback_without_javascript"&gt;Fallback Without JavaScript&lt;/h2&gt;&lt;p&gt;If we &lt;em&gt;don’t&lt;/em&gt; have JavaScript the &lt;code&gt;ConvertSVG()&lt;/code&gt; stuff isn’t going to happen. The browser will still load the sprite sheet, but the &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; stuff and the &lt;span class="caps"&gt;CSS&lt;/span&gt; styles won’t work, because the &lt;span class="caps"&gt;SVG&lt;/span&gt; will be external.&lt;/p&gt;
&lt;p&gt;We can use the venerable &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; element &lt;a class="footnote-ref" href="#_footnote_4" id="_footnoteref_4" title="View footnote 4"&gt;[4]&lt;/a&gt; to provide an alternative. The contents of the &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; element will only be processed by the browser if JavaScript is disabled. We can include the relevant icon as an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element inside the noscript element, like this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/icons/fa/solid/tag.svg"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"18px"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#tag"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, if you have JavaScript enabled:&lt;/p&gt;
&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;the sprite sheet and the &lt;code&gt;convertSVG()&lt;/code&gt; will work&lt;/li&gt;&lt;li&gt;the &lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;use href="#tag"&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&lt;/code&gt; will work&lt;/li&gt;&lt;li&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; styles will work&lt;/li&gt;&lt;li&gt;&lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; element will be ignored&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;p&gt;If you have JavaScript disabled, you’ll get the opposite:&lt;/p&gt;
&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;the sprite sheet and the &lt;code&gt;convertSVG()&lt;/code&gt; won’t work&lt;/li&gt;&lt;li&gt;the &lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;use href="#tag"&amp;gt;&amp;lt;/use&amp;gt;&amp;lt;/svg&amp;gt;&lt;/code&gt; won’t work&lt;/li&gt;&lt;li&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; styles won’t work&lt;/li&gt;&lt;li&gt;&lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; element will be loaded &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; you’ll get the &lt;span class="caps"&gt;SVG&lt;/span&gt; icon loaded in the right place&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;p&gt;So, with JavaScript disabled, the icons will load in the correct place - the only thing missing will be the &lt;span class="caps"&gt;CSS&lt;/span&gt; styles, because the icons are now external. There’s nothing we can do about that, sadly. If you need to, you can apply some fallback styles to fix spacing issues that might arise because the intended styles no longer apply:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="nt"&gt;noscript&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_show_me_the_numbers"&gt;Show Me the Numbers&lt;/h2&gt;&lt;figure class="table-block"&gt;&lt;figcaption&gt;Table 1. All figures are for this page, uncompressed, and in bytes unless otherwise specified. Other external assets that this page loads, like &lt;span class="caps"&gt;CSS&lt;/span&gt;, have been removed for simplicity.&lt;/figcaption&gt;
&lt;table class="frame-all grid-all stretch"&gt;&lt;colgroup&gt;&lt;col style="width: 25%;" /&gt;&lt;col style="width: 12.5%;" /&gt;&lt;col style="width: 12.5%;" /&gt;&lt;col style="width: 12.5%;" /&gt;&lt;col style="width: 12.5%;" /&gt;&lt;col style="width: 12.5%;" /&gt;&lt;col style="width: 12.5%;" /&gt;&lt;/colgroup&gt;&lt;thead&gt;&lt;tr&gt;&lt;th class="halign-left valign-top"&gt;&lt;/th&gt;&lt;th class="halign-right valign-top"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt;&lt;/th&gt;&lt;th class="halign-right valign-top"&gt;JavaScript&lt;/th&gt;&lt;th class="halign-right valign-top"&gt;Sprite Sheet&lt;/th&gt;&lt;th class="halign-right valign-top"&gt;Total Cacheable&lt;/th&gt;&lt;th class="halign-right valign-top"&gt;Total&lt;/th&gt;&lt;th class="halign-right valign-top"&gt;Total Saving (%age)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="halign-left valign-top"&gt;All Inline&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;58,793&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;58,793&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="halign-left valign-top"&gt;Inline Sprite Sheet&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;55,940&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;-&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;55,940&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;2,853 (5%)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="halign-left valign-top"&gt;External Sprite Sheet, w. &lt;span class="caps"&gt;JS&lt;/span&gt;&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;44,825&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;391&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;11,301&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;11,692&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;56,517&lt;/td&gt;&lt;td class="halign-right valign-top"&gt;13,968 (24%)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;We can see that that just consolidating all the repeated &lt;span class="caps"&gt;SVG&lt;/span&gt; icons into an inline sprite sheet saves us 2,853 bytes, or 5% of the total non-cacheable size.&lt;/p&gt;
&lt;p&gt;Once you make the sprite sheet external and add the JavaScript, your overall total size actually goes up &lt;em&gt;very slightly&lt;/em&gt; - by 577 bytes. However, &lt;em&gt;11,692 bytes of this is now cacheable&lt;/em&gt;, where it wasn’t before - so even though you are +577 bytes on first load, you are -13,968 bytes on subsequent page loads because of caching. This is a saving of 24% over the original non-cacheable size.&lt;/p&gt;
&lt;hr /&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_footnotes_references"&gt;Footnotes &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; References&lt;/h3&gt;&lt;/section&gt;&lt;/section&gt;&lt;section class="footnotes"&gt;&lt;hr /&gt;&lt;ol class="footnotes"&gt;&lt;li class="footnote" id="_footnote_1"&gt;&lt;span class="caps"&gt;CSS&lt;/span&gt; Tricks Icon System with &lt;span class="caps"&gt;SVG&lt;/span&gt; Sprites: &lt;a class="bare" href="https://css-tricks.com/svg-sprites-use-better-icon-fonts/"&gt;https://css-tricks.com/svg-sprites-use-better-icon-fonts/&lt;/a&gt; &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; using &lt;span class="caps"&gt;SVG&lt;/span&gt; symbols: &lt;a class="bare" href="https://css-tricks.com/svg-symbol-good-choice-icons/"&gt;https://css-tricks.com/svg-symbol-good-choice-icons/&lt;/a&gt; &lt;a class="footnote-backref" href="#_footnoteref_1" title="Jump to the first occurrence in the text"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li class="footnote" id="_footnote_2"&gt;The &amp;lt;use&amp;gt; element takes nodes from within the &lt;span class="caps"&gt;SVG&lt;/span&gt; document, and duplicates them somewhere else: &lt;a class="bare" href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use"&gt;https://developer.mozilla.org/en-&lt;span class="caps"&gt;US&lt;/span&gt;/docs/Web/&lt;span class="caps"&gt;SVG&lt;/span&gt;/Element/use&lt;/a&gt; &lt;a class="footnote-backref" href="#_footnoteref_2" title="Jump to the first occurrence in the text"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li class="footnote" id="_footnote_3"&gt;The preload value of the &amp;lt;link&amp;gt; element’s rel attribute lets you declare fetch requests in the &lt;span class="caps"&gt;HTML&lt;/span&gt;’s &amp;lt;head&amp;gt;, specifying resources that your page will need very soon, which you want to start loading early in the page lifecycle, before browsers’ main rendering machinery kicks in. This ensures they are available earlier and are less likely to block the page’s render, improving performance: &lt;a class="bare" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload"&gt;https://developer.mozilla.org/en-&lt;span class="caps"&gt;US&lt;/span&gt;/docs/Web/&lt;span class="caps"&gt;HTML&lt;/span&gt;/Link_types/preload&lt;/a&gt; &lt;a class="footnote-backref" href="#_footnoteref_3" title="Jump to the first occurrence in the text"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;li class="footnote" id="_footnote_4"&gt;The &amp;lt;noscript&amp;gt; &lt;span class="caps"&gt;HTML&lt;/span&gt; element defines a section of &lt;span class="caps"&gt;HTML&lt;/span&gt; to be inserted if a script type on the page is unsupported or if scripting is currently turned off in the browser: &lt;a class="bare" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript"&gt;https://developer.mozilla.org/en-&lt;span class="caps"&gt;US&lt;/span&gt;/docs/Web/&lt;span class="caps"&gt;HTML&lt;/span&gt;/Element/noscript&lt;/a&gt; &lt;a class="footnote-backref" href="#_footnoteref_4" title="Jump to the first occurrence in the text"&gt;↩&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 09 Nov 2021 23:08:46 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/11/09/styleable-inline-svg-icon-sprite-system-with-caching-fallback/</guid></item><item><title>Incoherence of cryptoassets, the best parts</title><link>https://stop.zona-m.net/2021/11/incoherence-of-cryptoassets-the-best-parts/</link><description>&lt;p&gt;Some things are just too good to not synthesize them.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 09 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/incoherence-of-cryptoassets-the-best-parts/</guid></item><item><title>Companies Rarely Grow Into Their Valuations</title><link>https://whoisnnamdi.com/grow-valuation/</link><description>Companies don't catch up to their valuations; their valuations catch up to them.</description><author>Who is Nnamdi?</author><pubDate>Tue, 09 Nov 2021 18:28:00 GMT</pubDate><guid isPermaLink="true">https://whoisnnamdi.com/grow-valuation/</guid></item><item><title>Emotional Mirror Jaguar</title><link>https://june.kim/emotional-mirror-jaguar/</link><author>june.kim</author><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/emotional-mirror-jaguar/</guid></item><item><title>The New HeroQuest</title><link>https://benovermyer.com/blog/2021/11/the-new-heroquest/</link><description>&lt;p&gt;So today I received the new HeroQuest in the mail. Last year,
I backed the Hasbro-run crowdfunding campaign to remake it. I
backed at the highest level, the "Mythic" tier, which came with
quite a few extra goodies.&lt;/p&gt;
&lt;p&gt;I plan on writing a second blog post after I play it a few times.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0107.jpg" /&gt;
&lt;p&gt;&lt;em&gt;Opening up the shipping box!&lt;/em&gt;&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0108.jpg" /&gt;
&lt;p&gt;&lt;em&gt;The contents of the shipping box.&lt;/em&gt;&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0109.jpg" /&gt;
&lt;p&gt;&lt;em&gt;This is what came in the Mythic-exclusive box.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What follows is a series of photos comparing my copy of the
original game released in the late 80s to the new one.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0110.jpg" /&gt;
&lt;p&gt;The original box is shorter and wider. The artwork on the new
one is a nice tribute to the original.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0111.jpg" /&gt;
&lt;p&gt;The board in the new one is a bigger scale but otherwise follows
the layout of the original.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0112.jpg" /&gt;
&lt;p&gt;On the left is a goblin from the original, and on the right is
the new one. The originals were made of a hard plastic; the new
ones are a softer plastic. I'm not sure if they'll hold paint
well yet.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0113.jpg" /&gt;
&lt;p&gt;On the left is the barbarian that I painted when I was a teenager,
with the sword partially broken. On the right is the new barbarian.
Not shown here is the female barbarian that came in the Mythic box.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0114.jpg" /&gt;
&lt;p&gt;The gargoyle in the new one is slightly bigger and has an axe instead
of a whip. Otherwise it's pretty close to the original.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0115.jpg" /&gt;
&lt;p&gt;The new dice are bigger and less round. The shield is a different
symbol, too. I think these will be easier to roll, honestly.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0116.jpg" /&gt;
&lt;p&gt;All of the furniture in the new game is full plastic 3D models. The
furniture in the original was cardboard with some plastic parts. Not
shown here are the rats and skulls that come in the new set.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0117.jpg" /&gt;
&lt;p&gt;I like the new doors better, though one or two of the ones in my box
are slightly warped.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0118.jpg" /&gt;
&lt;p&gt;The new character sheets are almost identical to the original, but
there are a LOT more of them. This is a thick set.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0119.jpg" /&gt;
&lt;p&gt;The rulebook in the new version is full color on glossy paper. The
artwork is much improved. I haven't dug through the rules to see if
the layout is changed, though.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0120.jpg" /&gt;
&lt;p&gt;The Zargon player's screen is the same size as the original, but it
has new artwork.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0121.jpg" /&gt;
&lt;p&gt;The reverse of the Zargon player's screen has mostly the same information,
although the names of some of the monsters are different and the text
is laid out better.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0122.jpg" /&gt;
&lt;p&gt;The new quest book is also full color glossy. Again, I haven't flipped
through it yet.&lt;/p&gt;
&lt;img class="photo" src="https://benovermyer.com/blog/2021/11/the-new-heroquest/IMG_0123.jpeg" /&gt;
&lt;p&gt;Each of the original cards is present in the new version, but with new
artwork. Also, Chaos Magic has been renamed Dread Magic, and there are
new equipment cards, player rule reference cards, and character cards.&lt;/p&gt;</description><author>Ben Overmyer's Site</author><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://benovermyer.com/blog/2021/11/the-new-heroquest/</guid></item><item><title>What Do You Want to Spend Your Life On?</title><link>/2021/11/spend</link><description>Here’s a funny phrase: “What do you want to spend your life on?”</description><author>Elliott Slaughter</author><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">/2021/11/spend</guid></item><item><title>2021-11-09</title><link>https://ho.dges.online/pictures/2021-11-09/</link><description/><author>ho.dges.online</author><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/pictures/2021-11-09/</guid></item><item><title>Coexistence: Automation and Humanity</title><link>https://macleodsawyer.com/blog/automation/</link><description>&lt;p&gt;&lt;small&gt;This is another response to a post that was once &lt;a href="https://macleod.tumblr.com/post/669221729885896704/roach-works-scumbag-stallion-make-no-mistake"&gt;found here&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Automation of mindless and repeatable vocations are coming, and are here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But,&lt;/strong&gt; automation requires co-existing humans and robotics in the same work space. Automation by itself fails, automation without a handler, fails, automation because it is focused and the world is not, fails.&lt;/p&gt;
&lt;p&gt;I am a roboticist, I develop and build robots and I can say with good authority (given to me, by me) that robotics and automation will only ever make sense within our current scope when there are overseers and other users to ensure proper order is in place.&lt;/p&gt;
&lt;p&gt;Robotics are not at a place (or will be for many years) where this will not be the case. The ‘we will automate you if you don’t accept our pay’ is absolute bullshit for several reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The cost to train, develop, and implement a robotic system for any amount of work is costly (millions)&lt;/li&gt;
&lt;li&gt;The cost to train, develop, and implement overseers is costly (far more than the bare minimum they want to pay)&lt;/li&gt;
&lt;li&gt;Robots are not generalists (yet…), and every job needs generalists so that they can do &lt;em&gt;more&lt;/em&gt; than what they needed, or if deadlines change, or parts change, or really &lt;em&gt;anything changes&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;You have to radically change the entirely of a workplace, shop floor, warehouse, store, and more to become a 'robot-friendly’ work place. There are safety requirements, there are OSHA guidelines to ensure human workers are not harmed, there are insane requirements everywhere to ensure that a million dollar robot doesn’t topple over and break everything around it including itself.&lt;/li&gt;
&lt;li&gt;Maintenance_. Robots break._ Just as humans need to go home and rest, so do robots. Just as humans need to relax or else they break, so do robots. When robots do inevitably break you have to call in experts to fix them, to care for them, and to ensure that they never break and have a long lifespan.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Don’t believe any company that tries to espouse the “then we’ll just give your jobs to the robots” because if they could, they would have already - and they won’t because they can’t.&lt;/p&gt;
&lt;p&gt;Automation is coming, but automation will only ever make sense when they can work alongside humans. Robots are tools, just like a computer is a tool, just like a hammer is a tool.&lt;/p&gt;</description><author>Macleod Sawyer | Web Portal Feed</author><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://macleodsawyer.com/blog/automation/</guid></item><item><title>Compile-time validation of UUIDs</title><link>https://ybogomolov.me/type-level-uuid</link><description>&lt;p&gt;In this post I show how to use TypeScript literal string template types to get a compile-time UUID validation.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id="type-level-uuid-numbers"&gt;Type-level UUID numbers&lt;/h1&gt;

&lt;p&gt;It happens so that we can use recent TypeScript’s type system improvements to get a compile-time validation of UUID number, including validation of correctness of its version. We start with defining a set of allowed symbols for both hex alphabet and (separately) for version:&lt;/p&gt;

&lt;div class="language-ts highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;VersionChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;UUID format is the following: &lt;code class="language-plaintext highlighter-rouge"&gt;xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx&lt;/code&gt;, with &lt;code class="language-plaintext highlighter-rouge"&gt;M&lt;/code&gt; representing version (special case for 0, and actual versions are 1-5) and &lt;code class="language-plaintext highlighter-rouge"&gt;N&lt;/code&gt; representing variant. To encode this, let’s create a helper which will check if a string &lt;code class="language-plaintext highlighter-rouge"&gt;S&lt;/code&gt; consists of &lt;code class="language-plaintext highlighter-rouge"&gt;Char&lt;/code&gt;s and has length of &lt;code class="language-plaintext highlighter-rouge"&gt;Len&lt;/code&gt; symbols.&lt;/p&gt;

&lt;p&gt;Unfortunately, TypeScript’s strings, even literal ones, don’t have &lt;code class="language-plaintext highlighter-rouge"&gt;length&lt;/code&gt; as a constant — it’s just a &lt;code class="language-plaintext highlighter-rouge"&gt;number&lt;/code&gt;. So we need to implement this check using a recursive type with an accumulator.&lt;/p&gt;

&lt;p&gt;I’m also defining a helper &lt;code class="language-plaintext highlighter-rouge"&gt;Prev&lt;/code&gt; to calculate previous number for a given one. It uses accessing tuple by index to get a &lt;em&gt;previous&lt;/em&gt; number regarding the index — e.g., for &lt;code class="language-plaintext highlighter-rouge"&gt;X = 5&lt;/code&gt; it will get sixth element of a tuple, which is &lt;code class="language-plaintext highlighter-rouge"&gt;4&lt;/code&gt;. As the longest group of symbols in UUID has length of 12, so maximal number present in a tuple is &lt;code class="language-plaintext highlighter-rouge"&gt;11&lt;/code&gt;, and rest are &lt;code class="language-plaintext highlighter-rouge"&gt;never&lt;/code&gt;s:&lt;/p&gt;

&lt;div class="language-ts highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Prev&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;[]][&lt;/span&gt;&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;HasLength&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Len&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Lowercase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Char&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;HasLength&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prev&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Len&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can define helpers to check whether a string is a group of 4, 8, 12, a special case — version and 3 more bytes, and a special case of nil UUID:&lt;/p&gt;

&lt;div class="language-ts highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Char4&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HasLength&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Char8&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HasLength&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Char12&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HasLength&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;VersionGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;VersionChar&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HasLength&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NilUUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;00000000-0000-0000-0000-000000000000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we can write a (rather lengthy and mouthful) definition of UUID number, using previously defined helpers:&lt;/p&gt;

&lt;div class="language-ts highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;NilUUID&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S8&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S4_1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S4_2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S4_3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S12&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S8&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Char8&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S4_1&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Char4&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S4_1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S4_2&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;VersionGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S4_2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S4_3&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Char4&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S4_3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;S12&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Char12&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S12&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;
                            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Time for some examples! Let’s imagine we have a function which gets a user by its UUID. I don’t really care about the implementation, so I’ll just log that id. But you can clearly see that incorrect cases are rejected by the compiler, and correct ones are allowed:&lt;/p&gt;

&lt;p&gt;&lt;img alt="Type-level UUID example" src="/assets/images/uuid_example.png" /&gt;&lt;/p&gt;</description><author/><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://ybogomolov.me/type-level-uuid</guid></item><item><title>Programs and Intent</title><link>https://lambdaland.org/posts/2021-11-09_programs_and_intent/</link><description>&lt;p&gt;What does this program do? At the most reduced level, one could say that a program’s behavior is defined by the effect it has on the hardware running it. That’s not very useful however; when we’re programming, we often have to deal with legacy code and tease out the original intent of the code.&lt;/p&gt;
&lt;p&gt;Saying that the meaning of a program is entirely encapsulated by the code is saying that the intent and the implementation are the same. They so rarely are!&lt;/p&gt;
&lt;p&gt;Today I found some Elixir code that looked like the following:&lt;/p&gt;
&lt;pre class="giallo" style="color: #D8DEE9; background-color: #2E3440;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1; font-weight: bold;"&gt;@&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;spec&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt; all_have_key?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span&gt;lst &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;::&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt; [&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;{}],&lt;/span&gt;&lt;span&gt; needed_key &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;::&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; String&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;t&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;())&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt; ::&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt; boolean&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;def&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt; all_have_key?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span&gt;lst&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;,&lt;/span&gt;&lt;span&gt; needed_key&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;)&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;  lst&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;  |&amp;gt;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; Enum&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;map&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;fn&lt;/span&gt;&lt;span&gt; m &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt; m &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;|&amp;gt;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; Map&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;keys&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;()&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt; |&amp;gt;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; Enum&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;any?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;fn&lt;/span&gt;&lt;span&gt; k &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt; k &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;==&lt;/span&gt;&lt;span&gt; needed_key &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;end&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;)&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt; end&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;  |&amp;gt;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; Enum&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;all?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Note, I just threw that together—possible syntax errors in there.)&lt;/p&gt;
&lt;p&gt;What is this code trying to do? It’s trying to check that each map in a list has a given key and return true or false on that condition. I don’t think the code even had a &lt;code&gt;@spec&lt;/code&gt; to help explain that: all I had was the function name (which was not as clear as &lt;code&gt;all_have_key&lt;/code&gt;) and the source.&lt;/p&gt;
&lt;p&gt;After a few moments of reflection, I rewrote it to this:&lt;/p&gt;
&lt;pre class="giallo" style="color: #D8DEE9; background-color: #2E3440;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1; font-weight: bold;"&gt;@&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;spec&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt; all_have_key?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span&gt;lst &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;::&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt; [&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;{}],&lt;/span&gt;&lt;span&gt; needed_key &lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;::&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; String&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;t&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;())&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt; ::&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt; boolean&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;def&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt; all_have_key?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span&gt;lst&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;,&lt;/span&gt;&lt;span&gt; needed_key&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;)&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;  lst&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;  |&amp;gt;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; Enum&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;map&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt;Map&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;has_key?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;(&lt;/span&gt;&lt;span style="color: #81A1C1;"&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;,&lt;/span&gt;&lt;span&gt; needed_key&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;  |&amp;gt;&lt;/span&gt;&lt;span style="color: #8FBCBB;"&gt; Enum&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;.&lt;/span&gt;&lt;span style="color: #88C0D0;"&gt;all?&lt;/span&gt;&lt;span style="color: #ECEFF4;"&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #81A1C1;"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That big long complicated bit has a built-in function. The built-in function is more efficient because it doesn’t traverse the entire list of keys searching for a match: with a map, you get O(1) lookup time.&lt;/p&gt;
&lt;p&gt;So what does a program mean? I’m pretty confident that I preserved the intended meaning of this program. But what’s a better way to express that intent?&lt;/p&gt;
&lt;p&gt;Tests are useful, but they don’t capture everything. While I think this function was tested, no test can ever ensure 100% preservation of intent. &lt;em&gt;Tests can only find witnesses of meaning mismatches.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Type systems are helpful too. But types come in varying degrees of precision: some languages give you an &lt;code&gt;Int&lt;/code&gt;, while others give you &lt;code&gt;Int&lt;/code&gt; or &lt;code&gt;Nat&lt;/code&gt; or &lt;code&gt;∈ {1, 2, 3}&lt;/code&gt;. More powerful type systems let you express more of your intent in a way that can be mechanically checked, but they tend to also be more burdensome.&lt;/p&gt;
&lt;p&gt;This is an open question that I know there’s a lot of ongoing research around. I’m excited to see what I find!&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Tue, 09 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-11-09_programs_and_intent/</guid></item><item><title>"Websites are dead" is NOT the whole picture</title><link>https://stop.zona-m.net/2021/11/websites-are-dead-is-not-the-whole-picture/</link><description>&lt;p&gt;Websites may look dead if you only look at them SHORTLY enough.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 08 Nov 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/websites-are-dead-is-not-the-whole-picture/</guid></item><item><title>Things I Read : CPython Performance</title><link>https://blog.yelinaung.com/posts/things-i-read-8th-nov-2021/</link><description>&lt;p&gt;I&amp;rsquo;ve been looking at how I can improve the performance of an internal service written in Python. I was wondering if there is a way to speed things up without rewriting large part of the code or using another programming language to rewrite part of the code.
Then I remember reading about &lt;a href="https://github.com/pyston/pyston"&gt;Pyston&lt;/a&gt; (a faster and highly-compatible with Cpython from Dropbox) on &lt;a href="https://news.ycombinator.com/item?id=27059119"&gt;HackerNews&lt;/a&gt; and this seems like an opportunity to try it out.
I&amp;rsquo;ve integrated into the service, planning to run some benchmarks and hopefully I will have something to share another time!&lt;/p&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Mon, 08 Nov 2021 18:25:02 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/posts/things-i-read-8th-nov-2021/</guid></item><item><title>Border of the Known</title><link>http://evantravers.com/articles/2021/11/08/border-of-the-known/</link><description>&lt;p&gt;&lt;img alt="" src="https://images.evantravers.com/articles/2021/11/border-of-the-known.png" /&gt;&lt;/p&gt;&lt;p&gt;In any team-oriented context (product work, academic study, team projects, etc.) the whole team would benefit from documenting and sharing Strong Facts or Strong Opinions&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-facts" id="fnref-facts"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;As you practice Note-Making and learn new information &lt;em&gt;you can't know where it belongs&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It could be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Known and documented facts I'm just re-discovering.&lt;/li&gt;
&lt;li&gt;Already known but undocumented facts I can add to the shared repository.&lt;/li&gt;
&lt;li&gt;Unknown, undocumented hypotheses that I can validate and then give to the team as insight.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It may take some time to discover that an idea or fact is already in the Border of the Known… shared knowledge you are just learning. If you have written down unrecorded &amp;quot;oral history&amp;quot; you can add to the team's knowledge bank.&lt;/p&gt;
&lt;p&gt;Any information you discover that is &lt;strong&gt;outside&lt;/strong&gt; that shared boundary of knowledge is &lt;em&gt;potentially&lt;/em&gt; new information… true research. if validated, it is your unique contribution to the knowledge space:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;research is simply a continuation of something we already naturally do: learning. Learning happens when you understand something that someone else already understands. Research happens when you understand something that nobody else understands yet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;– &lt;a href="https://kanjun.me/writing/research-as-understanding"&gt;Research as Understanding - Kanjun Qiu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In a &lt;strong&gt;cooperative&lt;/strong&gt; shared space, you can validate, persuade, and publish moving it into the shared knowledge pool. In a &lt;strong&gt;competitive&lt;/strong&gt; space you can keep this information to improve your position.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-facts"&gt;
&lt;p&gt;In my system, a “named note” or evergreen note should be a strong fact or a strong opinion. &lt;a href="https://notes.andymatuschak.org/Evergreen_notes"&gt;Evergreen Note&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref-facts"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Mon, 08 Nov 2021 13:54:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/11/08/border-of-the-known/</guid></item><item><title>Greta Thunberg; a Future Martyr</title><link>https://blog.vfiles.no/posts/greta-thunberg-a-future-martyr/</link><description>&lt;p&gt;Starting Sunday (October 31, 2021), climate negotiators from nearly every country will gather for two weeks in &lt;a href="https://en.wikipedia.org/wiki/Glasgow"&gt;Glasgow&lt;/a&gt;, Scotland, United Kingdom to hammer out a new agreement aimed at cutting emissions to a level scientists hope will limit global warming, also known as United Nations Climate Change Conference (&lt;a href="https://en.wikipedia.org/wiki/2021_United_Nations_Climate_Change_Conference"&gt;COP26&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Elizabeth_II"&gt;Queen Elizabeth&lt;/a&gt; was supposed to open it all, but canceled due to illness. &lt;a href="https://en.wikipedia.org/wiki/Prince_William,_Duke_of_Cambridge"&gt;William&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Catherine,_Duchess_of_Cambridge"&gt;Kate&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Charles,_Prince_of_Wales"&gt;Charles&lt;/a&gt;, on the other hand, they arrived. And &lt;a href="https://en.wikipedia.org/wiki/Barack_Obama"&gt;Barack Obama&lt;/a&gt;. As well as the world&amp;rsquo;s richest men, like &lt;a href="https://en.wikipedia.org/wiki/Jeff_Bezos"&gt;Jeff Bezos&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Bill_Gates"&gt;Bill Gates&lt;/a&gt;.&lt;/p&gt;</description><author>Posts on A blog</author><pubDate>Mon, 08 Nov 2021 11:03:06 GMT</pubDate><guid isPermaLink="true">https://blog.vfiles.no/posts/greta-thunberg-a-future-martyr/</guid></item><item><title>Why I like lobste.rs</title><link>https://ztoz.blog/posts/why-lobste.rs/</link><description>&lt;p&gt;Lobste.rs is a &amp;ldquo;computing focused community&amp;rdquo; and this &lt;a href="https://lobste.rs/s/ydno8w/tree_structure_file_systems"&gt;post&lt;/a&gt; is a good example of why I like it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Thinking about the data model of file systems and alternatives is a pretty geeky subject, but impactful&lt;/li&gt;
&lt;li&gt;Comments are respectful and build on the post&lt;/li&gt;
&lt;li&gt;Comments add historical context&lt;/li&gt;
&lt;/ul&gt;</description><author>ℤ→ℤ</author><pubDate>Mon, 08 Nov 2021 03:41:05 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/why-lobste.rs/</guid></item><item><title>Community Heat, or Why You Should Get Good at Events</title><link>https://www.swyx.io/community-heat</link><description>A piece of advice I heard about marketing and community that I've repeated to founders ever since - Get Good at Events.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 08 Nov 2021 03:32:56 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/community-heat</guid></item><item><title>Cyclic Workflows with Prefect</title><link>https://kinoshita.eti.br/2021/11/08/cyclic-workflows-with-prefect.html</link><description>&lt;div style="width: 50%; margin: 0 auto;"&gt;
 
 
 
 &lt;figure class="feature"&gt;
 
 &lt;img alt="Prefect logo" height="" src="https://kinoshita.eti.br/assets/posts/2021-11-08-cyclic-workflows-with-prefect/prefect.svg" title="Prefect logo" width="" /&gt;
 &lt;figcaption&gt;&lt;/figcaption&gt;
 
 &lt;/figure&gt;
 

&lt;/div&gt;
&lt;p&gt;Last month I wrote about
&lt;a href="https://kinoshita.eti.br/2021/10/01/cyclic-workflows-with-cylc-and-stackstorm.html"&gt;Cyclic Workflows with Cylc and StackStorm&lt;/a&gt;
and how few workflow managers support cyclic workflows.&lt;/p&gt;
&lt;p&gt;I was surprised today while reading Prefect documentation to see this paragraph:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most workflow frameworks act as if looping is impossible (stressing the Acyclic part of the DAG),
but it&amp;rsquo;s actually trivial to implement. We simply dynamically unroll the loop, similar to how RNN
gradients are sometimes computed.&lt;/p&gt;&lt;/blockquote&gt;</description><author>About on kinow</author><pubDate>Mon, 08 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://kinoshita.eti.br/2021/11/08/cyclic-workflows-with-prefect.html</guid></item><item><title>Tips for neat splash pages</title><link>https://geo.rocks/post/guide-geo-splash-page/</link><description/><author>Geography &amp;amp; Coding</author><pubDate>Sun, 07 Nov 2021 21:28:36 GMT</pubDate><guid isPermaLink="true">https://geo.rocks/post/guide-geo-splash-page/</guid></item><item><title>Things I Read - SIM Cards!</title><link>https://blog.yelinaung.com/posts/things-i-read-7th-nov-2021/</link><description>&lt;p&gt;Another rainy day.&lt;/p&gt;
&lt;p&gt;SIM Cards!!&lt;/p&gt;
&lt;p&gt;Another day, another interesting HN post.
I saw this post &lt;a href="https://news.ycombinator.com/item?id=29135559"&gt;What is AT&amp;amp;T doing at 1111340002?&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And the top was very interesting to me.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Even in relatively technical circles, like HN, many people are not aware of this and I use every opportunity I have to reiterate:&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A SIM card is a full-blown computer with its own CPU and memory.&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Your carrier can upload and run arbitrary code without your consent or knowledge. They can do this at any time.
This means that your &amp;ldquo;phone&amp;rdquo; is actually three different computers running in concert - the actual phone itself (iOS or Android or Symbian), the baseband processor running the baseband code, and the SIM card.&lt;/p&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Sun, 07 Nov 2021 10:39:38 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/posts/things-i-read-7th-nov-2021/</guid></item><item><title>About</title><link>https://blog.yelinaung.com/about/</link><description>&lt;p&gt;Hi! My name is &lt;a href="https://www.yelinaung.com/"&gt;yelinaung&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am writing down&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what I&amp;rsquo;ve learned recently&lt;/li&gt;
&lt;li&gt;learning progress&lt;/li&gt;
&lt;li&gt;interesting topics&lt;/li&gt;
&lt;li&gt;books&lt;/li&gt;
&lt;/ul&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Sun, 07 Nov 2021 10:08:04 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/about/</guid></item><item><title>What makes you a game changer</title><link>http://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer</link><description>&lt;blockquote&gt;
  &lt;p&gt;Life is too short to drink cheap whiskey&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;unknown
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the third post in a series of 13 posts about how to be a game changer. In the previous two posts, &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;we defined the concept&lt;/a&gt; and started looking at &lt;a href="https://dimitarsimeonov.com/2021/10/31/which-games-to-change"&gt;which games to change&lt;/a&gt;. Today we’ll look at the main way to be a game changer - to opt out.&lt;/p&gt;

&lt;p&gt;According to Merriam-Webster dictionary a game changer is “a newly introduced element or factor that changes an existing situation or activity in a significant way”. While this definition is aligned with how some people use the phrase, it is superficial, and it doesn’t define what it means for a &lt;em&gt;person to be&lt;/em&gt; a game changer.&lt;/p&gt;

&lt;p&gt;One way this definition gets wrong is that it considers games mutable. By that logic any minuscule change in the parameters of the game is technically a game changer. Yet, I don’t want to be the type of person that starts a sentence with “Technically… ☝️”. I think what really matters is when the game is changed so much that the behavior of the players changes and they have to pick a new way to play. That’s when it gets really interesting and worth talking about.&lt;/p&gt;

&lt;p&gt;We can think of changed games being an instance of a distinct new game. This distinction may seem contrived at this point, as what it means to be a new game is still ambiguous to separate out, but it will become clearer in a bit. By thinking of the changed game as a new game, we realize that the person who changed it decided not to play the old game. That person walked out, or opted out of the old game. They exited the old game. Which leads to the most common method people change games:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;You become a game changer by opting out of bad games.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img alt="choose the opt out pill" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/03morpheus.png" width="100%" /&gt;&lt;/p&gt;

&lt;h3 id="opting-out"&gt;Opting out&lt;/h3&gt;

&lt;p&gt;Opting out starts as a seed in their mind. It might be as explicit as deciding - “I’m not going to participate,” but it can also be subconscious, or the person may decide to “hack” the existing game rules for their advantage. For example, a boxer may decide not to become good at knocking out their opponents but instead always win through a minimal point difference by having a new type of defense which doesn’t let others score points against them. They may keep their hands up protecting themselves all the time, and patiently wait for an opening. Their style may be described as “ugly” by traditional fans and called “not real boxing” by gatekeepers, but they will be effective nonetheless, and as a result other boxers may adapt the same “ugly” style. That boxer decided not to fight according to everyone else’s expectations - they opted out of the boxing narrative.&lt;/p&gt;

&lt;p&gt;Other types of game-changing are more subtle and a lot of us do them every day. A busy person may find a way to meal-prep ahead their food for the week, thus discovering a new freedom in how they spend their time through the day. A student may drop a class they realized is not helpful or interesting, and go on a path towards a different major and career.&lt;/p&gt;

&lt;h3 id="the-ethics-of-opting-out"&gt;The ethics of opting out.&lt;/h3&gt;

&lt;p&gt;Opting out of negative-sum games is opting out of something unethical, and therefore it can be ethical, if replaced with something better. There might be a question of loyalty and trustworthiness about the person opting out. But expecting a loyalty from someone who is persistently on the losing side is itself unethical. If we want to live in a kind, ethical and rich world, at some point we have to opt out of unethical games.&lt;/p&gt;

&lt;h3 id="types-of-opting-out"&gt;Types of opting out&lt;/h3&gt;

&lt;p&gt;Opting out is a bit of an abstract concept, and by itself is not super actionable, as it begs the follow up question “What does opting out look like in a specific situation?” And the answers to this question may be manyfold - you may opt out by physically walking out, by focusing on a different objective than you’re expected to, by becoming less adversarial, by turning one-off situations into repeated situations, and even by reframing how you see and approach interactions.&lt;/p&gt;

&lt;p&gt;Four years ago I started thinking about how everything we consume affects our thinking, and I started gradually opting out of alcohol, caffeine, social media. I wanted to play the game of life as myself, rather than be swayed by substances and narratives. I’ve also started opting out of certain types of food such eating too much sugar, or vegetable oils with polyunsaturated fats. And I also stopped using commercial shampoo and instead try to use a more wholesome soap. Recently I started opting out of hot showers and replacing them with cold showers in the morning. I’ve opted out of jobs and partnerships that looked promising.&lt;/p&gt;

&lt;p&gt;Neo opted out of the Matrix.&lt;/p&gt;

&lt;h3 id="next-up"&gt;Next up&lt;/h3&gt;

&lt;p&gt;The ways you can opt-out depend on which negative game you find yourself in, but the first step is always to detect the game. In the next posts, we’ll cover specific types of games and opting out in detail.&lt;/p&gt;</description><author>D13V</author><pubDate>Sun, 07 Nov 2021 06:13:54 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/11/07/what-makes-you-a-game-changer</guid></item><item><title>There is only one problem with  these electrification strategies</title><link>https://stop.zona-m.net/2021/11/there-is-only-one-problem-with-these-electrification-strategies/</link><description>&lt;p&gt;&amp;hellip; it&amp;rsquo;s their &amp;ldquo;Business as Usual&amp;rdquo; foundation.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 07 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/there-is-only-one-problem-with-these-electrification-strategies/</guid></item><item><title>2021–11–07: Pinephone Pro – USB, Type-C, OTG, DP-Alt mode, Charger,… – some success :)</title><link>https://xnux.eu/log/#053</link><author>megi's PinePhone Development Log</author><pubDate>Sun, 07 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#053</guid></item><item><title>Parchis: the reason why I am a developer</title><link>https://danielpecos.com/2021/11/06/parchis-the-reason-why-i-am-a-developer/</link><description>&lt;p&gt;A while back, digging into an old &lt;code&gt;DO NOT DELETE&lt;/code&gt; backup folder in one of my old hard drives, I stumped upon a folder called &lt;code&gt;parchis&lt;/code&gt;, and for a moment I held my breath until I found some &lt;code&gt;*.bas&lt;/code&gt; files there!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So what?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Well, I just found the lost source code of my very first project, the one I coded while learning about variables, procedures and loops, the one that made me realize that this is the kind of stuff I wanted to do as my professional career and hobby.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The one that made me love coding.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But before explaining further, let me explain what the Parchís game is about, and for that I&amp;rsquo;m going to borrow the &lt;a href="https://en.wikipedia.org/wiki/Parch%C3%ADs"&gt;definition from the Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Parchís is a Spanish board game of the Cross and Circle family. It is an adaptation of the Indian game Pachisi. Parchís was a very popular game in Spain at one point as well as in Europe and north Morocco - specifically Tangiers and Tetouan, and it is still popular specially among adults and seniors. Since it uses dice, Parchís is not usually regarded as an abstract strategy game like checkers or chess. It does not depend entirely on luck either, since the four pawns under a player&amp;rsquo;s command demand some sort of strategy.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So here I am, 17 years old me, learning how to code from a book I borrowed from the library about QuickBasic. Yep, back then Internet was not a thing yet (at least not much yet in Spain), so that was how we used to browse and learn new topics, by going to a library. And, oh did I enjoy it.&lt;/p&gt;








  


&lt;img alt="Parchis in QuickBasic" class="" height="572" src="https://danielpecos.com/2021/11/06/parchis-the-reason-why-i-am-a-developer/assets/code_hu_680b3e379041876b.webp" style="width: auto; height: auto;" width="850" /&gt;

&lt;p&gt;I still don&amp;rsquo;t know what exactly hooked me up, but there I was writing this weird mixed of English &amp;amp; Spanish that the computer was able to understand, using this archaic environment in MS-DOS. Maybe it was being able to finally understand how games were made, or the beauty of taking a problem and plan and design a solution, and not only that, taking care of the solution being elegant and simple. Pure beauty.&lt;/p&gt;
&lt;p&gt;But anyway, after a few weeks (if I remember correctly), I managed to put something together that resembled the game I was aiming for and that kind of worked. To be honest, trying to read the code today is quite painful, and hard to keep all the logic and structure in your head, which makes me even prouder of being able of making it work being so poorly written.&lt;/p&gt;
&lt;p&gt;This is how the program looks like:&lt;/p&gt;








  


&lt;img alt="Parchis - Daniel Pecos - 1998" class="" height="643" src="https://danielpecos.com/2021/11/06/parchis-the-reason-why-i-am-a-developer/assets/parchis_hu_80f395e6108d80b6.webp" style="width: auto; height: auto;" width="850" /&gt;

&lt;p&gt;And no, the screenshot is not from back then, but from a couple of days ago, when I found the source code. I wanted to check if it was possible to still run that code, so I started digging about modern quick basic interpreters, and found some, but at the end the best solution was to use &lt;code&gt;dosbox&lt;/code&gt;, a MS-DOS emulator and installing QuickBasic 4.5 to open the software.&lt;/p&gt;
&lt;p&gt;And lo and behold, it worked!&lt;/p&gt;
&lt;p&gt;(BTW, I use Arch, and this is running on Arch Linux)&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve put all the &lt;a href="https://github.com/dpecos/parchis"&gt;code together in a Github repository&lt;/a&gt;, where I&amp;rsquo;ve fixed some MS-DOS &lt;strong&gt;encoding&lt;/strong&gt; issues and cleaned a little bit the code, but I&amp;rsquo;ve kept all the original comments, bugs and code smells that I wrote back then. There are some instructions on how to get it running, just in case you are interested on running old QuickBasic programs.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t want to change a single bit of it, but I want to be able to look at it from time to time and remember how all of this started.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to have a look, feel free, although I doubt you&amp;rsquo;ll find any value on the code itself, it&amp;rsquo;s ugly, poorly written, anb barely works, but to me :-)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dpecos/parchis"&gt;https://github.com/dpecos/parchis&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And if you&amp;rsquo;d like to actually see how I got it up and running, here you have a small screencast of the process and gameplay:&lt;/p&gt;

&lt;video controls="controls" height="600" width="800"&gt;
  &lt;source src="https://danielpecos.com/2021/11/06/parchis-the-reason-why-i-am-a-developer/assets/demo.webm" type="video/webm" /&gt;
&lt;/video&gt;


&lt;p&gt;Last but not least, I have to say that it was my friend &lt;a href="https://www.youtube.com/user/t3rr1k"&gt;Alfredo&lt;/a&gt; who actually introduced me to software development, showing me a couple of small programs he was developing to learn QuickBasic, and triggering my curiosity about it, leading towards this project.&lt;/p&gt;
&lt;p&gt;So basically all of this is his fault. Thx for that &lt;a href="https://www.youtube.com/user/t3rr1k"&gt;Alfredo&lt;/a&gt; ;-)&lt;/p&gt;</description><author>GeekWare - Daniel Pecos Martínez</author><pubDate>Sat, 06 Nov 2021 22:58:00 GMT</pubDate><guid isPermaLink="true">https://danielpecos.com/2021/11/06/parchis-the-reason-why-i-am-a-developer/</guid></item><item><title>What are Dev Containers, What are GitHub Codespaces, and Why Should You Care? A Practical Intro</title><link>https://www.mikekasberg.com/blog/2021/11/06/what-are-dev-containers.html</link><description>&lt;p&gt;&lt;a href="https://github.blog/2021-08-11-githubs-engineering-team-moved-codespaces/"&gt;GitHub
Codespaces&lt;/a&gt;
made waves when GitHub introduced the feature several months ago. Codespaces
promises a lot. It solves configuration headaches, it creates disposable
environments, it can provide better performance than the laptop you’re working
on, and it can better enable remote collaboration. I was interested to dip my
toes in the water and see what it’s like to work with Codespaces, but that can
be a little tricky to do if you don’t have access to a GitHub org that’s paying
for Codespaces. Fortunately, as it turns out, there’s a very accessible
alternative that’s free and easy to try, and it’s a great way to get a taste of
what the Codespaces experience might be like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt; has a feature called &lt;a href="https://code.visualstudio.com/docs/remote/containers"&gt;Dev
Containers&lt;/a&gt;, and it works
almost exactly like GitHub Codespaces, but it runs locally in Docker rather than in
the cloud. Of course, this means you don’t get the performance benefits that are
possible with Codespaces, but most of the other benefits still apply. In
particular, a dev container provides a disposable development environment with
all the right tools ready to go. In fact, GitHub Codespaces &lt;a href="https://docs.github.com/en/codespaces/customizing-your-codespace/configuring-codespaces-for-your-project"&gt;use the
same&lt;/a&gt;
&lt;code class="language-plaintext highlighter-rouge"&gt;devcontainer.json&lt;/code&gt; file a local dev container would use – a Codespace is really
just a dev container in the cloud. More recently,
&lt;a href="https://www.jetbrains.com/help/idea/connect-to-devcontainer.html"&gt;IntelliJ&lt;/a&gt; and
other IDEs have also added dev container support.&lt;/p&gt;

&lt;h2 id="what-is-a-dev-container"&gt;What is a Dev Container?&lt;/h2&gt;

&lt;p&gt;To really understand dev containers (or Codespaces) and the benefits they
provide, I think it’s important to understand the problem they’re trying to
solve. Just for fun, I maintain a small open source project called &lt;a href="https://mkasberg.github.io/script-seed/"&gt;Script
Seed&lt;/a&gt;. It isn’t anything terribly
exciting – it’s just a collection of sample “Hello World” scripts in various
scripting languages. But because that project uses 12+ different scripting
languages, it really shows the usefulness of a dev container. I don’t have all
12+ languages from that project installed on my laptop, so I use Docker to test
it.&lt;/p&gt;

&lt;p&gt;Even before dev containers were popular, I included a
&lt;a href="https://github.com/mkasberg/script-seed/blob/364724d61ff957473502ead5a49875e390cacd58/Dockerfile"&gt;Dockerfile&lt;/a&gt;
and a
&lt;a href="https://github.com/mkasberg/script-seed/blob/364724d61ff957473502ead5a49875e390cacd58/Makefile"&gt;Makefile&lt;/a&gt;
in Script Seed. The Dockerfile was an Ubuntu container that had &lt;em&gt;all&lt;/em&gt; the
supported scripting languages installed, so you could test any script in the
project. And the Makefile provided a user-friendly syntax for running the
container. This Dockerfile was, essentially, a dev container. It provided anyone
who wanted to develop Script Seed a pre-configured, disposable workspace inside
the container with all the tools needed to develop and run the project. A true
VS Code dev container takes that same principle to the next level by adding some
conventions, improving the experience, and integrating tightly with the IDE.&lt;/p&gt;

&lt;h2 id="setting-up-a-vs-code-dev-container-or-codespace"&gt;Setting up a VS Code Dev Container (or Codespace)&lt;/h2&gt;

&lt;p&gt;&lt;img alt="VS Code running in a dev container" src="https://www.mikekasberg.com/images/what-are-dev-containers/vs-code.png" /&gt;&lt;/p&gt;

&lt;p&gt;Recently, I took that same project (&lt;a href="https://mkasberg.github.io/script-seed/"&gt;Script
Seed&lt;/a&gt;) and migrated my old homegrown
Dockerfile to a true VS Code dev container. In setting up this small example
project with a dev container, I learned some things along the way that I’ll be
able to apply when setting up larger projects with dev containers or Codespaces.
I think a small project like this that’s easy to understand provides a perfect
playground to learn about dev containers.  Here’s the
&lt;a href="https://github.com/mkasberg/script-seed/pull/74"&gt;PR&lt;/a&gt; where I added the dev
container to Script Seed. I’ll walk you through what I did, which will be great
to see if you want to learn how to set up your own dev container or Codespace on
your own project.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/mkasberg/script-seed/pull/74/commits/0ca88ae1517909c2b7673f14584065f29f16773f"&gt;first
commit&lt;/a&gt;,
I add a &lt;code class="language-plaintext highlighter-rouge"&gt;Dockerfile&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;devcontainer.json&lt;/code&gt; to the to the &lt;code class="language-plaintext highlighter-rouge"&gt;.devcontainer&lt;/code&gt;
folder and I add a &lt;code class="language-plaintext highlighter-rouge"&gt;bin/server&lt;/code&gt; script. The &lt;code class="language-plaintext highlighter-rouge"&gt;bin/server&lt;/code&gt; script is just a helper
copied from my old docker entrypoint, so let’s focus on the other two files. The
best way to begin setting up a dev container is to let VS Code generate the
files for you by selecting &lt;strong&gt;Remote-Containers: Add Development Container
Configuration Files…&lt;/strong&gt; from the command palette, as documented in &lt;a href="https://code.visualstudio.com/docs/remote/create-dev-container"&gt;Create a
development
container&lt;/a&gt;.  If
there’s already a Dockerfile in your project, you’ll be prompted to start from
that Dockerfile or from a predefined configuration definition. Although I first
tried to use my existing Dockerfile, I went back and did the process again using
a predefined container, and had a better first-time experience with the
predefined container. The VS Code team has done a really nice job setting up
these predefined containers, so I’d recommend trying one of them so you can
understand all the benefits they include before trying to build your own
container. In particular, these dev containers provide a non-root user, provide
a nice Bash prompt, and install many tools you might want.&lt;/p&gt;

&lt;p&gt;Starting from the predefined &lt;a href="https://github.com/microsoft/vscode-dev-containers/tree/v0.194.3/containers/ubuntu"&gt;Ubuntu
dev container&lt;/a&gt;,
I installed all the languages I needed (as I had in my other Dockerfile) and was
ready to go! I made a couple light customizations to &lt;code class="language-plaintext highlighter-rouge"&gt;devcontainer.json&lt;/code&gt;
including naming my container and forwarding a port for the HTTP server to run
on.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/mkasberg/script-seed/pull/74/commits/1df1e1278e1fdc36ade994b7c098f0e3b1f8f5f7"&gt;second
commit&lt;/a&gt;
in my PR, I update my old Makefile to use the new dev container. Primarily, I
want to use &lt;code class="language-plaintext highlighter-rouge"&gt;make test&lt;/code&gt; to run tests in CI (with GitHub Actions), but this is
also a nice pattern to allow anyone who isn’t using VS Code to interact with the
dev container. Finally, in the &lt;a href="https://github.com/mkasberg/script-seed/pull/74/commits/00851264210a7ddff93aba1d2fa527a1401d25ec"&gt;third
commit&lt;/a&gt;,
I update my documentation to describe the new dev container.&lt;/p&gt;

&lt;p&gt;Once the dev container is set up, using it is easy. Just open the project in VS
Code with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"&gt;Remote
Containers&lt;/a&gt;
extension installed, and it will prompt you to re-open the project inside the
container. (You can try this yourself by cloning &lt;a href="https://github.com/mkasberg/script-seed"&gt;Script
Seed&lt;/a&gt; if you want.) The first run will
be a little slow as it pulls and builds the Docker container. Subsequent starts
will be faster as long as the Docker image doesn’t need to be rebuilt, and there
are some advanced options I haven’t played with yet to improve startup time by
pre-building containers. Inside the IDE, you’ll be able to interact with tools
installed in the container, including opening a terminal in the IDE that will
get you to a shell in the container. You can specify extensions to install in
your &lt;code class="language-plaintext highlighter-rouge"&gt;devcontainer.json&lt;/code&gt; file and those extensions will be automatically
installed when you open the container in the IDE. You can do things like run and
debug code inside the container using tools from the IDE. If your project
includes a web server, you can run it inside the container (in my case, the
&lt;code class="language-plaintext highlighter-rouge"&gt;bin/server&lt;/code&gt; script) and open the site in a browser on the host computer via a
forwarded port.&lt;/p&gt;

&lt;h2 id="customization"&gt;Customization&lt;/h2&gt;

&lt;p&gt;One thing you might be wondering about is customization. If you’re working
primarily inside the container, how do you interact with your usual tools like
git, ssh, or vim, and how do you customize those tools if the config for the container
is shared? Fortunately, VS Code has already thought about this and has some good
solutions for dev containers. For example, there’s a mechanism to &lt;a href="https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container"&gt;share Git
credentials&lt;/a&gt;
with the container either with the git credentials helper or with SSH agent
forwarding. VS Code Dev Containers also provides a way to &lt;a href="https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories"&gt;customize the
container with your own
dotfiles&lt;/a&gt;.
I haven’t played with this too much yet, but I’ve started modifying &lt;a href="https://github.com/mkasberg/dotfiles"&gt;my own
dotfiles&lt;/a&gt; to work better with this kind of
setup. Perhaps I’ll write more about that experience later.&lt;/p&gt;

&lt;p&gt;In summary, dev containers provide a ton of value and are relatively easy to set
up – particularly if you’re already familiar with Docker. Normally, a lot of
the friction you experience when you open a new project for the first time is
getting your development environment set up. You might need several languages,
compilers, and tools installed on your system, and you might need new extensions
installed in your IDE. That could take hours to set up, and using a dev
container removes a lot of that friction and provides a great way to manage a
consistent, disposable development environment that’s more inviting for new
users. And GitHub Codespaces take it to the next level by hosting that
environment in the cloud.&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Sat, 06 Nov 2021 19:30:00 GMT</pubDate><guid isPermaLink="true">https://www.mikekasberg.com/blog/2021/11/06/what-are-dev-containers.html</guid></item><item><title>You Should Write to Your UK MP about Corruption Now</title><link>https://duncanlock.net/blog/2021/11/06/you-should-write-to-your-uk-mp-about-corruption-now/</link><description>&lt;p&gt;I&amp;#8217;ve just emailed my &lt;span class="caps"&gt;UK&lt;/span&gt; &lt;span class="caps"&gt;MP&lt;/span&gt; to &lt;a href="https://www.bbc.com/news/explainers-59147044"&gt;complain about government corruption &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Owen Patterson&lt;/a&gt;. You should do the same. As always, we need as many people as possible to speak&amp;nbsp;out.&lt;/p&gt;
&lt;p&gt;This is what I wrote, if anyone wants some&amp;nbsp;ideas:&lt;/p&gt;
&lt;div class="quote-block"&gt;&lt;blockquote&gt;&lt;p&gt;&amp;lt;My Name&amp;gt;&lt;br /&gt;
&amp;lt;My Address&amp;gt;&lt;br /&gt;
&amp;lt;My Email&amp;nbsp;Address&amp;gt;&lt;/p&gt;
&lt;p&gt;Dear &amp;lt;&lt;span class="caps"&gt;MP&lt;/span&gt;&amp;nbsp;Name&amp;gt;,&lt;/p&gt;
&lt;p&gt;Like many others, I am angered - but not very surprised - by the latest revelations of government corruption, revealed by the corrupt behaviour of Owen&amp;nbsp;Patterson.&lt;/p&gt;
&lt;p&gt;I do have to admit to being slightly surprised that the government&amp;#8217;s reaction to this was naked collusion and a blatant attempt to water down the rules &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;standards!&lt;/p&gt;
&lt;p&gt;The existing Committee on Standards needs to be strengthened, not weakened by the introduction of another Conservative-dominated committee overseeing its&amp;nbsp;work.&lt;/p&gt;
&lt;p&gt;The government needs to adopt the recommendations of numerous independent bodies - such as the anti-corruption watchdog the Committee on Standards in Public Life - that rules about MPs&amp;#8217; behaviour should be&amp;nbsp;tightened.&lt;/p&gt;
&lt;p&gt;As a voter in your constituency, this issue is important to me - and the governments actions so far are not good&amp;nbsp;enough!&lt;/p&gt;
&lt;p&gt;Yours&amp;nbsp;sincerely,&lt;/p&gt;
&lt;p&gt;&amp;lt;Your&amp;nbsp;Name&amp;gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;p&gt;This form makes it easy to email your &lt;span class="caps"&gt;MP&lt;/span&gt; about this issue: &lt;a class="bare" href="https://do.opendemocracy.net/MPstandards"&gt;https://do.opendemocracy.net/MPstandards&lt;/a&gt;. It&amp;#8217;ll take 5 minutes - and it&amp;#8217;s better than &lt;em&gt;just&lt;/em&gt; being&amp;nbsp;annoyed.&lt;/p&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_getting_in_touch_with_your_mp"&gt;Getting in Touch with your &lt;span class="caps"&gt;MP&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Getting in touch with your &lt;span class="caps"&gt;MP&lt;/span&gt; generally is much easier than it used to be. You can use www.theyworkforyou.com to find your &lt;span class="caps"&gt;UK&lt;/span&gt; &lt;span class="caps"&gt;MP&lt;/span&gt; and see what they&amp;#8217;ve been up to - as well as sending them a&amp;nbsp;message.&lt;/p&gt;
&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;Does your &lt;span class="caps"&gt;MP&lt;/span&gt; represent you?: &lt;a class="bare" href="https://www.theyworkforyou.com/"&gt;https://www.theyworkforyou.com/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Write to your politicians, national or local, for free: &lt;a class="bare" href="https://www.writetothem.com/"&gt;https://www.writetothem.com/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Sat, 06 Nov 2021 17:45:00 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/11/06/you-should-write-to-your-uk-mp-about-corruption-now/</guid></item><item><title>Things I Read - Trie, Raft</title><link>https://blog.yelinaung.com/posts/things-i-read-6th-nov-2021/</link><description>&lt;p&gt;Things that I stumbled on a rainy day.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understanding how Trie (Data Structure) works&lt;/li&gt;
&lt;li&gt;Understanding how Raft (Distributed Consensus algorithm) works&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trie"&gt;Trie&lt;/h2&gt;
&lt;p&gt;I have heard/seen it but not in detail until now.
Saw someone posted about it &lt;a href="https://news.ycombinator.com/item?id=29078919"&gt;on HN&lt;/a&gt; and makes me curious.&lt;/p&gt;
&lt;p&gt;I also found a post about &lt;a href="https://albertauyeung.github.io/2020/06/15/python-trie.html/"&gt;Trie written in Python&lt;/a&gt; by Googling.
I like the clear explanation with diagrams and put &lt;a href="https://github.com/yelinaung/dsa/blob/master/trie.py"&gt;the code on github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="some-takeaways"&gt;Some takeaways&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Cool data structure to build autocomplete / prefix tree.&lt;/li&gt;
&lt;li&gt;Two main functions
&lt;ul&gt;
&lt;li&gt;Insert&lt;/li&gt;
&lt;li&gt;Query&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="raft"&gt;Raft&lt;/h2&gt;
&lt;p&gt;Another item on my endless &amp;ldquo;things to read&amp;rdquo; list is this particular &lt;a href="http://thesecretlivesofdata.com/raft/"&gt;Understanding Distributed Consensus&lt;/a&gt; link.&lt;/p&gt;</description><author>Ye Lin's Random stuff</author><pubDate>Sat, 06 Nov 2021 10:10:41 GMT</pubDate><guid isPermaLink="true">https://blog.yelinaung.com/posts/things-i-read-6th-nov-2021/</guid></item><item><title>The posthuman dog</title><link>https://nicolaiarocci.com/the-posthuman-dog/</link><description>&lt;p&gt;Flo, our dog, spent her whole fifteen-years long life with us. Many, many times
after she passed away, I wondered if she lived a happy dog life or not. In &lt;a href="https://aeon.co/essays/who-could-dogs-become-without-humans-in-their-lives"&gt;The
posthuman dog&lt;/a&gt; (Aeon), Jessica Pierce poses a fascinating question that
somehow helps find answers to my troubling question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If humans were to disappear from the face of the Earth, what might dogs
become? And would they be better off without us?&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 06 Nov 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/the-posthuman-dog/</guid></item><item><title>Is Eve still maintained?</title><link>https://nicolaiarocci.com/is-eve-still-maintained/</link><description>&lt;p&gt;Tonight someone opened &lt;a href="https://github.com/pyeve/eve/issues/1458"&gt;a ticket&lt;/a&gt; on
the Eve repository. I jotted down a quick reply and was about to hit the
Comment button when I thought a more articulated reply was in order. I also
want it published on my website.&lt;/p&gt;
&lt;p&gt;So the question is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is Eve still maintained?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;My reply goes like this:&lt;/p&gt;
&lt;p&gt;Hello, yes, &lt;a href="https://python-eve.org"&gt;Eve&lt;/a&gt; is in &amp;lsquo;maintenance mode&amp;rsquo;, as I call
it. I don&amp;rsquo;t actively develop new features anymore. Still, I am more than
willing to code-review and merge relevant pull requests, especially so if they
are bug fixes or improvements over existing features.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 06 Nov 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/is-eve-still-maintained/</guid></item><item><title>Dangers of OpenCSV beans streaming</title><link>https://b.agilob.net/programming/java/benchmarks/opencsv-dangers-of-streaming/</link><description>That one time I have to extract, transform and load a massive CSV file into a bunch of database entities and it was kinda slow&amp;hellip;
The class had position based CSV bindings, loaded into beans and streamed from a pretty big CSV file (+10Gb):
public class CSVUserEntry { @CsvBindByPosition(position = 0) private String userId; @CsvBindByPosition(position = 1) private String username; @CsvBindByPosition(position = 2) private String deviceId; @CsvBindByPosition(position = 3) private String keyAlias; @CsvBindByPosition(position = 4) private String passcodeKeyAlias; @CsvBindByPosition(position = 5) private String confirmationId; } Then I opened the stream in generic way, with a simple and fluent interfaces of Java stream API:</description><author>Random Notes by agilob</author><pubDate>Sat, 06 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/programming/java/benchmarks/opencsv-dangers-of-streaming/</guid></item><item><title>Never buy a flying car...</title><link>https://stop.zona-m.net/2021/11/never-buy-a-flying-car.../</link><description>&lt;p&gt;Unless it&amp;rsquo;s not for you only.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 06 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/never-buy-a-flying-car.../</guid></item><item><title>Postgres Query Rewrite</title><link>https://mfashby.net/posts/2021-11-05-postgres-query-rewrite/</link><description/><author>Home on mfashby.net</author><pubDate>Fri, 05 Nov 2021 13:57:12 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-11-05-postgres-query-rewrite/</guid></item><item><title>Localized Programming Languages</title><link>https://www.marginalia.nu/log/36-localized-programming-languages/</link><description>&lt;p&gt;This is an reply to a series of posts on anglo-centrism in programming languages that have been floating around in Gemini lately.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#ZgotmplZ"&gt;gemini://nytpu.com/gemlog/2021-10-31.gmi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#ZgotmplZ"&gt;gemini://alsd.eu/en/2021-11-04-thoughts-anglocentrism-cs.gmi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Around thirty years ago I was a kid with a computer. I learned to program quite a few years before I learned English. I also used DOS without understanding English. I knew what to type to do things, but I didn&amp;rsquo;t know what the words meant. I could start programs, I&amp;rsquo;d play in QBASIC, write small programs and amusements. To me &amp;ldquo;PRINT&amp;rdquo; was the word that made text appear on the screen. I learned years later the word meant something in English. To show you what my child eyes saw, I think rot13 does convey the experience quite well;&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 05 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/36-localized-programming-languages/</guid></item><item><title>Why buy toys if you can 3d print them?</title><link>https://stop.zona-m.net/2021/11/why-buy-toys-if-you-can-3d-print-them/</link><description>&lt;p&gt;Here are a few reasons&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 05 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/why-buy-toys-if-you-can-3d-print-them/</guid></item><item><title>2021–11–05: Pinephone Pro – USB, Type-C, OTG, DP-Alt mode, Charger,…</title><link>https://xnux.eu/log/#052</link><author>megi's PinePhone Development Log</author><pubDate>Fri, 05 Nov 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#052</guid></item><item><title>Keeping Gemini Difficult</title><link>https://www.marginalia.nu/log/35-keeping-gemini-difficult/</link><description>&lt;p&gt;This is a response to the post &amp;ldquo;Making Gemini Easy&amp;rdquo; over on ~tomasino, and the title is a bit tongue-in-cheek haha-but-no-really.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#ZgotmplZ"&gt;gemini://tilde.team/~tomasino/journal/20211103-making-gemini-easy.gmi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think the idea that we need to shield the users from how technology works is a terrible, terrible mistake. It disempowers the users, and concentrates power in the hands of a technological elite, and that divide is only going to grow.&lt;/p&gt;
&lt;p&gt;We already have an alarming number of people working with computers, and some may even be programmers, that simply do not understand how computers work. Their only concept of a computer is the user interface on the screen. The rest is unintelligible wizardry. Nobody has told them, it&amp;rsquo;s been deemed too complicated, nothing for them to worry their little heads about.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Thu, 04 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/35-keeping-gemini-difficult/</guid></item><item><title>Lessons learnt from exposing my server to the internet</title><link>https://blog.imraniqbal.org/sshguard/</link><description>&lt;p&gt;My previous &lt;a href="https://blog.imraniqbal.org/sftp/"&gt;post&lt;/a&gt; was about how I setup a SFTP server for my dad.
This post is going to be about all the things I learned about exposing your server to the internet.&lt;/p&gt;
&lt;h2 id="unable-to-login"&gt;&lt;a class="anchor" href="https://blog.imraniqbal.org/sshguard/#unable-to-login"&gt;
    #
&lt;/a&gt;
Unable to login&lt;/h2&gt;
&lt;p&gt;About a week after I exposed my server via SFTP to the world, I wanted to log in to my server to do some maintenance.
Much to my shock, my server was unresponsive to my regular ssh commands.&lt;/p&gt;
&lt;p&gt;This is when the panic started setting in.
My server lives in my home close to my desktop computer.
The server appeared to be still on, the lights were still on and the fans were still spinning.
I resisted the urge to reboot it and try to ssh again, as I wanted to know &lt;strong&gt;why&lt;/strong&gt; I could not ssh in.&lt;/p&gt;
&lt;p&gt;I temporarily repurposed my second monitor on my desktop and plugged in an extra keyboard that I had.
The standard login prompt greeted me, which meant the server was not dead and I was able to login.&lt;/p&gt;
&lt;p&gt;I checked the status of the ssh process with a &lt;code&gt;systemctl status sshd&lt;/code&gt; and (un)surprisingly it's dead.
The next step was to figure out why it's dead.
This started with looking at the logs via &lt;code&gt;journalctl -b -t sshd&lt;/code&gt;&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span&gt;Loads of similar lines&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245607]: Unable to negotiate with 86.111.187.162 port 36589: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245610]: Unable to negotiate with 86.111.187.162 port 36596: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245618]: Unable to negotiate with 86.111.187.162 port 36605: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245622]: Unable to negotiate with 86.111.187.162 port 36616: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245628]: Unable to negotiate with 86.111.187.162 port 36623: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245632]: Unable to negotiate with 86.111.187.162 port 36634: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245636]: Unable to negotiate with 86.111.187.162 port 36647: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245640]: Unable to negotiate with 86.111.187.162 port 36652: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh.&lt;/p&gt;
&lt;p&gt;Looked like folks over the internet were having a blast trying to break into the server.
The first step I needed to take was to free up my monitor and keyboard and go back to the comfort of working from my desktop.
A simple &lt;code&gt;systemctl start sshd&lt;/code&gt; was all that was needed.&lt;/p&gt;
&lt;h2 id="remediation"&gt;&lt;a class="anchor" href="https://blog.imraniqbal.org/sshguard/#remediation"&gt;
    #
&lt;/a&gt;
Remediation&lt;/h2&gt;
&lt;p&gt;It's clear that I need to harden my ssh setup.
I already the following setup:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;root login disabled&lt;/li&gt;
&lt;li&gt;password logins disabled (with the exception being the SFTP user)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From the logs, looks like no one has even figured out username for the ftp user and the password for it's a long generated one via Bitwarden.
The first thing I did was reduce &lt;code&gt;MaxAuthTries&lt;/code&gt; to 1.
The reasoning was pretty simple, either you know how to get in or you don't.&lt;/p&gt;
&lt;p&gt;The next steps were to consult &lt;a href="https://wiki.archlinux.org/title/OpenSSH#Protecting_against_brute_force_attacks" rel="external"&gt;the arch wiki&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Turned out I had already taken most of the necessary steps.
I was just missing a rate limiting/blacklisting solution.
I settled on using &lt;code&gt;sshguard&lt;/code&gt; combined with &lt;code&gt;ufw&lt;/code&gt;.
I verified with my dad that he could still login and do his work.&lt;/p&gt;
&lt;p&gt;Fingers crossed, this would be the last time I would have to deal with something like this.&lt;/p&gt;
&lt;h2 id="results"&gt;&lt;a class="anchor" href="https://blog.imraniqbal.org/sshguard/#results"&gt;
    #
&lt;/a&gt;
Results&lt;/h2&gt;
&lt;p&gt;My server has been up and running ever since with no issue to ssh.
I have also installed &lt;a href="https://www.netdata.cloud/" rel="external"&gt;&lt;code&gt;netdata&lt;/code&gt;&lt;/a&gt; and made it accessible solely over the local network.
This is just to give me a birds eye view of what is going on with my server without having to ssh in.&lt;/p&gt;
&lt;p&gt;Taking a quick look at logs shows me that sshguard is working as intended&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span&gt;Nov 03 14:27:28 server sshguard[1006]: Attack from &amp;quot;176.111.173.238&amp;quot; on service SSH with danger 10.&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Nov 03 14:27:28 server sshguard[1006]: Blocking &amp;quot;176.111.173.238/32&amp;quot; forever (REDACTED)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><author>Imran's Blog</author><pubDate>Thu, 04 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.imraniqbal.org/sshguard/</guid></item><item><title>Facebook ends face recognition. What does that REALLY mean?</title><link>https://stop.zona-m.net/2021/11/facebook-ends-face-recognition.-what-does-that-really-mean/</link><description>&lt;p&gt;Nothing definitive, in the long run.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 04 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/facebook-ends-face-recognition.-what-does-that-really-mean/</guid></item><item><title>Robot Olympics</title><link>https://blog.randombits.host/robot-olympics/</link><description>&lt;p&gt;I&amp;apos;m not a staunch nationalist, nor am I an athlete of any description, yet I find myself in the same quadrennial boat as most others - the Olympics is an entertaining event to watch. Good-spirited competition drives people to push themselves to the extremes and work together to&lt;/p&gt;</description><author>Random Bits</author><pubDate>Wed, 03 Nov 2021 21:28:00 GMT</pubDate><guid isPermaLink="true">https://blog.randombits.host/robot-olympics/</guid></item><item><title>Debloat MIUI</title><link>https://www.mylloon.fr/blog/p/debloat_miui</link><description>&lt;blockquote&gt;&lt;h2&gt;&lt;a class="anchor" href="#debloat_miui-préambule-important" id="debloat_miui-préambule-important"&gt;&lt;/a&gt;Préambule important&lt;/h2&gt;&lt;p&gt;&lt;em&gt;Dernière mise à jour 15 mai 2024&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-update" id="fnref-update"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Cette liste est très ancienne, nombreux programmes ne sont même
plus livrés par défaut dans la ROM. La priorité est ce qui est écrit
dans UAB car certains programmes peuvent être désormais important
pour le bon fonctionnement de l’OS.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Avant, j’utilisais &lt;a href="https://github.com/Szaki/XiaomiADBFastbootTools"&gt;XiaomiADBFastbootTools&lt;/a&gt;
pour supprimer les applications, mais maintenant
j’utilise &lt;a href="https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation"&gt;UAB&lt;/a&gt;
qui met directement dans l’application ce que fait les applications et les tris par
ordre de dangerosité. Je laisse cette liste au cas où, mais il faut préférer,
je pense, les informations dans l’application, car elles sont susceptibles d’être
mise à jour plus souvent.&lt;/p&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-analytics-commiuianalytics" id="debloat_miui-analytics-commiuianalytics"&gt;&lt;/a&gt;Analytics (com.miui.analytics)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Télémétrie
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-android-accessibility-suite-comgoogleandroidmarvintalkback" id="debloat_miui-android-accessibility-suite-comgoogleandroidmarvintalkback"&gt;&lt;/a&gt;Android Accessibility Suite (com.google.android.marvin.talkback)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application pour aider les personnes handicapées à utiliser le téléphone
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-backup-commiuibackup" id="debloat_miui-backup-commiuibackup"&gt;&lt;/a&gt;Backup (com.miui.backup)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Outil de sauvegarde pour une restauration future, comme un changement de téléphone
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-browser-comandroidbrowser" id="debloat_miui-browser-comandroidbrowser"&gt;&lt;/a&gt;Browser (com.android.browser)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Navigateur de base
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-cleaner-commiuicleanmaster" id="debloat_miui-cleaner-commiuicleanmaster"&gt;&lt;/a&gt;Cleaner (com.miui.cleanmaster)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Outil de &amp;quot;nettoyage&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-facebook-comfacebookappmanager--comfacebookservices--comfacebooksystem" id="debloat_miui-facebook-comfacebookappmanager--comfacebookservices--comfacebooksystem"&gt;&lt;/a&gt;Facebook (com.facebook.appmanager / com.facebook.services / com.facebook.system)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Facebook
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-feedback-commiuibugreport" id="debloat_miui-feedback-commiuibugreport"&gt;&lt;/a&gt;Feedback (com.miui.bugreport)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Outil pour informer les devs d'un bug dans le logiciel
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-games-comxiaomiglgm" id="debloat_miui-games-comxiaomiglgm"&gt;&lt;/a&gt;Games (com.xiaomi.glgm)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Equivalent du &amp;quot;Google Play Games&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-getapps-comxiaomimipicks" id="debloat_miui-getapps-comxiaomimipicks"&gt;&lt;/a&gt;GetApps (com.xiaomi.mipicks)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Marché d'applications
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-gmail-comgoogleandroidgm" id="debloat_miui-gmail-comgoogleandroidgm"&gt;&lt;/a&gt;Gmail (com.google.android.gm)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Client mail Gmail
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-google-app-comgoogleandroidgooglequicksearchbox" id="debloat_miui-google-app-comgoogleandroidgooglequicksearchbox"&gt;&lt;/a&gt;Google App (com.google.android.googlequicksearchbox)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;C'est la barre de recherche Google sur l'écran
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-google-assistant-comgoogleandroidappsgoogleassistant" id="debloat_miui-google-assistant-comgoogleandroidappsgoogleassistant"&gt;&lt;/a&gt;Google Assistant (com.google.android.apps.googleassistant)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;&amp;quot;Ok Google&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-google-chrome-comandroidchrome" id="debloat_miui-google-chrome-comandroidchrome"&gt;&lt;/a&gt;Google Chrome (com.android.chrome)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Navigateur Google Chrome
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-google-lens-comgooglearlens" id="debloat_miui-google-lens-comgooglearlens"&gt;&lt;/a&gt;Google Lens (com.google.ar.lens)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Google Lens &amp;quot;vous permet de rechercher ce que vous voyez, d'aller plus vite et de mieux comprendre le monde qui vous entoure.&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-google-one-comgoogleandroidappssubscriptionsred" id="debloat_miui-google-one-comgoogleandroidappssubscriptionsred"&gt;&lt;/a&gt;Google One (com.google.android.apps.subscriptions.red)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Service payant Google One
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-joyose-comxiaomijoyose" id="debloat_miui-joyose-comxiaomijoyose"&gt;&lt;/a&gt;Joyose (com.xiaomi.joyose)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Télémétrie
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-msa-commiuimsaglobal" id="debloat_miui-msa-commiuimsaglobal"&gt;&lt;/a&gt;MSA (com.miui.msa.global)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Officiellement en rapport avec l'écran d'accueil, la météo et les thèmes, mais la désinstaller ne fait rien, a part retirer des pubs
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-market-feedback-agent-comgoogleandroidfeedback" id="debloat_miui-market-feedback-agent-comgoogleandroidfeedback"&gt;&lt;/a&gt;Market Feedback Agent (com.google.android.feedback)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Outil pour informer les devs de Google d'un bug dans le PlayStore
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-mi-browser-commiglobalbrowser" id="debloat_miui-mi-browser-commiglobalbrowser"&gt;&lt;/a&gt;Mi Browser (com.mi.globalbrowser)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Navigateur web Xiaomi
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-mi-cloud-commiuicloudbackup--commiuicloudservice--commiuicloudservicesysbase--commiuimicloudsync" id="debloat_miui-mi-cloud-commiuicloudbackup--commiuicloudservice--commiuicloudservicesysbase--commiuimicloudsync"&gt;&lt;/a&gt;Mi Cloud (com.miui.cloudbackup / com.miui.cloudservice / com.miui.cloudservice.sysbase / com.miui.micloudsync)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Le Cloud de Xiaomi
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-mi-credit-comxiaomipayment" id="debloat_miui-mi-credit-comxiaomipayment"&gt;&lt;/a&gt;Mi Credit (com.xiaomi.payment)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Service de paiement qui ne fonctionne qu'en Chine
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-mi-drop-comxiaomimidrop" id="debloat_miui-mi-drop-comxiaomimidrop"&gt;&lt;/a&gt;Mi Drop (com.xiaomi.midrop)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Nouvellement appellé &amp;quot;Share Me&amp;quot;, application de P2P pour transferer des fichiers
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-mi-file-manager-commiandroidglobalfileexplorer" id="debloat_miui-mi-file-manager-commiandroidglobalfileexplorer"&gt;&lt;/a&gt;Mi File Manager (com.mi.android.globalFileexplorer)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Gestionnaire de fichier Xiaomi
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-mi-pay-commipaywalletin" id="debloat_miui-mi-pay-commipaywalletin"&gt;&lt;/a&gt;Mi Pay (com.mipay.wallet.in)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Fonctionne pour l'application &amp;quot;Mi Credit&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-miplayclient-comxiaomimiplay_client" id="debloat_miui-miplayclient-comxiaomimiplay_client"&gt;&lt;/a&gt;MiPlayClient (com.xiaomi.miplay_client)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application Mi Play
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-music-commiuiplayer" id="debloat_miui-music-commiuiplayer"&gt;&lt;/a&gt;Music (com.miui.player)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application de musique Xiaomi, utile pour mettre une sonnerie custom (la sonnerie reste même avec l'application désinstallée)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-notes-commiuinotes" id="debloat_miui-notes-commiuinotes"&gt;&lt;/a&gt;Notes (com.miui.notes)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application de notes Xiaomi
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-package-installer-commiuiglobalpackageinstaller" id="debloat_miui-package-installer-commiuiglobalpackageinstaller"&gt;&lt;/a&gt;Package Installer (com.miui.global.packageinstaller)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Ne sert à rien
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-partnerbookmarks-comandroidproviderspartnerbookmarks" id="debloat_miui-partnerbookmarks-comandroidproviderspartnerbookmarks"&gt;&lt;/a&gt;PartnerBookmarks (com.android.providers.partnerbookmarks)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Des favoris par défaut des sponsors de Xiaomi dans leur navigateur
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-quick-apps-commiuihybrid" id="debloat_miui-quick-apps-commiuihybrid"&gt;&lt;/a&gt;Quick Apps (com.miui.hybrid)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Télémétrie
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-quick-ball-commiuitouchassistant" id="debloat_miui-quick-ball-commiuitouchassistant"&gt;&lt;/a&gt;Quick Ball (com.miui.touchassistant)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Accès rapide aux applications
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-recorder-comandroidsoundrecorder" id="debloat_miui-recorder-comandroidsoundrecorder"&gt;&lt;/a&gt;Recorder (com.android.soundrecorder)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application pour enregister des vocaux
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-services--feedback-commiuimiservice" id="debloat_miui-services--feedback-commiuimiservice"&gt;&lt;/a&gt;Services &amp;amp; feedback (com.miui.miservice)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application pour donner un retour sur Miui
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-soterservice-comtencentsotersoterserver" id="debloat_miui-soterservice-comtencentsotersoterserver"&gt;&lt;/a&gt;SoterService (com.tencent.soter.soterserver)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Service de paiement chinois
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-vsimcore-commiuivsimcore" id="debloat_miui-vsimcore-commiuivsimcore"&gt;&lt;/a&gt;VsimCore (com.miui.vsimcore)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Carte Sim Virtuel, utile seulement en Inde et en Chine
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-weather-commiuiweather2" id="debloat_miui-weather-commiuiweather2"&gt;&lt;/a&gt;Weather (com.miui.weather2)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application météo
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-yellow-pages-commiuiyellowpage" id="debloat_miui-yellow-pages-commiuiyellowpage"&gt;&lt;/a&gt;Yellow Pages (com.miui.yellowpage)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application de pages jaune, mais que en Chine
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;a class="anchor" href="#debloat_miui-youtube-comgoogleandroidyoutube" id="debloat_miui-youtube-comgoogleandroidyoutube"&gt;&lt;/a&gt;Youtube (com.google.android.youtube)&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-hljs-plaintext"&gt;Application Youtube
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;En désinstallant ces applications, je ne perds, à ma connaissance, aucune fonctionnalité.&lt;/p&gt;&lt;p&gt;Sources : &lt;a href="https://devcondition.com/article/removing-unneeded-miui-applications/"&gt;devcondition.com&lt;/a&gt; / &lt;a href="https://technastic.com/xiaomi-bloatware-list-miui/"&gt;technastic.com&lt;/a&gt; / &lt;a href="https://play.google.com/store/apps"&gt;Playstore&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="https://github.com/AveyBD/XiaomiADBFastbootTools/blob/master/safe_to_remove.md"&gt;Une autre personne ayant fait le même job&lt;/a&gt;&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn-update"&gt;&lt;p&gt;Notice de dépréciation. &lt;a class="footnote-backref" href="#fnref-update"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;</description><author>Blog d'Anri</author><pubDate>Wed, 03 Nov 2021 14:12:00 GMT</pubDate><guid isPermaLink="true">https://www.mylloon.fr/blog/p/debloat_miui</guid></item><item><title>Overclock son écran</title><link>https://www.mylloon.fr/blog/p/screen_overclock</link><description>&lt;h2&gt;&lt;a class="anchor" href="#screen_overclock-avec-une-carte-graphique-amd" id="screen_overclock-avec-une-carte-graphique-amd"&gt;&lt;/a&gt;Avec une carte graphique AMD&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Rendez-vous dans le panneau de configuration AMD&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;Clic droit sur le bureau → AMD Radeon Software&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;Dans la catégorie &lt;em&gt;Affichage&lt;/em&gt;, crée une nouvelle résolution.&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/9CH0ogl.png" width="900" /&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Augmente le taux de rafraîchissement et teste-la pour voir si l’écran le supporte&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/W4pF5fQ.png" width="700" /&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Augmente de 5 en 5, quand l’écran n’affiche plus rien, réduit de 1 en 1 pour
trouver la fréquence parfaite. Quand tu l’as trouvé, vérifie que l’écran prend
bien toute la surface disponible, si ce n’est pas le cas,
baisse encore de 1 ou 2 Hz.&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;Pour appliquer la nouvelle fréquence, rends-toi dans Paramètres → Système → Affichage&lt;/li&gt;&lt;li&gt;Descends pour te rendre dans &lt;em&gt;Paramètres d’affichage avancés&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/Eqw6dbO.png" width="900" /&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Sélectionne ta fréquence dans la section &lt;em&gt;Fréquence de rafraîssement&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/QQABfyd.png" width="900" /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#screen_overclock-avec-une-carte-graphique-nvidia" id="screen_overclock-avec-une-carte-graphique-nvidia"&gt;&lt;/a&gt;Avec une carte graphique NVIDIA&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Rendez-vous dans le panneau de configuration NVIDIA&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;Clic droit sur le bureau → Panneau de configuration NVIDIA&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;Dans la catégorie &lt;em&gt;Affichage&lt;/em&gt; et sous-catégorie &lt;em&gt;Changer la résolution&lt;/em&gt; et clique sur &lt;em&gt;Personnaliser…&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/6UYt2pN.png" width="900" /&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Si &lt;em&gt;Personnaliser…&lt;/em&gt; n’est pas disponible, sélectionner votre résolution
dans la catégorie PC de la liste déroulante puis appliquez les modifications,
le bouton devrait se débloquer.&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;Crée une nouvelle résolution&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/b5EAm0t.png" width="500" /&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Augmente le taux de rafraîchissement et teste-la pour voir si l’écran le supporte&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/TzXG6t9.png" width="500" /&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Augmente de 5 en 5, quand l’écran n’affiche plus rien, réduit de 1 en 1 pour
trouver la fréquence parfaite. Quand tu l’as trouvé, vérifie que l’écran prend
bien toute la surface disponible, si ce n’est pas le cas, baisse
encore de 1 ou 2 Hz.&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;Pour appliquer la nouvelle fréquence, sélectionne la résolution fraîchement&lt;/li&gt;&lt;li&gt;Crée et applique les modifications.&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/MPdt0jt.png" width="900" /&gt;&lt;/p&gt;</description><author>Blog d'Anri</author><pubDate>Wed, 03 Nov 2021 14:12:00 GMT</pubDate><guid isPermaLink="true">https://www.mylloon.fr/blog/p/screen_overclock</guid></item><item><title>Bric-à-brac de trucs cool</title><link>https://www.mylloon.fr/blog/p/cat</link><description>&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Dernière mise-à-jour le 31 août 2022&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-update" id="fnref-update"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Cette liste n’est pas maintenue à jour.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Merci à&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://discord.gg/informatique/"&gt;&lt;em&gt;Discord de Capet&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://discord.gg/8YF6xUE/"&gt;&lt;em&gt;Discord de Wanda&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://twitter.com/sarquelplaisir/"&gt;&lt;em&gt;sarkraï&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;em&gt;Cette page est une liste de truc que je trouve cool, n’hésite pas à me contacter&lt;/em&gt;
&lt;em&gt;si tu veux discuter.&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-tables-des-matières-" id="cat-tables-des-matières-"&gt;&lt;/a&gt;Tables des matières &lt;!-- omit in toc --&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#cat-quel-syst%C3%A8me-dexploitation-choisir-"&gt;Quel système d’exploitation choisir ?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-web"&gt;Web&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#cat-le-navigateur"&gt;Le navigateur&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-extensions"&gt;Extensions&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-apps-pc"&gt;Apps PC&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#cat-benchmarks-stress-tests-et-infos-pc"&gt;Benchmarks, stress tests et infos PC&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-%C3%A9quivalents-%C3%A0-la-suite-dadobe"&gt;Équivalents à la suite d’Adobe&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-apps-android"&gt;Apps Android&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-liens-utiles"&gt;Liens utiles&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#cat-documentation"&gt;Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-sites"&gt;Sites&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-jeux"&gt;Jeux&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#cat-minecraft"&gt;Minecraft&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-return-to-castle-wolfenstein"&gt;Return To Castle Wolfenstein&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-league-of-legends"&gt;League of Legends&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-%C3%A9mulation"&gt;Émulation&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#cat-switch"&gt;Switch&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-multi-plateforme"&gt;Multi Plateforme&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#cat-contact--infos"&gt;Contact &amp;amp; Infos&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-quel-système-dexploitation-choisir-" id="cat-quel-système-dexploitation-choisir-"&gt;&lt;/a&gt;Quel système d’exploitation choisir ?&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Si tu n’as pas peur de bidouiller 2-3 trucs, alors Linux est fait pour toi.
Il y a plein de distributions Linux, la plus simple pour commencer est sûrement
&lt;a href="https://ubuntu.com/download/desktop"&gt;Ubuntu&lt;/a&gt;. Et ne t’inquiètes pas, si tu veux
jouer c’est largement faisable sous Linux.&lt;/li&gt;&lt;li&gt;Si tu veux juste jouer aux jeux vidéo ou avoir une utilisation classique d’un
ordinateur sans prise de tête, alors utiliser Windows est le choix le plus logique.
Le meilleur Windows est la dernière mise à jour de Windows, pas une version customisée,
LTSB, ou autres. Si tu souhaites “optimiser” ton pc, tu peux le faire en suivant
&lt;a href="https://installerwindows.fr/"&gt;ce tuto&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Si tu ne veux pas t’embêter à mettre en place ton OS et juste que ça fonctionne,
peu importe le prix, alors ton choix logique serait certainement de te tourner
vers l’environnement Apple, ils font de bons produits qui sont simples à
utiliser, cependant tu es quand même plus limité dans tes actions et dans ce
que tu peux faire dans ce genre d’environnement.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-web" id="cat-web"&gt;&lt;/a&gt;Web&lt;/h2&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-le-navigateur" id="cat-le-navigateur"&gt;&lt;/a&gt;Le navigateur&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Chaque navigateur se vaut, chacun ses goûts. Il n’y a pas d’écart de
performances entre les navigateurs, ce qui diffère, c’est les fonctionnalités
qu’ils amènent, choisis celui qui te plaît le plus, sans écouter les gens qui
disent que tel ou tel navigateur n’est pas bien parce qu’il n’est pas optimisé,
c’est la plupart du temps des bêtises.
&lt;a href="https://docs.google.com/spreadsheets/d/e/2PACX-1vTwCOY9Az4hNLDCUr7_v0pSLju_Ya6eXHJ1uGiT2BuAmkZKfTI2IG4yadM2HgT0LZ5kvG2aHY6GQgZW/pubhtml?gid=0&amp;amp;single=true"&gt;Ce spreadsheet&lt;/a&gt;
récapitule quelques fonctionnalités des navigateurs.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-extensions" id="cat-extensions"&gt;&lt;/a&gt;Extensions&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/gorhill/uBlock#readme"&gt;uBlock Origin&lt;/a&gt;
(inutile avec Brave)&lt;ul&gt;&lt;li&gt;Le seul bloqueur de pub qui bloque toutes les pubs.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://darkreader.org/"&gt;Dark Reader&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Ajoute un thème sombre aux sites.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://bitwarden.com/download/"&gt;Bitwarden&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Gestionnaire de mot de passe gratuit, jolie et open source. L’application
permet aussi de gérer la double authentification. Possibilité
d’&lt;a href="https://github.com/dani-garcia/vaultwarden"&gt;héberger soi-même le serveur&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://sponsor.ajay.app/"&gt;SponsorBlock&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Bloque les pubs intégrés aux vidéos YouTube.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://malsync.moe/"&gt;MAL-Sync&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Ajout un suivi automatique des épisodes sur MyAnimeList
(fonctionne avec plein de sites).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://betterttv.com/"&gt;BetterTTV&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Ajoute, entre autres, des emojis sur Twitch&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://keepa.com/#!"&gt;Keepa&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Affiche l’historique des prix sur Amazon&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://returnyoutubedislike.com/install"&gt;Return YouTube Dislike&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Affiche le compteur des pouces rouges dans youtube (une estimation)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/cleanlock/VideoAdBlockForTwitch#available-browsers"&gt;Twitch Adblock&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Bloque les pubs sur Twitch&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-apps-pc" id="cat-apps-pc"&gt;&lt;/a&gt;Apps PC&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Capture d’écran&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Le nouvel outil par défaut dans Microsoft (une
&lt;a href="https://i.imgur.com/oS2MGR1.png"&gt;option dans les paramètres&lt;/a&gt; permet
d’utiliser la touche &lt;em&gt;Impr écran&lt;/em&gt;) et affiche une notification vers un
mini Paint pour annoter la capture.&lt;/li&gt;&lt;li&gt;&lt;a href="https://getsharex.com/downloads/index.html"&gt;ShareX&lt;/a&gt; permet, entre autres,
d’uploader directement la capture sur internet. Et a aussi son propre
mini Paint.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Lecteur Audio/Vidéo&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Il y a &lt;a href="https://www.videolan.org/vlc/"&gt;VLC&lt;/a&gt; et si une vidéo ne se lit pas,
&lt;a href="https://github.com/clsid2/mpc-hc/releases"&gt;MPC-HC&lt;/a&gt; la lira sûrement.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Désinstaller des programmes&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://geekuninstaller.com/download"&gt;Geek Uninstaller&lt;/a&gt; est l’outil parfait
pour désinstaller proprement les programmes (ceux du Microsoft Store aussi).
Il y a aussi &lt;a href="https://www.revouninstaller.com/fr/revo-uninstaller-free-download/"&gt;Revo Uninstaller&lt;/a&gt;
qui a une belle interface, mais qui est plus lourd.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;IDE&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://code.visualstudio.com/download"&gt;VS Code&lt;/a&gt; en
&lt;a href="https://i.imgur.com/YKTs06t.png"&gt;désactivant la télémétrie de Microsoft&lt;/a&gt;
(ou directement &lt;a href="https://vscodium.com/"&gt;VS Codium&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.vim.org/download.php"&gt;Vim&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.gnu.org/software/emacs/download.html"&gt;Emacs&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Suite bureautique&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Celle de &lt;a href="https://tb.rg-adguard.net/public.php"&gt;Microsoft&lt;/a&gt; est payante, mais
&lt;a href="https://github.com/massgravel/Microsoft-Activation-Scripts/releases"&gt;activable avec ce programme&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="https://fr.libreoffice.org/download/telecharger-libreoffice/"&gt;LibreOffice&lt;/a&gt;
est l’alternative aux standards de Microsoft.&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/ONLYOFFICE/DesktopEditors/releases"&gt;OnlyOffice&lt;/a&gt; gère
les standards de Microsoft.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Éditeur d’images&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://www.gimp.org/"&gt;GIMP&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.photopea.com/"&gt;Site qui ressemble à Photoshop&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Avoir &lt;a href="https://gist.github.com/OzakIOne/99e908d8dd8314acbdea1ba78ebcb2b4"&gt;Photoshop illégalement&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Partage d’écran&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://parsec.app/downloads"&gt;Parsec&lt;/a&gt; est stable, facile et gratuit.&lt;/li&gt;&lt;li&gt;&lt;a href="https://anydesk.com/fr?path=fr%2F"&gt;AnyDesk&lt;/a&gt; fonctionne mieux que
&lt;a href="https://www.teamviewer.com/fr/telecharger/windows/"&gt;TeamViewer&lt;/a&gt; et n’est gratuit.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Détection de doublons&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/qarmin/czkawka/releases"&gt;Czkawka&lt;/a&gt; permet de gérer les
fichiers, images et musiques en double.&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.panaustik.com/telechargement/"&gt;Panaustik&lt;/a&gt; et
&lt;a href="https://github.com/ermig1979/AntiDupl/releases"&gt;AntiDupl.NET&lt;/a&gt; sont des
alternatives pour les images, mais elles fonctionnent moins rapidement pour
le même résultat.&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/arsenetar/dupeguru/releases"&gt;dupeGuru&lt;/a&gt; existe aussi,
mais pas encore testé.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Montage vidéo&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://www.mediafire.com/file/ddyegzimf3ab3a7/VEGAS_Pro_17.zip/file"&gt;Vegas Pro&lt;/a&gt;
(payant)&lt;/li&gt;&lt;li&gt;&lt;a href="https://gist.github.com/OzakIOne/99e908d8dd8314acbdea1ba78ebcb2b4"&gt;Adobe Premiere&lt;/a&gt;
(payant).&lt;/li&gt;&lt;li&gt;&lt;a href="https://kdenlive.org/fr/telechargements/"&gt;Kdenlive&lt;/a&gt; gratuit, mais moins
bien que ses concurrents&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.blackmagicdesign.com/fr/products/davinciresolve/"&gt;DaVinci Resolve&lt;/a&gt;
gratuit et fonctionne très bien
(&lt;a href="https://www.youtube.com/channel/UCkNXzFVAwU10ViF7xSR8ieA"&gt;cette chaîne&lt;/a&gt;
fait de bons tutos, &lt;a href="https://www.youtube.com/c/MrAlexTech"&gt;celle-là aussi&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Les logiciels &lt;a href="https://sourceforge.net/projects/avidemux/files/avidemux/"&gt;Avidemux&lt;/a&gt;
et &lt;a href="https://github.com/mifi/lossless-cut/releases"&gt;LosslessCut&lt;/a&gt; permettent
de faire du montage plus basique.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Émulation&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Pour émuler Android la meilleure chose est d’utiliser
&lt;a href="https://www.android-x86.org/"&gt;Android x86&lt;/a&gt; avec
&lt;a href="https://www.virtualbox.org/wiki/Downloads"&gt;VirtualBox&lt;/a&gt; ou, si tu as
Windows 10 Pro/Entreprise avec
&lt;a href="https://docs.microsoft.com/fr-fr/virtualization/hyper-v-on-windows/about/"&gt;Hyper-V&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Téléchargeur de vidéos YouTube&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/Tyrrrz/YoutubeDownloader/releases"&gt;Youtube Downloader&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/blackjack4494/yt-dlc/releases"&gt;Youtube-DLC&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/oleksis/youtube-dl-gui/releases"&gt;Youtube-DLG&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Prise de notes&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/marktext/marktext/releases"&gt;Marktext&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Éditeur Markdown simple.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/laurent22/joplin/releases"&gt;Joplin&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Prise de notes et liste “To Do” avec synchronisation intégrée.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Zettlr/Zettlr/releases"&gt;Zettlr&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Éditeur Markdown plus avancé que Marktext.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://codimd.math.cnrs.fr/"&gt;CodiMD&lt;/a&gt; &lt;em&gt;(site internet)&lt;/em&gt;&lt;ul&gt;&lt;li&gt;Instance du CNRS pour tous les étudiants de France.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Support de plusieurs plugins, ce qui le rend très puissant.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://eartrumpet.app/"&gt;EarTrumpet&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Contrôleur de volume pour Windows.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://fosscord.com/"&gt;FossCord&lt;/a&gt; (en dev)&lt;ul&gt;&lt;li&gt;Une tentative de créer une version d’un Discord compatible avec celles
officielles, mais qui serait plus sécurisé et auto-hébergeable.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.spotify.com/fr/download/other/"&gt;Spotify&lt;/a&gt; sans pubs&lt;ul&gt;&lt;li&gt;Un &lt;a href="https://gist.github.com/Davoleo/de08de14934146847ef7fff070e4a04b"&gt;guide&lt;/a&gt;
est disponible pour ne pas avoir de pubs, le programme utilisé est
&lt;a href="https://github.com/mrpond/BlockTheSpot/blob/master/SpotXBasic.bat"&gt;BlockTheSpot&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://imageglass.org/"&gt;ImageGlass&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Il lit presque toutes les images rapidement, cependant l’application Windows
fait déjà bien le travail (alternative de
&lt;a href="https://www.fosshub.com/IrfanView.html"&gt;Irfanview&lt;/a&gt; (avec tous les plugins)).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.7-zip.org/"&gt;7-zip&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Pour les archives,
&lt;a href="https://www.reddit.com/r/pcmasterrace/comments/68a9yz/7zip_vs_winrar_comparison/"&gt;mieux&lt;/a&gt;
que &lt;a href="https://www.win-rar.com/predownload.html"&gt;WinRAR&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.voidtools.com/"&gt;Everything Lite&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Recherche Windows amélioré.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://wiztreefree.com/download"&gt;WizTree&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Affiche quels sont les dossiers et fichiers qui prennent de la place sur
ton PC&lt;ul&gt;&lt;li&gt;Alternatives (&lt;em&gt;moins bien&lt;/em&gt;) :&lt;ul&gt;&lt;li&gt;&lt;a href="https://windirstat.net/download.html"&gt;WinDirStat&lt;/a&gt; (lent)&lt;/li&gt;&lt;li&gt;&lt;a href="https://customers.jam-software.de/downloadTrial.php?&amp;amp;article_no=80"&gt;Treesize&lt;/a&gt;
(interface Windows 8)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.qbittorrent.org/download.php"&gt;qBittorrent&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Pour télécharger des torrents.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/gyf304/lpvpn"&gt;LAN Party VPN&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Permets de jouer en multijoueur sur des jeux qui ne supportent qu’un mode
&lt;em&gt;local&lt;/em&gt;. Fonctionne avec &lt;a href="https://discord.com/"&gt;Discord&lt;/a&gt;.&lt;ul&gt;&lt;li&gt;Alternatives moins bien :&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.radmin-vpn.com/fr/"&gt;Radmin VPN&lt;/a&gt;
(installe un autre programme inutile)&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.vpn.net/"&gt;Hamachi&lt;/a&gt; (vieux)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://audacity.fr/"&gt;Audacity&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Pour enregistrer sa voix et y apporter des modifications.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://obsproject.com/fr/download"&gt;OBS Studio&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Enregistrer son écran, Stream, Shadowplay &lt;em&gt;-like&lt;/em&gt; intégré
(tampon de relecture).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://handbrake.fr/"&gt;Handbrake&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Permets de convertir des vidéos.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.diskgenius.com/download.php"&gt;DiskGenius&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Gère les problèmes avec les disques de stockage.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/rocksdanister/lively/releases"&gt;Lively&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Fond d’écran animé et gratuit.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/elmoiv/elmocut/releases"&gt;Elmocut&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Limiteur de connexion pour les périphériques connectés au même réseau&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Genymobile/gnirehtet/releases"&gt;Gnirehtet&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Partage de connexion du PC vers le téléphone.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Ryochan7/DS4Windows/releases"&gt;DS4Windows&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Ut1iliser une manette PS4 sur PC.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Genymobile/scrcpy/releases"&gt;scrcpy&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Partage d’écran Android sur PC avec ou sans câble.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.sourcetreeapp.com/"&gt;Sourcetree&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Interface pour Git (fun fact, si tu as python-tk d’installer, tu peux utiliser
&lt;code&gt;gitk&lt;/code&gt; comme interface).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://winmerge.org/?lang=fr"&gt;Winmerge&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Différence entre deux fichiers.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-benchmarks-stress-tests-et-infos-pc" id="cat-benchmarks-stress-tests-et-infos-pc"&gt;&lt;/a&gt;Benchmarks, stress tests et infos PC&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href="https://userdiag.com/"&gt;Ce site&lt;/a&gt; permet de faire un rapide diagnostic de ton PC.&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://www.atto.com/disk-benchmark/"&gt;ATTO Disk Benchmark&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Benchmark de stockage avec informations très détaillées.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://crystalmark.info/en/software/crystaldiskinfo/"&gt;CrystalDiskInfo&lt;/a&gt; et
&lt;a href="https://crystalmark.info/en/software/crystaldiskmark/"&gt;CrystalDiskMark&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Benchmark plus concis et infos sur le stockage.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.maxon.net/en/downloads"&gt;Cinebench&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Benchmark et stresstest du CPU (trouvable aussi sur le
&lt;a href="https://www.microsoft.com/fr-fr/p/cinebench/9pgzkjc81q7j"&gt;Microsoft Store&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.aida64.com/downloads"&gt;AIDA64&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Benchmark et stresstest pour le CPU et la RAM.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.guru3d.com/files-details/prime95-download.html"&gt;Prime95&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Stress test du CPU.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.cpuid.com/softwares/cpu-z.html"&gt;CPU-Z&lt;/a&gt; et
&lt;a href="https://www.techpowerup.com/gpuz/"&gt;GPU-Z&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Infos détaillées sur le CPU et le GPU.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://geeks3d.com/furmark/"&gt;FurMark&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Stress test du GPU.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://unigine.com/"&gt;Unigine&lt;/a&gt;, des benchmarks et stresstests pour le GPU.&lt;ul&gt;&lt;li&gt;&lt;a href="https://benchmark.unigine.com/valley"&gt;Valley [2013]&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://benchmark.unigine.com/superposition"&gt;Superposition [2017]&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://benchmark.unigine.com/heaven"&gt;Heaven [2019]&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.3dmark.com/"&gt;3DMark&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Benchmark et stresstest pour le CPU et le GPU.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://rog.asus.com/tag/realbench/"&gt;Asus realbench&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Stresstest et benchmark pour le GPU et le CPU.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://fr.msi.com/page/afterburner"&gt;MSI Afterburner&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Pour OC ta Carte graphique Nvidia et modifier la courbe de tes ventilos.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/lostindark/DriverStoreExplorer/releases"&gt;DriverStore Explorer&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Explorateur de drivers installés sur le PC&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/CXWorld/CapFrameX/releases"&gt;CapFrameX&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Permets de faire des beaux graphiques pour des benchmarks&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-équivalents-à-la-suite-dadobe" id="cat-équivalents-à-la-suite-dadobe"&gt;&lt;/a&gt;Équivalents à la suite d’Adobe&lt;/h3&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/rt1d8fJ.png" width="500" /&gt;
    &lt;br /&gt;
    Source : &lt;a href="https://www.reddit.com/r/linuxmasterrace/"&gt;r/linuxmasterrace&lt;/a&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#cat-apps-android" id="cat-apps-android"&gt;&lt;/a&gt;Apps Android&lt;/h2&gt;&lt;p&gt;&lt;em&gt;J’ai un téléphone&lt;/em&gt;
&lt;a href="https://www.lesnumeriques.com/telephone-portable/xiaomi-mi-note-10-lite-p57723/test.html"&gt;&lt;em&gt;Xiaomi Mi Note 10 lite&lt;/em&gt;&lt;/a&gt;,
&lt;em&gt;non rooté que j’ai debloat, la liste des applications que j’ai désinstallées est&lt;/em&gt;
&lt;em&gt;&lt;a href="https://mylloon.fr/blog/p/debloat_miui"&gt;ici&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=org.mozilla.firefox"&gt;Firefox&lt;/a&gt; /
&lt;a href="https://play.google.com/store/apps/details?id=com.brave.browser"&gt;Brave&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Rapide sur téléphone, et comprend les mêmes extensions que j’utilise sur PC
(uBlock, DarkReader, BitWarden). Brave fait la même chose.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://forum.mobilism.org/viewtopic.php?f=1332&amp;amp;t=3788759"&gt;Spotify&lt;/a&gt;&lt;ul&gt;&lt;li&gt;J’utilise Spotify, tu peux utiliser &lt;a href="https://github.com/xManager-v2/xManager-Spotify/releases"&gt;xManager&lt;/a&gt;
ou &lt;a href="https://gist.github.com/Davoleo/de08de14934146847ef7fff070e4a04b"&gt;suivez ce guide&lt;/a&gt;,
ou même utiliser &lt;a href="https://git.kennel.ml/Anri/mobilismScrap"&gt;mon application qui va chercher l’apk sur Mobilism&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://revanced.app/"&gt;YouTube ReVanced&lt;/a&gt;&lt;ul&gt;&lt;li&gt;YouTube sans pubs, etc. En attendant que le manager sorte, tu peux lire
&lt;a href="https://mylloon.fr/blog/p/revanced"&gt;ma configuration ici&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://f-droid.org/fr/packages/com.moez.QKSMS/"&gt;QKSMS&lt;/a&gt; (version payante
disponible sur F-Droid)&lt;ul&gt;&lt;li&gt;Application SMS.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://f-droid.org/packages/com.fsck.k9/"&gt;K-9 Mail&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Remplaçant de Gmail, configuration facile et expérience similaire à
cette dernière.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/jobobby04/TachiyomiSY/releases"&gt;TachiyomiSY&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Fork de &lt;a href="https://tachiyomi.org/"&gt;Tachiyomi&lt;/a&gt; pour lire des mangas
&lt;em&gt;(&lt;a href="https://tachiyomi.org/extensions/"&gt;extensions Tachiyomi&lt;/a&gt; que j’utilise :&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#fr.frscan"&gt;FR Scan&lt;/a&gt;,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#all.komga"&gt;Komga&lt;/a&gt; avec mon instance,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#all.littlegarden"&gt;Little Garden&lt;/a&gt;,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#fr.mangasorigines"&gt;Mangas Origines&lt;/a&gt;,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#en.mangarawclub"&gt;mcreader.net&lt;/a&gt;,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#all.ninemanga"&gt;NineManga&lt;/a&gt;,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#fr.scanfr"&gt;Scan FR&lt;/a&gt;,&lt;/em&gt;
&lt;em&gt;&lt;a href="https://tachiyomi.org/extensions/#all.webtoons"&gt;Webtoons.com&lt;/a&gt;).&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=wangdaye.com.geometricweather"&gt;Geometric Weather&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Application météo (&lt;a href="https://meteofrance.com/"&gt;Météo France&lt;/a&gt; est une des
source disponible).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden"&gt;Bitwarden&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Gestionnaire de mot de passe.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=de.spiritcroc.riotx"&gt;SchildiChat&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Un mélange entre WhatsApp et Discord, décentralisé, qui fonctionne avec le
protocole &lt;a href="https://matrix.org/"&gt;Matrix&lt;/a&gt;
(fork d’&lt;a href="https://play.google.com/store/apps/details?id=im.vector.app"&gt;Element&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Iamlooker/Droid-ify/releases"&gt;Droid-ify&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Alternatif à F-Droid, simple et pas trop moche. J’ai listé les repos que
j’utilise &lt;a href="https://mylloon.fr/blog/p/fdroid_repos"&gt;ici&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=ml.docilealligator.infinityforreddit"&gt;Infinity&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Client Reddit.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=me.zhanghai.android.files"&gt;Material Files&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Bonne application pour les fichiers, simple et belle.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/yausername/dvd/releases"&gt;dvd&lt;/a&gt; ou
&lt;a href="https://github.com/bawaviki/youtube-dl-android/releases"&gt;Youtube-DL-Android&lt;/a&gt; ou
&lt;a href="https://github.com/JunkFood02/Seal/releases"&gt;Seal&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Une application pour télécharger des vidéos sur téléphone (intégration avec
ReVanced).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.omgodse.notally"&gt;Notally&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Pour prendre des notes.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://f-droid.org/packages/ws.xsoh.etar/"&gt;Etar&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Alternative au Google Agenda.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/deckerst/aves/releases"&gt;Aves&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Application galerie.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/prathameshmm02/Calculator-inator/releases"&gt;Calculator-inator&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Application calculatrice.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Kr0oked/Compass/releases"&gt;Compass&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Application boussole.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/jarnedemeulemeester/findroid/releases"&gt;Findroid&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Alternative au client officiel de Jellyfin.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://f-droid.org/packages/us.spotco.maps/"&gt;GMaps WV&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Google Maps dans une application pour limiter au maximum de donner des
informations à Google.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Livinglist/Hacki/releases"&gt;Hacki&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Client Hacker News.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Chooloo/koler/releases"&gt;Koler&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Application téléphone&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.signal.org/fr/download/"&gt;Signal&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Messagerie sécurisée&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/erickok/transdroid/releases"&gt;Transdrone&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Client pour mon instance &lt;a href="https://transmissionbt.com/"&gt;Transmission&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-liens-utiles" id="cat-liens-utiles"&gt;&lt;/a&gt;Liens utiles&lt;/h2&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-documentation" id="cat-documentation"&gt;&lt;/a&gt;Documentation&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;S’informer sur la tech&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Il y a &lt;a href="https://docs.google.com/document/d/1jLqrHPCK70dxc-_O6cYXaEESy8q3Ug5FoSs7qpxRlIk/edit"&gt;ce gdoc&lt;/a&gt;
qui répertorie plusieurs sites et chaînes YouTube fiables pour s’informer.
Pour lire les flux RSS proposés par les sites il y a
&lt;a href="https://www.inoreader.com/all_articles"&gt;Inoreader&lt;/a&gt; sur PC et téléphone.
Perso j’utilise &lt;a href="https://f-droid.org/fr/packages/apps.amine.bou.readerforselfoss/"&gt;Selfoss&lt;/a&gt;
avec une instance que j’héberge moi-même.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Vérification fichiers systèmes en cas de problèmes avec Windows :&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Windows + R → &lt;code&gt;cmd&lt;/code&gt; → CTRL + MAJ + ENTRÉE &lt;em&gt;(pour lancer cmd en admin)&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Ensuite dans l’ordre et une par une les commandes suivantes (ça peut prendre
un peu de temps)&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;code class="language-hljs-powershell"&gt;DISM /Online /Cleanup-Image /CheckHealth
DISM /Online /Cleanup-Image /ScanHealth
DISM /Online /Cleanup-Image /RestoreHealth
sfc /scannow
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Monter son PC avec processeur &lt;a href="https://www.youtube.com/watch?v=scrJiy_tL4Q"&gt;Intel&lt;/a&gt;
ou &lt;a href="https://www.youtube.com/watch?v=VM2fUEjTRZE"&gt;AMD&lt;/a&gt;
(&lt;a href="https://www.youtube.com/playlist?list=PLYwBaLsBmW7xIfLl0gCUGbwKJK5N9KU0V"&gt;Playlist Topachat&lt;/a&gt;
pour les généralités).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href="https://youtu.be/0L2XpBGKUa4"&gt;Pour réinstaller proprement sa carte graphique&lt;/a&gt;
quand il y a quelque chose qui pose problème.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Récapitulatifs des différences entre les types d’écrans :&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/JrUPeN0.png" width="700" /&gt;
    &lt;br /&gt;
    Source : &lt;a href="https://www.mrhightech.fr/msi-optix-mag241cr-test/"&gt;mrhightech.fr&lt;/a&gt;
&lt;/p&gt;
&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/JNXlQVt.png" width="700" /&gt;
    &lt;br /&gt;
    Source : &lt;a href="https://youtu.be/9ZwCDBBwSdU"&gt;The Tech Chap&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=gyZZQxA3Vwc"&gt;Les switchs mécaniques&lt;/a&gt; (dans
la vidéo, tu peux ignorer toute la partie axée sur la &lt;em&gt;vitesse d’appui&lt;/em&gt;
&lt;em&gt;sur la touche&lt;/em&gt;, car dans la vraie vie, tu ne sentiras jamais la différence).&lt;/li&gt;&lt;/ul&gt;&lt;p align="center"&gt;&lt;img src="https://i.imgur.com/iyFUZmm.png" width="550" /&gt;
    &lt;br /&gt;
    Source : &lt;a href="https://blog.materiel.net/clavier-mecanique-le-retour-tactile-ou-lineaire-du-roi/"&gt;materiel.net&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href="https://www.techpowerup.com/download/visual-c-redistributable-runtime-package-all-in-one/"&gt;Visual C++ Redistributable Runtimes All-in-One&lt;/a&gt;
pour installer tous les composants nécessaires à plein de programmes après une
installation de Windows.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Explication sur les spécifications des câbles Ethernet
&lt;img src="https://i.imgur.com/C9AKhu8.png" width="900" /&gt;
&lt;br /&gt;
Source : julienth37#7125 du &lt;a href="https://discord.gg/8YF6xUE"&gt;serveur Discord de Wanda&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href="https://www.docdroid.net/BYU9Dis/mousecleaningguide-pdf"&gt;Tuto pour bien nettoyer son tapis de souris&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Fenêtres flottantes sous MIUI&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Si, comme moi, vous utilisez YouTube Vanced et que vous avez dû désactiver
les &lt;em&gt;optimisations MIUI&lt;/em&gt;, vous avez aussi perdu la fonctionnalité de fenêtre
flottante. Pour récupérer cette fonctionnalité, vous devez activer la
fonctionnalité &lt;em&gt;Activer les fenêtres en forme libre (Activer le support pour&lt;/em&gt;
&lt;em&gt;les fenêtres expérimentales en forme libre)&lt;/em&gt; sous la catégorie &lt;em&gt;Applications&lt;/em&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Mixaill/awesome-gog-galaxy/blob/master/README.md"&gt;Liste des intégrations de GOG Galaxy 2.0 et de ce qui est prévu pour l’avenir&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/abbodi1406/offlineinsiderenroll"&gt;Active le mode Insider dans Windows sans renseigner de compte Microsoft&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Explication de comment est nommé en CPU Intel
&lt;img src="https://i.imgur.com/hr7SLGQ.png" width="800" /&gt;
&lt;br /&gt;
Source : Heavenlot#0002 du &lt;a href="https://discord.gg/informatique/"&gt;serveur de l’entraide informatique de Capet&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Sens du ventilateur&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="https://media.discordapp.net/attachments/479583079342538753/916449628910673940/00088.png" /&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-sites" id="cat-sites"&gt;&lt;/a&gt;Sites&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Vie privée&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;La vie privée, c’est important, &lt;a href="https://www.privacyguides.org/"&gt;ce site&lt;/a&gt; fait
une bonne liste des applications cool pour votre vie privée, à checker mais
ne pas tout prendre au pied de la lettre, ça donne juste des idées.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Savoir faire le bon choix&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://docs.google.com/spreadsheets/d/e/2PACX-1vSA8AE88v4BP-43UjynJLTDAsNA9r50pZkHCsX2jT4x59AssLweT09s6DoIG9vEAgnE25mn3t3tWZ20/pubhtml"&gt;Boitier, Écran, Watercooling&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1RAnmZxDNduaGV8kB-GCvZ0MO6d9-0j9jmrU2f8dp0Ww/edit#gid=0"&gt;Tapis de souris&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.rocketjumpninja.com/mouse-search"&gt;Souris&lt;/a&gt;
(&lt;a href="https://www.rtings.com/mouse/tools/3d-model-shape-compare/?orientation=3D"&gt;comparaison 3D&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.av-test.org/fr/antivirus/particuliers-windows/"&gt;Antivirus&lt;/a&gt;
(tldr: Windows Defender est très bon).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Alternative à Google Drive&lt;/strong&gt; &lt;em&gt;&amp;amp; Co&lt;/em&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://cryptpad.fr/"&gt;Cryptpad&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Alternative complète au Google Drive (limité à 1 Go sur cette instance
mais possibilité d’utiliser sans compte).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://ethercalc.net/"&gt;EtherCalc&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Collaboration quasi instantanée (il faut valider les cellules) mais
facile à utiliser.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Tracker de prix&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://ledenicheur.fr/"&gt;Pour des produits généraux&lt;/a&gt; et &lt;a href="https://www.idealo.fr/"&gt;idealo&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.gputracker.eu/fr"&gt;Pour composants informatiques&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Dessin&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://excalidraw.com/"&gt;Excalidraw&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Collaborative, lien partageable, possibilité d’exportation en
PNG/JPG et presets.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://asciiflow.com/#/"&gt;En ASCII&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://kroki.io/#"&gt;Diagrammes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://whiteboardfox.com/"&gt;Style “tableau blanc”&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Comparateurs&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://versus.com/fr"&gt;Pour des produits généraux&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.kimovil.com/fr/"&gt;Spécialisé sur les téléphones portables&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://juxtapose.knightlab.com/"&gt;Comparer deux images&lt;/a&gt; &lt;em&gt;(juxtaposeJS)&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://eternalbox.dev/jukebox_index.html"&gt;Répéteur de “morceau” dans une chanson&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://send.vis.ee/"&gt;Hébergeur de fichiers&lt;/a&gt; basés sur l’ancien projet de Mozilla.&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.rtings.com/tv/reviews/by-size/size-to-distance-relationship"&gt;Connaître la distance parfaite &lt;em&gt;théorique&lt;/em&gt; entre vous et un écran&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://colormind.io/image/"&gt;Site pour avoir les couleurs principales d’une image&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://tosdr.org/"&gt;Site qui récapitule les bons et mauvais points des conditions d’utilisation&lt;/a&gt;
d’un site.&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.frameip.com/"&gt;Grosse documentation sur le réseau&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://sql.sh/"&gt;Grosse documentation sur le SQL&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://free-for.dev/#/"&gt;Grosse documentation sur différents outils pour les devs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.fonerbooks.com/pcrepair.htm"&gt;Procédure pour trouver le problème d’un PC quand il ne démarre pas&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.justtherecipe.com/"&gt;Site pour avoir un bel affichage pour une recette de cuisine&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.adobe.com/acrobat/online/compress-pdf.html"&gt;Compresser les PDF avec l’outil d’Adobe&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://squoosh.app/"&gt;Compresser image&lt;/a&gt; dans plusieurs formats.&lt;/li&gt;&lt;li&gt;&lt;a href="https://app.screego.net/"&gt;Partage d’écran dans le navigateur&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-jeux" id="cat-jeux"&gt;&lt;/a&gt;Jeux&lt;/h2&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-minecraft" id="cat-minecraft"&gt;&lt;/a&gt;Minecraft&lt;/h3&gt;&lt;p&gt;J’ai fait &lt;a href="https://youtu.be/7Qq24Ji3p2Y"&gt;une vidéo qui fait un peu le tour de ma configuration Minecraft&lt;/a&gt;
si les détails t’intéressent.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://multimc.org/#Download"&gt;MultiMC&lt;/a&gt; est un client pour mieux gérer les
versions et les mods, il y a aussi &lt;a href="https://polymc.org/download/"&gt;PolyMC&lt;/a&gt; qui
est globalement mieux.&lt;/li&gt;&lt;li&gt;&lt;a href="https://fabricmc.net/"&gt;Fabric&lt;/a&gt; est l’outil qui permet de charger les Mods dans
minecraft (remplaçant de &lt;a href="https://files.minecraftforge.net/net/minecraftforge/forge/"&gt;Forge&lt;/a&gt;).
Il y a aussi &lt;a href="https://quiltmc.org/"&gt;Quilt&lt;/a&gt; qui est prometteur.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Mods&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://modrinth.com/"&gt;Modrinth&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Annuaire de mods.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Optimisateurs&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/juliand665/Dynamic-FPS/releases"&gt;Dynamic FPS&lt;/a&gt; met
les FPS à 1 quand le jeu est en second plan et a 0 quand le jeu est réduit.&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/tr7zw/EntityCulling/releases"&gt;EntityCulling&lt;/a&gt; cache les
entités qui sont invisibles au joueur (de dos par exemple).&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/astei/lazydfu/releases"&gt;LazyDFU&lt;/a&gt; permet d’améliorer
le temps de chargement du jeu.&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/CaffeineMC/lithium-fabric/releases"&gt;Lithium&lt;/a&gt; optimise
aussi le jeu globalement (côté serveur, utile aussi en solo) ou
&lt;a href="https://github.com/astei/krypton/releases"&gt;Krypton&lt;/a&gt; optimise la partie
réseau entre client et serveur (compatible en théorie avec Lithium mais
Krypton est meilleur dans la partie réseau, au choix).&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/fxmorin/MemoryLeakFix/releases"&gt;MemoryLeakFix&lt;/a&gt; résoud
des fuites de mémoires.&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/CaffeineMC/sodium-fabric/releases"&gt;Sodium&lt;/a&gt; optimise
grandement le jeu en sa globalité (&lt;a href="https://github.com/FlashyReese/sodium-extra-fabric/releases"&gt;Sodium Extra&lt;/a&gt;
pour plus d’options).&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/CaffeineMC/phosphor-fabric/releases"&gt;Phosphor&lt;/a&gt;
(ou &lt;a href="https://modrinth.com/mod/starlight"&gt;Starlight&lt;/a&gt;) optimise la partie
lumière (soit l’un soit l’autre).&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/CaffeineMC/hydrogen-fabric/releases"&gt;Hydrogen&lt;/a&gt;
optimise la gestion de la RAM pour les partie avec beaucoup de mods.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Shaders&lt;/strong&gt; &lt;em&gt;(moteurs)&lt;/em&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/IrisShaders/Iris/releases"&gt;Iris&lt;/a&gt;, intègre Sodium.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/emilyploszaj/emi/releases"&gt;EMI&lt;/a&gt; affiche un grand
inventaire avec toutes les recettes.&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Johni0702/bobby/releases"&gt;Bobby&lt;/a&gt; permet d’avoir une
grande render distance en multijoueur&lt;/li&gt;&lt;li&gt;&lt;a href="https://modrinth.com/mod/fabrishot"&gt;Fabrishot&lt;/a&gt; permet de prendre des
captures d’écrans en très haute définition.&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.curseforge.com/minecraft/mc-mods/wi-zoom"&gt;WI Zoom&lt;/a&gt; permet de
zoomer dans le jeu&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/apace100/origins-fabric/releases"&gt;Origins&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Permets de sélectionner une classe au démarrage d’un monde pour avoir
des aptitudes uniques.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Cubitect/cubiomes-viewer/releases"&gt;Cubiomes viewer&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Affiche la map avec ses biomes et l’emplacement des lieux importants,
atlernative à &lt;a href="https://github.com/toolbox4minecraft/amidst/releases"&gt;Amidst&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/chunky-dev/chunky/releases"&gt;Chunky&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Permets de regénérer des chunks en les supprimant des fichiers du jeu.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Shaders&lt;/strong&gt; &lt;em&gt;(packs)&lt;/em&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://www.complementary.dev/"&gt;Complementary Shaders v4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://www.sonicether.com/seus/"&gt;SEUS &lt;strong&gt;Renewed&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://sildurs-shaders.github.io/downloads/"&gt;Sildur’s &lt;strong&gt;Vibrant Shaders&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-return-to-castle-wolfenstein" id="cat-return-to-castle-wolfenstein"&gt;&lt;/a&gt;Return To Castle Wolfenstein&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/iortcw/iortcw"&gt;IORTCW&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-league-of-legends" id="cat-league-of-legends"&gt;&lt;/a&gt;League of Legends&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/RiotGames/leaguedirector/releases"&gt;League Director&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;a class="anchor" href="#cat-émulation" id="cat-émulation"&gt;&lt;/a&gt;Émulation&lt;/h3&gt;&lt;h4&gt;&lt;a class="anchor" href="#cat-switch" id="cat-switch"&gt;&lt;/a&gt;Switch&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://yuzu-emu.org/"&gt;Yuzu&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Émulateur Switch&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href="https://youtu.be/zkMN_V-8czU?t=947"&gt;Vidéo&lt;/a&gt; -
&lt;a href="https://nsw2u.in/"&gt;Jeux site 1&lt;/a&gt; -
&lt;a href="https://docs.google.com/spreadsheets/d/e/2PACX-1vSt88LJF-rLO8ICAVoTIRNrPCEAUeZfqF0wjFN5FwEZiiHyFyATemRJ20BA6BfmuEex8deWt8hMdZUD/pubhtml"&gt;Jeux site 2&lt;/a&gt; -
&lt;a href="https://mega.nz/file/5MZHAAAR#TtEo7s_MIzgSjYrdDoOD4CCZUxMvD9DSpCufa4nFLlY"&gt;keys dir&lt;/a&gt; -
&lt;a href="https://drive.google.com/file/d/13BYcQ_GcuWaE5Ka8zo6pN-giT4bYSH_u/view"&gt;nand dir&lt;/a&gt; (&lt;a href="https://youtu.be/HYtq2yLfZBE"&gt;source&lt;/a&gt;) -
&lt;a href="https://darthsternie.net/switch-firmwares/"&gt;Firmware&lt;/a&gt; (inutile) -
&lt;a href="https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods"&gt;Mods GitHub&lt;/a&gt; -
&lt;a href="https://yuzu-emu.org/game/"&gt;Compatibilité des jeux&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;&lt;a class="anchor" href="#cat-multi-plateforme" id="cat-multi-plateforme"&gt;&lt;/a&gt;Multi Plateforme&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.swordfish.lemuroid"&gt;Lemuroid&lt;/a&gt;&lt;ul&gt;&lt;li&gt;Émule plusieurs consoles portables sur Android.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;&lt;a class="anchor" href="#cat-contact--infos" id="cat-contact--infos"&gt;&lt;/a&gt;Contact &amp;amp; Infos&lt;/h2&gt;&lt;p&gt;&lt;a href="https://fr.pcpartpicker.com/user/Mylloon/saved/#view=GF8MCJ"&gt;&lt;em&gt;Ma config (2019)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;&lt;hr /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Site&lt;/strong&gt; : &lt;a href="https://www.mylloon.fr/"&gt;&lt;em&gt;www.mylloon.fr&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Réseaux&lt;/strong&gt; : &lt;a href="https://mylloon.fr/c"&gt;&lt;em&gt;/networks&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn-update"&gt;&lt;p&gt;Notice de dépréciation. &lt;a class="footnote-backref" href="#fnref-update"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;</description><author>Blog d'Anri</author><pubDate>Wed, 03 Nov 2021 14:12:00 GMT</pubDate><guid isPermaLink="true">https://www.mylloon.fr/blog/p/cat</guid></item><item><title>Tech rants: PC-s use way too much power in 2021</title><link>https://ounapuu.ee/posts/2021/11/03/tech-rants-1/</link><description>Quick rant about the state of PC-s in 2021 regarding power and resource usage.</description><author>./techtipsy</author><pubDate>Wed, 03 Nov 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/11/03/tech-rants-1/</guid></item><item><title>Drag and drop image upload</title><link>https://www.pinecoder.dev/blog/2021-11-03/drag-and-drop-image-upload/</link><description>While planning drag and drop image support in Chronicles, I had some confusion about handling files once dropped. There are many tutorials out there but they often provide a &amp;ldquo;copy this code&amp;rdquo; approach and offer little insight. Many recommended using base64 encoding but that did not seem right. The more general problem of file uploading through a browser is one I occassionally return to, so I wanted a conceptual deep dive I could use as a foundation.</description><author>Home on Pinecoder blog</author><pubDate>Wed, 03 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.pinecoder.dev/blog/2021-11-03/drag-and-drop-image-upload/</guid></item><item><title>Impressions of the Framework Laptop</title><link>https://maxleiter.com/blog/framework</link><description>&lt;p&gt;I received my &lt;a href="https://frame.work" rel="noopener noreferrer" target="_blank"&gt;Framework Laptop DIY edition&lt;/a&gt; a few days ago and have been setting it up software-wise. I have it running Manjaro Linux with the Sway window manager. This is my first time using a Wayland desktop, and oh has it been a learning experience. This post will be largely about the hardware, but I plan to make one about the software once I'm more happy with my setup.&lt;/p&gt;
&lt;p&gt;Here's what I got:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Framework Laptop DIY Edition&lt;/li&gt;
&lt;li&gt;16GB (2 x 8GB) DDR4-3200&lt;/li&gt;
&lt;li&gt;Intel® Wi-Fi 6E AX210 No vPro&lt;/li&gt;
&lt;li&gt;1TB - WD_BLACK™ SN850 NVMe™&lt;/li&gt;
&lt;li&gt;USB-A Expansion Card&lt;/li&gt;
&lt;li&gt;USB-C Expansion Card x2&lt;/li&gt;
&lt;li&gt;HDMI Expansion Card&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I bought everything from the Framework store (at a slight markup of ~$80 for the RAM, SSD, and wifi card), but I'm happy to support them and for the convenience of knowing it would work properly was well worth it.&lt;/p&gt;
&lt;h2&gt;The Good&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Well crafted body. I was not expecting a premium build quality from a startup trying to create an upgradeable laptop, but wow they excelled.&lt;/li&gt;
&lt;li&gt;It's &lt;em&gt;really light&lt;/em&gt;. It's just 2.8 pounds, compared to my 13&amp;quot; Macbook Pro's 3.02. You wouldn't think that's a big difference, and maybe you wouldn't notice, but I sure did.&lt;/li&gt;
&lt;li&gt;A solid keyboard. Most non-Mac laptops I've tried have awful keyboards. It's not as rigid as an Apple non-butterfly board, but it's close. Definitly usable.&lt;/li&gt;
&lt;li&gt;Modular expansion ports (via pass-through to USB-C) are a fantastic idea. I can't wait for 3rd party ones to be released.&lt;/li&gt;
&lt;li&gt;Hardware switches for the camera and microphone.&lt;/li&gt;
&lt;li&gt;Beautiful glossy screen. I haven't used it extensively outside, but it's worked well enough for me (especially at full brightness).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Bad&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;There are only 3 preset levels of keyboard backlight you can switch between: off, on, and half. Perhaps my Mac spoiled me, but I'm used to having more fine-tuned control over it.&lt;/li&gt;
&lt;li&gt;Installing the wifi card was difficult. It took me twice as long to install the WiFi as it did to setup the rest of the computer. They acknowledge this in their installation instructions, but othes seem to also be having problems:&lt;br /&gt;&lt;img alt="Image of the Framework setup guide comments showing 6 people also having difficulty" src="/blog/framework/comments.png" /&gt;&lt;/li&gt;
&lt;li&gt;Removing expansion ports requires far more force than installing them. Feels a little awkward.&lt;/li&gt;
&lt;li&gt;12 seconds of pressing the power button to hard reboot. Not that big of a deal, but definitely feels &lt;em&gt;way&lt;/em&gt; too long.&lt;/li&gt;
&lt;li&gt;Why does the screen need to swing open 185°?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Ugly&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This isn't necessarily due to the hardware, but the battery life on Linux currently requires a little finessing to be reasonable. I'm sitting at ~4h of moderate use after tweaking numerous configs and following multiple different sets of instructions from around the internet. This isn't good enough for me to take on campus for class, but it's great for doing work around the house.&lt;/li&gt;
&lt;li&gt;Lacks the ability to set charging thresholds. It's &lt;a href="https://community.frame.work/t/battery-charge-optioms/755/3" rel="noopener noreferrer" target="_blank"&gt;being worked on&lt;/a&gt;, but at the moment it doesn't seem possible.&lt;/li&gt;
&lt;li&gt;A strange 2256x1504 resolution. Why couldn't they have chosen something more standard? Although I think my issues with this are mostly due to Linux / Wayland. Maybe I'll get used to 3:2.&lt;/li&gt;
&lt;li&gt;It requires a little extra care to open the laptop without sliding it (perhaps due to its light weight).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;In conclusion&lt;/h2&gt;
&lt;p&gt;I'll let my Tweet from a few days ago sum it up:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/maxleiter/status/1455642990274248706" rel="noopener noreferrer" target="_blank"&gt;![Image of the Framework setup guide comments showing 6 people also having difficulty](/blog/framework/twitter.png)&lt;/a&gt;&lt;/p&gt;</description><author>Max Leiter</author><pubDate>Wed, 03 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://maxleiter.com/blog/framework</guid></item><item><title>Git loses untracked files during "stash" if there are conficts</title><link>https://www.databasesandlife.com/git-stash-loses-untracked-files/</link><description>&lt;p&gt;Git had a bug from version 2.33 onwards, fixed in 2.35. (Thanks to &lt;a href="https://www.databasesandlife.com/git-stash-loses-untracked-files/#comment-6225425065"&gt;Richard Laager&amp;rsquo;s comment&lt;/a&gt; for the exact version numbers.)&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Stashing&amp;rdquo; then &amp;ldquo;unstashing&amp;rdquo; untracked files (new files you&amp;rsquo;ve just created) loses those new files, &lt;em&gt;if&lt;/em&gt; any of the existing tracked files has a conflict.&lt;/p&gt;
&lt;p&gt;Let me explain.&lt;/p&gt;</description><author>Databases &amp;amp; Life</author><pubDate>Wed, 03 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.databasesandlife.com/git-stash-loses-untracked-files/</guid></item><item><title>Unstructured data are BAD for everybody</title><link>https://stop.zona-m.net/2021/11/unstructured-data-are-bad-for-everybody/</link><description>&lt;p&gt;Not just for &amp;ldquo;Materials Scientists&amp;rdquo;!&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 03 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/unstructured-data-are-bad-for-everybody/</guid></item><item><title>Nanoblomo 2021 #02 - Fun with VQGAN+CLIP</title><link>https://rjp.is/blogging/posts/nanoblomo-2021-02/</link><description>In which we stylise some Moomins.</description><author>infrequent oscillations</author><pubDate>Wed, 03 Nov 2021 00:09:36 GMT</pubDate><guid isPermaLink="true">https://rjp.is/blogging/posts/nanoblomo-2021-02/</guid></item><item><title>Note 59</title><link>https://qubyte.codes/notes/1635875090652</link><description>&lt;p&gt;A caterpillar.&lt;/p&gt;

    &lt;img alt="A yellow and black caterpillar perched behind some purple flowers." src="https://qubyte.codes/images/1635875054653.jpeg" /&gt;</description><author>Qubyte Codes</author><pubDate>Tue, 02 Nov 2021 19:44:50 GMT</pubDate><guid isPermaLink="true">https://qubyte.codes/notes/1635875090652</guid></item><item><title>The Bullet Journal Method by Ryder Carroll</title><link>http://evantravers.com/articles/2021/11/02/the-bullet-journal-method-by-ryder-carroll/</link><description>&lt;p&gt;&lt;img alt="" src="https://images.evantravers.com/articles/2021/11/bullet-journal-cover.jpg" /&gt;&lt;/p&gt;&lt;p&gt;It feels weird to be finally reading this years after I've been &amp;quot;doing&amp;quot; the bullet journal method. My first bullet journal is dated from 2016, so I guess five years?! It's been a very important part of my productivity/focus journey, but it means that I have a very strange lens with which to approach this book.&lt;/p&gt;
&lt;p&gt;Firstly: this is a &lt;em&gt;very&lt;/em&gt; handsome book. It's a love letter to journaling and paper. Reads great in the hand and lovely on the shelf.&lt;/p&gt;
&lt;p&gt;It's typeset nicely, and it's fun how it uses the physical medium of the book to reinforce how the system works. The page explaining the different Collections is itself a Collection!&lt;/p&gt;
&lt;p&gt;&lt;img alt="illustrations from the bullet journal book" src="https://images.evantravers.com/articles/2021/11/collections.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The contraints and delights of paper itself is a major theme of the book and the system. Ryder reminds the reader that the act of handwriting has recall and retention powers. The slowness of handwriting vs. typing forces the note-taker to employ advanced synthesization and summarization during capture. Notes taken by hand tend to be more concise and readable. This is an important part of my &lt;a href="http://evantravers.com/articles/tags/creativity-systems/"&gt;Creativity System&lt;/a&gt;, and I'm inspired to spend more time &amp;quot;thinking on paper.&amp;quot;&lt;/p&gt;
&lt;p&gt;The physical restrictions of paper force you to adopt intentional practices of planning and review. You can't copy/paste or search, so you have to be thoughtful about what you take with you and where you put it. I'm begin to think that &lt;em&gt;The Bullet Journal Method&lt;/em&gt; is probably the best start for anyone who is struggling with the practices required to build a disciplined/organized life. I'm pretty sure that the intentional practice of Rapid Logging and Migration were the first things that let me &lt;a href="http://evantravers.com/articles/2019/10/09/sisyphus-and-the-focus-boulder/"&gt;defeat the Focus Boulder&lt;/a&gt;. As much as I love Obsidian and Things and other digital tools, it's the rituals and practices that Migration taught me that keep the Evan-machine going.&lt;/p&gt;
&lt;p&gt;Another huge benefit of the Bullet Journal practice is the question &amp;quot;what just happened?&amp;quot; By externalizing our thoughts into words, we are able to separate our identity from the thoughts and feelings we are experiencing. This helps us be more objective and take every thought captive. To externalize your experiences and impulses creates this space between input and reaction that Ryder beautifully illustrates here:&lt;/p&gt;
&lt;p&gt;&lt;img alt="reflection" src="https://images.evantravers.com/articles/2021/11/reflection.png" /&gt;&lt;/p&gt;
&lt;p&gt;By journaling we become witnesses to our own story from the outside, an invaluable practice for learning impulse management and fostering gratefulness.&lt;/p&gt;
&lt;p&gt;Section 3, &amp;quot;The Practice,&amp;quot; is a succinct (if overly zen/stoic) summary of basic healthy productivity practices. There is certainly some Self-Centered Self Help&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-selfhelp" id="fnref-selfhelp"&gt;1&lt;/a&gt;&lt;/sup&gt; problems in there, but it's manageable.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#action-items" id="action-items"&gt;&lt;/a&gt;Action Items&lt;/h2&gt;
&lt;p&gt;While I like &lt;a href="https://twitter.com/mattragland"&gt;Matt Ragland&lt;/a&gt;'s split journal method, I think that I'm going to have &lt;em&gt;all&lt;/em&gt; collections on the &amp;quot;organized&amp;quot; side, and only scrawl on the unorganized side. Before, I was only putting Daily/Weekly collections on the &amp;quot;organized&amp;quot; side of the journal, and everything else was on the &amp;quot;notes&amp;quot; side… but I have realized that I want &lt;em&gt;everything&lt;/em&gt; with a title on the organization side. If I write something in the random side worth linking to, it's worth restating in the  appropriate Bullet Journal collection.&lt;/p&gt;
&lt;p&gt;I'll also take his advice to start a new journal every year instead of limping the journal along past the new year. I'm excited to get back into it.&lt;/p&gt;
&lt;p&gt;I think that next year I want to recommit to living in the Bullet Journal first. Thanks to Things and fun Drafts automations, I find myself dictating to my phone more than writing in my notebook. I live at a keyboard, so digital is closer at hand. I am convinced that by Rapid Logging more in the notebook first, I'll be practicing a little more reflection and presence that will help me be grateful and stay focused on what matters. Choosing to restate some of my tasks in my daily log will help me avoid over-committing to tasks by looking at the never-ending digital backlog, which Ryder says better:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Set intentions, not expectations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Great read, even if you have no intention of using your journal as a Bullet Journal.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-selfhelp"&gt;
&lt;p&gt;Self-help books generally have a core of self centeredness. Mentally replacing the &amp;quot;you are worth it&amp;quot; and &amp;quot;pursue your dreams&amp;quot; narratives with &amp;quot;He is worthy&amp;quot; and &amp;quot;I am a steward of talents&amp;quot; redeems this for a believer. &lt;a class="footnote-backref" href="#fnref-selfhelp"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Tue, 02 Nov 2021 12:48:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/11/02/the-bullet-journal-method-by-ryder-carroll/</guid></item><item><title>Debezium and Friends – Conference Talks 2021</title><link>https://www.morling.dev/blog/debezium-talks-2021/</link><description>Table of Contents Don’t Fear Outdated Caches – Change Data Capture to the Rescue! Change Data Streaming Patterns in Distributed Systems Analyzing Real-time Order Deliveries using CDC with Debezium and Pinot Dissecting our Legacy: The Strangler Fig Pattern with Apache Kafka, Debezium and MongoDB Bonus: Debezium at the Trino Community Broadcast Learning More If you love to attend conferences around the world without actually leaving the comfort of your house, 2021 certainly was (and is!</description><author>Gunnar Morling</author><pubDate>Tue, 02 Nov 2021 11:50:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/debezium-talks-2021/</guid></item><item><title>A Polemic Against Internet Arguments</title><link>https://www.marginalia.nu/log/34-internet-arguments/</link><description>&lt;p&gt;I want you to consider for a moment all the human lifetime wasted in ideological stalemates on the Internet, all that energy, all that anger and frustration. Imagine if you take even a fraction of that time, and put it to creating something constructive instead, learning skills, doing anything meaningful.&lt;/p&gt;
&lt;p&gt;It boggles the mind, doesn&amp;rsquo;t it? It must amount to entire human lifetimes every week.&lt;/p&gt;
&lt;p&gt;Ideology, or in a wider sense, ethics, is all about what should be done. How we should live. These aren&amp;rsquo;t statements about the world, but opinions about what the world should look like. They aren&amp;rsquo;t true or false, and any argument against them always boils down to &amp;ldquo;I disagree!&amp;rdquo;. Arguing about ethical systems is some of the least constructive things a human can do. It is more pointless than masturbation, which at least feels good for a moment.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Tue, 02 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/34-internet-arguments/</guid></item><item><title>Reclaim space on disk</title><link>https://dystroy.org/blog/reclaim-space-on-disk/</link><description>&lt;p&gt;As my disks tend to be quite messy with time, I had to devise ways to reclaim space, which means both spotting big directories and files, and to deal with the thousands of duplicates you can't delete one per one.&lt;/p&gt;
&lt;p&gt;Here's my toolbox for cleaning my disks on linux in 3 major steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dystroy.org/blog/reclaim-space-on-disk/#dysk"&gt;Confirm the problem with dysk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dystroy.org/blog/reclaim-space-on-disk/#broot"&gt;Spot and delete big  files and directories with broot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dystroy.org/blog/reclaim-space-on-disk/#backdown"&gt;Delete thousands of duplicates with backdown&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description><author>dystroy|Canop / blog</author><pubDate>Tue, 02 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://dystroy.org/blog/reclaim-space-on-disk/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #44</title><link>https://justingarrison.com/123dev/2021-11-02-123dev-44/</link><description>The newsletter passed 250 subscribers and 200 Twitter followers this week and I appreciate you all. ❤️ I love reading your replies and seeing you all share issues too. If you have suggestions for future topics feel free to hit reply and send them my way.</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 02 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-11-02-123dev-44/</guid></item><item><title>It’s okay to not be okay</title><link>https://avikdas.com/2021/11/02/its-okay-to-not-be-okay.html</link><description>What I’m about to say applies to everybody, but with Movember and my own experience as a man in mind, I hope my words will at least be useful to the men who read this. It’s okay to not be okay.</description><author>Avik Das</author><pubDate>Tue, 02 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://avikdas.com/2021/11/02/its-okay-to-not-be-okay.html</guid></item><item><title>Never trash READABLE data!</title><link>https://stop.zona-m.net/2021/11/never-trash-readable-data/</link><description>&lt;p&gt;NEVER. I mean it.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 02 Nov 2021 01:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/11/never-trash-readable-data/</guid></item><item><title>A ‘git up’ alias that works for any default branch</title><link>https://duncanlock.net/blog/2021/11/01/git-up-alias-that-works-for-any-default-branch/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;Traditionally, git&amp;#8217;s default branch was called &lt;code&gt;master&lt;/code&gt; - but this isn&amp;#8217;t always true, it&amp;#8217;s just a default - and one that&amp;#8217;s currently undergoing change. GitHub &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; GitLab have changed their defaults to &lt;code&gt;main&lt;/code&gt; and the git project itself is in the process of doing the&amp;nbsp;same.&lt;/p&gt;
&lt;p&gt;I have a longstanding git alias - &lt;code&gt;git up&lt;/code&gt; - that I use to pull my local checkout up to date with the remote. It&amp;#8217;s a shorthand for the&amp;nbsp;following:&lt;/p&gt;
&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;Change branch to the default&amp;nbsp;branch&lt;/li&gt;&lt;li&gt;Fetch all changes from the remote, removing any local branches that have been deleted on the&amp;nbsp;remote&lt;/li&gt;&lt;li&gt;Pull any changes from the default remote&amp;nbsp;branch&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;p&gt;This used to hardcode &lt;code&gt;master&lt;/code&gt; as the default branch, but this was getting less and less reliable, so I figured out a better version that checks the repo&amp;#8217;s default branch and uses&amp;nbsp;that:&lt;/p&gt;
&lt;figure class="listing-block"&gt;&lt;figcaption&gt;Add these lines to your ~/.gitconfig file&lt;/figcaption&gt;
&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  head-branch &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="s2"&gt;"git remote show origin | grep 'HEAD branch' | cut -d' ' -f5"&lt;/span&gt;
  up &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="s2"&gt;"git switch &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git head-branch&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; git fetch --all --prune --progress &amp;amp;&amp;amp; git pull"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a class="bare" href="https://stackoverflow.com/questions/28666357/git-how-to-get-default-branch"&gt;https://stackoverflow.com/questions/28666357/git-how-to-get-default-branch&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="bare" href="https://about.gitlab.com/blog/2021/03/10/new-git-default-branch-name/"&gt;https://about.gitlab.com/blog/2021/03/10/new-git-default-branch-name/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 02 Nov 2021 01:46:11 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/11/01/git-up-alias-that-works-for-any-default-branch/</guid></item><item><title>Visualize dependencies of structs in Go</title><link>https://github.com/davidschlachter/embedded-struct-visualizer</link><description>Tool to help understand relationships between custom struct types in large Go projects.</description><author>David Schlachter</author><pubDate>Tue, 02 Nov 2021 00:05:00 GMT</pubDate><guid isPermaLink="true">https://github.com/davidschlachter/embedded-struct-visualizer</guid></item><item><title>Nanoblomo 2021 #01 - A new PC</title><link>https://rjp.is/blogging/posts/nanoblomo-2021-01/</link><description>In which we install a new PC.</description><author>infrequent oscillations</author><pubDate>Mon, 01 Nov 2021 23:22:36 GMT</pubDate><guid isPermaLink="true">https://rjp.is/blogging/posts/nanoblomo-2021-01/</guid></item><item><title>What to Expect When You're Emailing</title><link>https://www.brightball.com/articles/what-to-expect-when-youre-emailing</link><description>During Carolina Code Conf, I gave what is probably the most entertaining presentation ever given about email. We walk through how to protect, raise and nuture the domain for my pirate themed gym, Slimmer Ye Timbers.</description><author>Brightball Articles</author><pubDate>Mon, 01 Nov 2021 18:37:21 GMT</pubDate><guid isPermaLink="true">https://www.brightball.com/articles/what-to-expect-when-youre-emailing</guid></item><item><title>Video: Developing a Layered Email Security Strategy Webinar</title><link>https://www.brightball.com/articles/developing-a-layered-email-security-strategy-webinar</link><description>In this joint webinar between dmarcian, GreatHorn and Inspired eLearning I had the opportunity to share an overview of SPF, DKIM and DMARC as part of a layered email security strategy.</description><author>Brightball Articles</author><pubDate>Mon, 01 Nov 2021 18:21:27 GMT</pubDate><guid isPermaLink="true">https://www.brightball.com/articles/developing-a-layered-email-security-strategy-webinar</guid></item><item><title>The manager-elevator</title><link>https://world.hey.com/joaoqalves/the-manager-elevator-0f4bf40e</link><description>&lt;div class="trix-content"&gt;
  &lt;div&gt;I first read about "servant leadership" in a job offer when I joined &lt;em&gt;mytaxi&lt;/em&gt;&amp;nbsp; (&lt;a href="https://free-now.com/uk/"&gt;FREE NOW&lt;/a&gt;) back in 2018. They were looking for a "servant leader." I bit the bullet and changed my career from software development to engineering management.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Before joining the company, I searched for books that more experienced folks in the industry recommended. I ended up buying "&lt;a href="https://davidmarquet.com/turn-the-ship-around-book/"&gt;Turn the ship around&lt;/a&gt;" and "&lt;a href="https://en.wikipedia.org/wiki/Maverick_(book)"&gt;Maverick!&lt;/a&gt;". These are two masterpieces of enlightenment when it comes to driving teams and organizations. The two most relevant concepts I captured were:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Lead by example&lt;/strong&gt;. One cannot change the culture through elaborated pitches and preaching how &lt;em&gt;best practices™&lt;/em&gt; will improve your team members' lives.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Create leaders, not followers&lt;/strong&gt;.&lt;strong&gt; &lt;/strong&gt;Some charismatic people tend to think its job is about deciding things or, worse, &lt;em&gt;giving orders&lt;/em&gt;. Empowering people to make decisions themselves and creating other leaders unlocks organizational potential. Hence it's more durable.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;They felt natural to me because that's how I always have seen myself. Someone that pushes other people to try new approaches and guides them through these changes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;h1&gt;Glorified coaching&lt;/h1&gt;&lt;div&gt;Lately, I've been hearing a lot about "servant leadership." Usually, with a negative connotation. When digging into the root causes of this mock, I've found patterns.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sometimes, people confuse it with &lt;em&gt;cheerleading&lt;/em&gt;. It's great to bring your team coffee or ask for food delivery because they're solving a significant problem. But wouldn't it be better if you did that while communicating to other teams, managing blockers, or helping answer less-important inquires on Slack? I admit cheerleading is essential as a leader, but it's not all.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I've also seen the &lt;em&gt;laissez-faire&lt;/em&gt; style. These folks are utterly reactive to what their team does, and their best quality is to say, "If you need help, I'm here." That's not how it should work. If you see someone drowning, you don't wait for them to ask for help! Likewise, if you see your team getting stuck or something odd, taking immediate action — it may be as simple as listening to someone's complaints — is crucial to create natural trust in leaders.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Finally, the "&lt;em&gt;I don't know about the details&lt;/em&gt;" type. I get it. It's challenging to be on top of everything. If you're managing a complex domain or scope, it may be daunting. However, these details give you superpowers. They allow you to have more productive conversations with your peers, directors, other folks in the industry. They allow you to understand what is exactly happening when another person approaches you for escalation.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While this post is not a &lt;em&gt;rant&lt;/em&gt; against agile coaches, you'll likely become a glorified coach rather than an engineering leader if you tick most of the above boxes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h1&gt;Engineering Leadership starts with Engineering&lt;/h1&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I won't be the person that says that an engineering leader keeps coding. Sometimes it's desirable; sometimes, it's not. It also heavily depends on how many other things you're doing. Nevertheless, it would help if you kept your hax sharpened to lead any discipline effectively. Reading technical blogs, whitepapers, books, reviewing design documents, or doing code reviews may help too.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Going to the trenches and getting your hands dirty gives you a clearer perspective on how things work in practice &lt;em&gt;versus&lt;/em&gt; how they're supposed. You start seeing more trade-offs. You can challenge the status-quo and make your teams reflect better.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Engineering leaders are aware of where the industry is going. They aren't afraid of sponsoring strategic decisions that might affect one or more teams. These decisions go from migrating from a technology that's becoming a liability — e.g., Adobe Flash — to try a different approach to DevOps — e.g., Platforms as products —, and so on. That's engineering leaders' scope. They help businesses deliver value through creating and shaping organizations. If they run away from it or let teams and systems rot, it's their responsibility.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If we take off engineering from engineering leadership, what's left?&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h1&gt;From strategy to tactics and back&lt;/h1&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I love to listen to a good story. I love to feel inspired by leaders that shape an industry or the world. The other day, I was amazed by how &lt;a href="https://en.wikipedia.org/wiki/Ferran_Adri%C3%A0"&gt;Ferran Adriá&lt;/a&gt; explained innovation and how he was a forward thinker more than 30 years ago. But it worked way better because Ferran was passionate about cooking. He was a cook himself.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Being strategic and visionary is a great way to drive and motivate your teams. At the same time, if you don't take the time to be on the ground and support your team members, you'll likely miss crucial details to support your strategy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That's why I believe the best leaders are in the &lt;em&gt;manager-elevator&lt;/em&gt; spectrum. Someone that's able to be in a tactical meeting — even if it's to listen — and one hour later is writing the strategy of the whole department or presenting it to the CTO.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let's press the button and ride this elevator up and down.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;— João&lt;/div&gt;
&lt;/div&gt;</description><author>João Alves</author><pubDate>Mon, 01 Nov 2021 14:57:11 GMT</pubDate><guid isPermaLink="true">https://world.hey.com/joaoqalves/the-manager-elevator-0f4bf40e</guid></item><item><title>Review of Macbook Pro 14 (M1 Max, 32GPU Cores, 64GB Memory) - Performance, Windows, Parallels and more</title><link>https://www.outcoldman.com/blog/2021/11/01/mbp-14-2021-review/</link><description>&lt;p&gt;I have been using Macbook Pro with M1 since the release of the MBP 13&amp;quot; M1 in 2020. And I am huge fan of the new Apple CPU - M1.
Time to time I need to do some work on my 2019 16&amp;quot; MBP with Intel Processor (all maxed out), so I keep experience the difference between
using those two.&lt;/p&gt;
&lt;p&gt;On MBP with M1 I can definitely see that the macOS works way more smooth. No more glitches or frozen screens for a few
seconds, less heat, less time I hear the fans work. And a lot of work you can do on just single battery charge.&lt;/p&gt;</description><author>outcoldman</author><pubDate>Mon, 01 Nov 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://www.outcoldman.com/blog/2021/11/01/mbp-14-2021-review/</guid></item><item><title>Which games to change?</title><link>http://dimitarsimeonov.com/2021/10/31/which-games-to-change</link><description>&lt;blockquote&gt;
  &lt;p&gt;Show me a gambler and I’ll show you a loser, show me a hero, and I’ll show you a corpse.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Mario Puzo, author of The Godfather
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Last week we began the “&lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;How to be a game changer&lt;/a&gt;” series. Today, we’ll start answering the question - “Which games to change”, by defining the two main types of games we don’t want to play - zero-sum games and win-lose games.&lt;/p&gt;

&lt;p&gt;&lt;img alt="which games to change" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/02_win_win.png" width="100%" /&gt;&lt;/p&gt;

&lt;h3 id="zero-sum-games"&gt;Zero sum games&lt;/h3&gt;

&lt;p&gt;One way to simplify looking at games, is to look at the possible outcomes. There is a whole branch of Economics called Game Theory which goes into detail about categorizing and analyzing games based on these outcomes. In game theory, a game is simply an interaction between multiple parties, where they can choose a behavior, and after the interaction any participant in a game may end up winning something or losing something else, resulting in a combination of wins and losses. Game Theory often simplifies that to a simple number representing how much value, or utility, or money you could win or lose depending on the outcome, and while it is a limiting way to look at it, it is still a helpful way get started.&lt;/p&gt;

&lt;p&gt;One of the most obvious games which I assert &lt;em&gt;everyone&lt;/em&gt; should try to change are the so called &lt;strong&gt;“zero-sum” games&lt;/strong&gt;. Here zero-sum refers to the sum of outcomes of all players after the game, compared to the situation before the game. These are the games where as much as some players win, the other players collectively lose. For example if you and I bet $5 on the result of a coin toss, one of us will have slightly more money, but together our overall money has changed by zero. In zero-sum games the total amount of value and utility across all participants does not increase as a result of the game. Some people individually may be better off, but the group as a whole is not better off.&lt;/p&gt;

&lt;p&gt;If we go back to our traffic example from the previous post, driving on a road with congestion, which has two lanes merging together into a single lane (for example, due to construction) may be thought of as a zero sum game, where only certain number of vehicles can go through a bottleneck at a time. If one car goes through, no other car is going through. There is a limit of total utility, is proportional to the maximum safe and allowed speed with which cars merge in. Often, cars will alternate from each lane as a way to coordinate, but some drivers might feel competitive, or in a hurry, and try to cut in front of others. And they may succeed, and save seconds for themselves. But by disrupting the natural rhythm, they create confusion, congestion, and waste time for everyone behind them. Other drivers may have to hit the breaks, or press their horn and reach out for verbal insults. It’s a pity. More cars will get piled further behind. The driver who cut might think they’re winning this time, but they probably suffered a time loss behind someone else earlier who cut as well, but they only observed it indirectly.&lt;/p&gt;

&lt;p&gt;Furthermore, with zero-sum games we’re only creating more conflict among ourselves. In my opinion, all zero-sum games negatively affect the participants as a whole. Therefore they are in practice even worse, they are “negative-sum games” and the community as a whole is worse off after playing them.&lt;/p&gt;

&lt;p&gt;There’s no good reason to play these games.&lt;/p&gt;

&lt;h3 id="win-lose-games"&gt;Win-lose games&lt;/h3&gt;

&lt;p&gt;Win-lose games are similar to zero-sum games as in some people will benefit, and others will lose as an outcome of the game, but they differ as in the win may sometimes seem to be larger than the loss. But in practice, if a game ends up generating any positive value in the process, it is likely that this value is redistributed, and nobody is losing and the game becomes win-win?&lt;/p&gt;

&lt;p&gt;As a result, a win-lose game in real life will usually be a negative-sum game, as some more savvy players may figure out how to “rig” the game, and exploit others. The possibility to lose something from the game is a big red flag ⛳️. If you encounter any interaction where you may lose something - think why the interaction is that way. Is it rigged to transfer value out of you to others?&lt;/p&gt;

&lt;p&gt;Some win-lose games with uncertain outcomes are more tempting to play than games which obviously are zero-sum as we hope that we may win, and we may win big. For example, if we participate in a pyramid scheme, we will likely come up ahead as long as we’re not the last ones. If we knowingly participate in a such a scheme - shame on us. But even with good intentions, we often can get tricked into participating into pyramid-like scheme, where at the end the “biggest fools” will lose something. Some pyramids can have a really elaborate disguise, and subtle giveaways.&lt;/p&gt;

&lt;p&gt;But one giveaway which we can always use to detect games we don’t want to play is the ability for anyone in the game to “lose” in some way. &lt;em&gt;Games which genuinely create value tend to have a way to redistribute this value&lt;/em&gt; over time, if people play them again and again, and they also incentivize people to keep creating value. And that presents another way to tell these games apart - if a game is played long term, repeatedly, it is more likely to be genuinely creating value for all participants, even if that value gets distributed unevenly. There are some exceptions such as the “scapegoat” game, which we’ll discuss in a future post.&lt;/p&gt;

&lt;p&gt;By opting out of short term games, or win-lose games, we will not only do better for ourselves, we will make it less likely others get exploited.&lt;/p&gt;

&lt;h3 id="next-up"&gt;Next up&lt;/h3&gt;

&lt;p&gt;Next week we’ll start discussing the tools we have available to change these games. In the meantime, don’t believe anything I wrote above, and ask yourself - “When anyone want to play a zero-sum or win-lose game?”. You may also ask yourself, “Which of my interactions are win-lose?”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Mon, 01 Nov 2021 01:11:56 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/10/31/which-games-to-change</guid></item><item><title>Updated Rss Feed</title><link>https://ztoz.blog/posts/updated-rss-feed/</link><description>&lt;p&gt;I&amp;rsquo;ve updated the RSS feed to include the full content of the articles rather than just the summary.&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Sun, 31 Oct 2021 21:47:17 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/updated-rss-feed/</guid></item><item><title>Controlling Elgato Key Light with Hammerspoon</title><link>http://evantravers.com/articles/2021/10/31/controlling-elgato-key-light-with-hammerspoon/</link><description>&lt;p&gt;Here's a quick automation that I've been hacking idly on for months: having my Elgato Key Air Light be automatically controlled from my Mac.&lt;/p&gt;
&lt;p&gt;While perusing hammerspoon's API, I noticed that it has a &lt;a href="https://www.hammerspoon.org/docs/hs.bonjour.html"&gt;Bonjour API&lt;/a&gt; and wound up writing this snippet:&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-lua" tabindex="0"&gt;&lt;div class="line"&gt;&lt;span&gt;hs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bonjour&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;networkServices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;hs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;inspect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;end&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I ran this in the Hammerspoon console, I saw a list of findable services including the Elgato light... so it was only a matter of time before I found the right commands. Thanks to &lt;a href="https://www.kumariyer.com/post/controlling-elgato/"&gt;this blog post&lt;/a&gt; I found &lt;a href="https://github.com/NickParks/elgato-light-api"&gt;some working TypeScript&lt;/a&gt; that I was eventually able to adapt after I had grokked more of Hammerspoon's Bonjour implementation.&lt;/p&gt;
&lt;p&gt;(It turns out that the Elgato Key runs an unsecured web server that takes HTTP requests in a very simple fashion. It's kind of refreshing. Most of the code just finds the IP and port of the light, the rest sends the HTTP requests with a different JSON payload for on or off.)&lt;/p&gt;
&lt;p&gt;I use this on and off switch when I &lt;a href="https://github.com/evantravers/hammerspoon-config/blob/19c3e9a6b5536c6db85b14de57374fb07c0c188d/spaces/collaboration.lua#L17-L26"&gt;enter and leave&lt;/a&gt; my meeting Headspace.&lt;/p&gt;
&lt;p&gt;I quickly realized that my quick and dirty hack would turn on and off all lights on the same network with your laptop... so I decided to restrict the code to only when I'm plugged into my trusty CalDigit TS3+ dock:&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-lua" tabindex="0"&gt;&lt;div class="line"&gt;&lt;span&gt;IsDocked&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;  &lt;span&gt;return&lt;/span&gt; &lt;span&gt;hs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fnutils&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;some&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;hs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;usb&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;attachedDevices&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;function&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;device&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;    &lt;span&gt;return&lt;/span&gt; &lt;span&gt;device&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;productName&lt;/span&gt; &lt;span&gt;==&lt;/span&gt; &lt;span&gt;&amp;quot;CalDigit Thunderbolt 3 Audio&amp;quot;&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;  &lt;span&gt;end&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That function returns &lt;code&gt;true&lt;/code&gt; if my laptop is docked. In my meeting space automation I use something like:&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-lua" tabindex="0"&gt;&lt;div class="line"&gt;&lt;span&gt;if&lt;/span&gt; &lt;span&gt;IsDocked&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt; &lt;span&gt;then&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;  &lt;span&gt;spoon&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ElgatoKey&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I pulled my naive implementation into a &lt;a href="https://github.com/evantravers/hammerspoon-config/blob/8b24d94bf60c5828259dfc858fa4e4dc777677b7/Spoons/ElgatoKey.spoon/init.lua"&gt;baby spoon library&lt;/a&gt;, so you can hack on it if you want.&lt;/p&gt;</description><author>trv.rs</author><pubDate>Sun, 31 Oct 2021 19:03:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/10/31/controlling-elgato-key-light-with-hammerspoon/</guid></item><item><title>Blog posts for founders (and early employees)</title><link>https://blog.separateconcerns.com/2021-10-31-blog-posts-founders.html</link><description>&lt;p&gt;Over the years, I have accumulated links to blog posts I intend to re-read every time find myself in a position to influence the early stage of a company.&lt;/p&gt;
&lt;p&gt;I thought it might be helpful to someone if I published part of that list here. I will avoid links which are too technical and focus on links that can be interest non-technical founders as well.&lt;/p&gt;
&lt;p&gt;Note that I haven’t included books. There are many good books for founders, but if you must read only one make it &lt;a href="https://www.amazon.com/Four-Steps-Epiphany-Steve-Blank/dp/0989200507"&gt;The Four Steps to the Epiphany&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If this isn’t enough for you, you’re always welcome to explore &lt;a href="https://pinboard.in/u:catwell/"&gt;my Pinboard account&lt;/a&gt;.&lt;/p&gt;
&lt;section id="My-own-posts"&gt;
&lt;h2&gt;My own posts&lt;/h2&gt;
&lt;p&gt;Let’s get started by mentioning posts from this blog which transcribe the opinions of two influential people, which I share (on those topics).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.separateconcerns.com/2013-03-24-vogels-skills.html"&gt;Werner Vogels on Skills&lt;/a&gt;, where the CTO of Amazon.com describes the qualities he is looking for in leaders at Amazon, in particular Ownership and the willingness to get their hands dirty doing operations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.separateconcerns.com/2014-05-28-hacker-founders.html"&gt;Hacker Founders&lt;/a&gt;, where Paul Graham, founder of Y Combinator, explains how programmers have an edge as company founders, including as CEOs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="Titles-are-Toxic"&gt;
&lt;h2&gt;&lt;a href="https://randsinrepose.com/archives/titles-are-toxic/"&gt;Titles are Toxic&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Michael Lopp (Rands) on issues with giving people titles. Personally I have never liked titles, which can be a source of conflicts and bad decisions. I have also often held titles that didn’t really correspond to the job I did - which was almost always multi-faceted. Yet, titles are a proxy used by people who don’t know you to understand what you do at work.&lt;/p&gt;
&lt;p&gt;Like Rands I don’t have a good solution to that. I write &lt;a href="https://blog.separateconcerns.com/2013-06-20-three-years-proprietary-projects.html"&gt;some&lt;/a&gt; &lt;a href="https://blog.separateconcerns.com/2018-01-06-four-years.html"&gt;blog&lt;/a&gt; &lt;a href="https://blog.separateconcerns.com/2021-05-01-inch.html"&gt;posts&lt;/a&gt; explaining what I do in more details that a title ever could, for people who care.&lt;/p&gt;
&lt;p&gt;In any case I always suggest avoiding giving some titles to people too early, including to founders: having a “technical co-founder” instead of a CTO will make things easier if you grow fast and need to hire someone later on.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Creating-a-Software-Company-9-Decisions-You-Have-to-Make"&gt;
&lt;h2&gt;&lt;a href="https://spin.atomicobject.com/2014/06/02/software-company-decisions/"&gt;Creating a Software Company? 9 Decisions You Have to Make&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A list of questions to ask yourself when founding a software company, by Michael Marsiglia, CEO of Atomic Object. It can also be a good idea to ask them before joining one.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="The-40%-rule"&gt;
&lt;h2&gt;&lt;a href="https://avc.com/2015/02/the-40-rule/"&gt;The 40% rule&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A simple but powerful rule of thumb for SaaS companies by Brad Feld, relayed by Fred Wilson:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;annual revenue growth rate + operating margin should equal 40%&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="Why-it’s-nice-to-compete-against-a-large-profitable-company"&gt;
&lt;h2&gt;&lt;a href="https://blog.asmartbear.com/compete-on-profit.html"&gt;Why it’s nice to compete against a large, profitable company&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Jason Cohen explains how a startup can disrupt a large competitor by making a part of their business unprofitable and forcing them to abandon it. It’s not as simple as this summary makes it sound, and it’s not the Innovator’s Dilemma. Read the article :)&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Product-design-at-GitHub"&gt;
&lt;h2&gt;&lt;a href="https://warpspire.com/posts/product-design"&gt;Product design at GitHub&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kyle Neath on how, in the early days of GitHub, everyone was a “product designer”. Many successful SaaS products started with the same concept:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We only hire smart people we trust to make our product better. We don’t have managers dictating what to work on. We don’t require executive signoff to ship features. Executives, system administrators, developers, and designers concieve, ship, and remove features alike.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Actually, I think I have never seen a company ship good product fast without doing some version of this.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Joel-Spolsky's-Strategy-Letters"&gt;
&lt;h2&gt;Joel Spolsky’s Strategy Letters&lt;/h2&gt;
&lt;p&gt;If you don’t know that series of blog posts, you are missing something. They are super insightful essays by a multi-successful founder about building companies.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.joelonsoftware.com/2000/05/12/strategy-letter-i-ben-and-jerrys-vs-amazon/"&gt;Ben and Jerry’s vs. Amazon&lt;/a&gt; - How to decide between slow profitable growth or fast VC-funded growth.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.joelonsoftware.com/2000/05/24/strategy-letter-ii-chicken-and-egg-problems/"&gt;Chicken and Egg Problems&lt;/a&gt; - Understanding the Chicken and Egg problem and some ways to solve it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.joelonsoftware.com/2000/06/03/strategy-letter-iii-let-me-go-back/"&gt;Let Me Go Back!&lt;/a&gt; - On barriers to entry, and how letting customers understand they can easily move &lt;strong&gt;out&lt;/strong&gt; of your product (maybe back to what they used before) is important.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.joelonsoftware.com/2001/03/23/strategy-letter-iv-bloatware-and-the-8020-myth/"&gt;Bloatware and the 80/20 Myth&lt;/a&gt; - 80% of software users use 20% of features, so you can win the market with just 20% of the features, right? No, because not everyone uses the &lt;strong&gt;same&lt;/strong&gt; 20%! I can’t state how often I meet founders who don’t understand this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.joelonsoftware.com/2002/06/12/strategy-letter-v/"&gt;Strategy Letter V&lt;/a&gt; - How companies have an advantage in commoditizing their complements, and how this can explain some investments in Open Source software.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(There is at least a &lt;a href="https://www.joelonsoftware.com/2007/09/18/strategy-letter-vi/"&gt;sixth strategy letter&lt;/a&gt;, but it is less interesting IMO.)&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Ev-William's-10-rules-for-Web-Startups"&gt;
&lt;h2&gt;Ev William’s 10 rules for Web Startups&lt;/h2&gt;
&lt;p&gt;This post used to be on Ev William’s blog which went offline some time ago, thankfully I &lt;a href="http://files.catwell.info/misc/mirror/evan-williams-10-rules-startups.txt"&gt;mirror articles that interest me that much&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Your-company's-most-valuable-resource"&gt;
&lt;h2&gt;&lt;a href="https://twitter.com/williampietri/status/732602463082188801"&gt;Your company’s most valuable resource&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Just a tweet, but an important one, by William Pietri:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your company’s most valuable resource is people giving a shit. Ask yourself: does your system encourage or discourage that?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="Wants-needs-and-chasm-crossing"&gt;
&lt;h2&gt;&lt;a href="https://apenwarr.ca/log/20211024"&gt;Wants, needs, and chasm-crossing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A recent post by Avery Pennarun, Tailscale CEO. It shows how the best strategy to acquire customers is often to solve a single &lt;strong&gt;Want&lt;/strong&gt; - something their current solution doesn’t provide - and then to resolve all their &lt;strong&gt;Needs&lt;/strong&gt; (blockers) so they can actually flock to your product instead of attempting to implement any more &lt;strong&gt;Wants&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Financial-modeling-for-startups"&gt;
&lt;h2&gt;&lt;a href="https://www.slideshare.net/rodrigo1971/financial-modeling-for-startups"&gt;Financial modeling for startups&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A slide deck by Rodrigo Sepulveda with a simple and effective approach to business models and PnLs for startups.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Startup-Therapy:-Ten-questions-to-ask-yourself-every-month"&gt;
&lt;h2&gt;&lt;a href="https://blog.asmartbear.com/startup-business-plan.html"&gt;Startup Therapy: Ten questions to ask yourself every month&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Jason Cohen (again) shares his alternative to business plans: a list of 10 questions to help you figure out where you are and where to go next.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="The-Small-Teams-Manifesto"&gt;
&lt;h2&gt;&lt;a href="http://files.catwell.info/misc/mirror/2015-08-17-codemanship-small-teams-manifesto.txt"&gt;The Small Teams Manifesto&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Jason Gorman says small teams win because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
the probability that a project fails increases rapidly with size;
&lt;/li&gt;
&lt;li&gt;
small teams interact more effectively with the customer and/or end users;
&lt;/li&gt;
&lt;li&gt;
small teams are easier to build (hiring good people is hard).
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="Innovating-with-purposeful-anger"&gt;
&lt;h2&gt;&lt;a href="https://www.icopilots.com/innovation-mindset/innovating-purposeful-anger-12965"&gt;Innovating with purposeful anger&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;One of the very interesting tools using anger as a driver to innovate is the “You Got Fired!” game.&lt;/p&gt;
&lt;p&gt;Get a bunch of managers in a business unit and role-play that their whole team is fired. Indicate also that as a cockup from legal their non-compete clause isn’t applicable.&lt;/p&gt;
&lt;p&gt;Then, give them an hour to build a competitor startup that will take revenge on their company on a “That’ll teach them!” mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="How-To-Decide-What-To-Build"&gt;
&lt;h2&gt;&lt;a href="http://files.catwell.info/misc/mirror/2019-04-dcgross-decide-build.txt"&gt;How To Decide What To Build&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Daniel Gross (a founder I have been following since his first YC startup, Greplin) on ways to decide what company to build on a personal level. I wish he had published that before my last attempt.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="Qu’est-ce-qu’un-projet-entrepreneurial-viable"&gt;
&lt;h2&gt;&lt;a href="https://philippesilberzahn.com/2011/10/03/projet-entrepreneurial-viable/"&gt;Qu’est-ce qu’un projet entrepreneurial viable?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A blog post in French by Philippe Silberzahn. A viable project stems from an individual acting on an idea and convincing people.&lt;/p&gt;
&lt;/section&gt;</description><author>Separate Concerns</author><pubDate>Sun, 31 Oct 2021 16:45:00 GMT</pubDate><guid isPermaLink="true">https://blog.separateconcerns.com/2021-10-31-blog-posts-founders.html</guid></item><item><title>Book Review: King and Emperor, A New Life of Charlemagne</title><link>https://nicolaiarocci.com/book-review-king-and-emperor-a-new-life-of-charlemagne/</link><description>&lt;p&gt;In this scholarly biography by Janet L. Nelson, Charlemagne is stripped back
from the years of mythologizing and idolizing that have occurred since his
death. He is presented as distinctly human, and this book is the first time
I have felt I could reasonably understand Charlemagne as the man he was, not
the man he has since been painted to be. Moreover, Nelson is excellent in her
discussions of Charlemagne&amp;rsquo;s wives and their roles. For example, the
commonplace assumption that Fastrada was a cruel person is questioned, and she
emerges as a capable companion and queen instead.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 31 Oct 2021 08:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-king-and-emperor-a-new-life-of-charlemagne/</guid></item><item><title>Italia III - Roma</title><link>https://faingezicht.com/photos/2021/10/31/roma/</link><description>As the history nerd that I am, I could explore Rome and its historical sites for years, and not get enough of it.

We tried to squeeze as much as we could into our couple of days there, and realized that we were surrounded by tourists everywhere we went. Next time, I'll make an effort to find at least a few places where the locals hang out, too.

_This is the third in a series shared throughout Fall 2021. When you're done here, make sure to also check out parts &lt;a href="/photos/2021/10/14/umbria/"&gt;one&lt;/a&gt;, &lt;a href="/photos/2021/10/19/florence/"&gt;two&lt;/a&gt;, &lt;a href="/photos/2021/11/12/positano/"&gt;four&lt;/a&gt;, and &lt;a href="/photos/2021/12/02/italia-revisited/"&gt;five&lt;/a&gt;._</description><author>Avy Faingezicht</author><pubDate>Sun, 31 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/10/31/roma/</guid></item><item><title>Perfecting GLFW for Zig, and finding lurking undefined behavior that went unnoticed for 6+ years</title><link>https://devlog.hexops.org/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/</link><description>&lt;p&gt;&lt;strong&gt;Today, I am announcing &lt;a href="https://github.com/hexops/mach-glfw"&gt;mach-glfw&lt;/a&gt;: Ziggified GLFW bindings with 100% API coverage, zero-fuss installation, cross compilation, and more.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="building-mach-for-everyone"&gt;Building Mach for everyone&lt;/h2&gt;
&lt;p&gt;If &lt;a href="https://github.com/hexops/mach"&gt;Mach engine&lt;/a&gt; only benefits people interested in using that engine, and not the broader Zig (and even gamedev) community I would consider that &lt;em&gt;a total failure&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Whether you&amp;rsquo;re interested in using all of Mach, just some of it with your own engine / project, or just the tools/ideas we develop in the future (with Unity, Unreal, etc.), &lt;em&gt;I truly aim to produce something that benefits you&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Mach is in super early stages, I&amp;rsquo;ve spent the last four months perfecting a Zig interface to GLFW, and making no-fuss installation and cross-compilation a reality. Today, you can benefit from that work too.&lt;/p&gt;
&lt;h2 id="building-glfw-for-every-platform"&gt;Building GLFW for every platform&lt;/h2&gt;
&lt;p&gt;Just &lt;code&gt;zig&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt;, that&amp;rsquo;s the idea. The GLFW C code is compiled with &lt;code&gt;zig&lt;/code&gt;, and the &lt;code&gt;build.zig&lt;/code&gt; file automatically uses &lt;code&gt;git&lt;/code&gt; to clone (a very minimal set of) system dependencies for you (X11 libraries, etc.)&lt;/p&gt;
&lt;p&gt;No installing apt packages. No dealing with missing header errors. It should just work out-of-the-box, and for every platform:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img2.png"&gt;&lt;img alt="Mach engine platform support, including Windows, Linux, Mac and cross-compilation between them with Android/iOS coming soon." class="color" src="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img2.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Today, this works for GLFW itself. Cross-compilation of &lt;em&gt;OpenGL and Vulkan apps&lt;/em&gt; is not yet fully functional. &lt;a href="https://github.com/hexops/mach/issues/59"&gt;We&amp;rsquo;re working on it, though.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="perfecting-glfw-for-zig"&gt;Perfecting GLFW for Zig&lt;/h2&gt;
&lt;p&gt;Aside from platform support, mach-glfw now has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;100% API coverage&lt;/strong&gt; of GLFW v3.3.4. Every function, type, constant, etc. has been wrapped in a ziggified API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;130+ tests&lt;/strong&gt;, with CI testing Linux, Windows, Mac (x86 and M1/ARM) and cross-compilation between them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might be asking: &lt;em&gt;why Zig bindings, when Zig can interface directly with C?&lt;/em&gt; Ziggified bindings to GLFW get us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Errors as &lt;a href="https://ziglang.org/documentation/master/#Errors"&gt;zig errors&lt;/a&gt; instead of via a callback function.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enums&lt;/strong&gt;: always know what value a GLFW function can accept as everything is strictly typed. And use the nice Zig syntax to access enums, like &lt;code&gt;window.getKey(.escape)&lt;/code&gt; instead of &lt;code&gt;c.glfwGetKey(window, c.GLFW_KEY_ESCAPE)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Slices instead of C pointers and lengths.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ziglang.org/documentation/master/#packed-struct"&gt;packed structs&lt;/a&gt; represent bit masks, so you can use &lt;code&gt;if (joystick.down and joystick.right)&lt;/code&gt; instead of &lt;code&gt;&amp;amp;&lt;/code&gt; &lt;code&gt;|&lt;/code&gt; etc. bitwise operators.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; instead of &lt;code&gt;c.GLFW_TRUE&lt;/code&gt; and &lt;code&gt;c.GLFW_FALSE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Generics: use &lt;code&gt;window.hint&lt;/code&gt; instead of &lt;code&gt;glfwWindowHint&lt;/code&gt;, &lt;code&gt;glfwWindowHintString&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Methods, e.g. &lt;code&gt;my_window.hint(...)&lt;/code&gt; instead of &lt;code&gt;glfwWindowHint(my_window, ...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="explicit-error-handling-solves-a-real-problem"&gt;Explicit error handling solves a real problem&lt;/h2&gt;
&lt;p&gt;GLFW traditionally passes errors to the user via a callback. This can make errors easy to ignore, as well as difficult to correlate and handle effectively at the time of the function invocation.&lt;/p&gt;
&lt;p&gt;We translated a &lt;a href="https://github.com/hexops/mach-glfw-vulkan-example"&gt;a Vulkan example to mach-glfw&lt;/a&gt;, which you can try for yourself today:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img1.png"&gt;&lt;img alt="mach-glfw and vulkan-zig libraries working together to produce a triangle." class="color" src="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img1.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After porting it, we found that the example was crashing with a &lt;code&gt;NoWindowContext&lt;/code&gt; error. Strange?&lt;/p&gt;
&lt;p&gt;As it turns out, we had found &lt;a href="https://github.com/Snektron/vulkan-zig/pull/21"&gt;a small bug in the vulkan-zig example code&lt;/a&gt;, it was calling &lt;code&gt;glfwSwapBuffers&lt;/code&gt; which is not needed for Vulkan. The error went unnoticed because it&amp;rsquo;s easy to miss errors with GLFW&amp;rsquo;s error callback handling style. But with mach-glfw, it was an explicit error you have to handle e.g. via &lt;code&gt;try glfw.swapBuffers()&lt;/code&gt; - we literally couldn&amp;rsquo;t miss it.&lt;/p&gt;
&lt;h2 id="finding-lurking-undefined-behavior-in-6-year-old-glfw-code"&gt;Finding lurking undefined behavior in 6+ year old GLFW code&lt;/h2&gt;
&lt;p&gt;One &lt;em&gt;particularly frustrating&lt;/em&gt; issue was tracking down why the last part of the GLFW API we needed to wrap for 100% coverage, the &lt;code&gt;glfwSetWindowIcon&lt;/code&gt; function, was crashing:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Test [76/135] Window.test "setIcon"... Illegal instruction at address 0x2cee09
upstream/glfw/src/x11_window.c:0:0: 0x2cee09 in _glfwPlatformSetWindowIcon (/mach/glfw/upstream/glfw/src/x11_window.c)
upstream/glfw/src/window.c:511:5: 0x2de484 in glfwSetWindowIcon (/mach/glfw/upstream/glfw/src/window.c)
_glfwPlatformSetWindowIcon(window, count, images);
^
/mach/glfw/src/Window.zig:508:28: 0x23a083 in Window.test "setIcon" (test)
c.glfwSetWindowIcon(self.handle, @intCast(c_int, im.len), &amp;amp;tmp[0]);
^
/usr/local/bin/lib/std/special/test_runner.zig:77:28: 0x25a0d1 in std.special.main (test)
} else test_fn.func();
^
/usr/local/bin/lib/std/start.zig:517:22: 0x2896bc in std.start.callMain (test)
root.main();
^
/usr/local/bin/lib/std/start.zig:469:12: 0x25c117 in std.start.callMainWithArgs (test)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/usr/local/bin/lib/std/start.zig:434:12: 0x25bec2 in std.start.main (test)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
???:?:?: 0x7f4b7c3280b2 in ??? (???)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s odd? &lt;code&gt;Illegal instruction at address 0x2cee09&lt;/code&gt; - are we corrupting the stack somehow? Is this a Zig compiler bug?&lt;/p&gt;
&lt;p&gt;Running in &lt;code&gt;lldb&lt;/code&gt; didn&amp;rsquo;t help with shining any light on the problem, either:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img3.png"&gt;&lt;img alt="lldb showing nothing particularly useful" class="color" src="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img3.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After poking around at the stack, checking all pointers and lengths were valid, etc. I was at a loss. The mach-glfw code &lt;em&gt;sure seemed valid&lt;/em&gt;, and yet, this crash. I managed to track the crash down to the first iteration of a loop in GLFW&amp;rsquo;s &lt;code&gt;x11_window.c&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_glfwSetWindowIconX11&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_GLFWwindow&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;GLFWimage&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;longCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;longCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_glfw_calloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// illegal instruction on first iteration?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="reaching-my-limits"&gt;Reaching my limits&lt;/h2&gt;
&lt;p&gt;At this point, I feel confident in saying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Zig code is correct, the pointers are valid, the lengths are correct, everything&amp;rsquo;s right.&lt;/li&gt;
&lt;li&gt;The GLFW code is pretty popular, and it&amp;rsquo;s been around for 6 years. Seems unlikely it&amp;rsquo;s a bug in GLFW?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily, my brother (and reverse engineer) &lt;a href="https://github.com/Andoryuuta"&gt;@Andoryuuta&lt;/a&gt; was available to help debug, so I pulled him in. Stepping through instructions, we could see clearly that after a bit shift we were stepping into the abyss:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;* thread #1, name = 'test', stop reason = instruction step over
frame #0: 0x00000000002c6f84 test`_glfwPlatformSetWindowIcon(window=0x00000000004e53d0, count=1, images=0x00007fffec0b3000) at x11_window.c:2156:58
2153 *target++ = (images[i].pixels[j * 4 + 0] &amp;lt;&amp;lt; 16) |
2154 (images[i].pixels[j * 4 + 1] &amp;lt;&amp;lt; 8) |
2155 (images[i].pixels[j * 4 + 2] &amp;lt;&amp;lt; 0) |
-&amp;gt; 2156 (images[i].pixels[j * 4 + 3] &amp;lt;&amp;lt; 24);
2157 printf("DID WE GET HERE???x\n");
2158 }
2159 }
(lldb)
Process 6516 stopped
* thread #1, name = 'test', stop reason = instruction step over
frame #0: 0x00000000002c6c21 test`_glfwPlatformSetWindowIcon(window=0x00000000004e53d0, count=1, images=0x00007fffec0b3000) at x11_window.c:0
1 //========================================================================
2 // GLFW 3.3 X11 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2019 Camilla Löwy &amp;lt;elmindreda@glfw.org&amp;gt;
6 //
7 // This software is provided 'as-is', without any express or implied
(lldb)
Process 6516 stopped
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Inspecting the binary in IDA Pro we were able to see that we were jumping into an &lt;code&gt;__asm { ud1 }&lt;/code&gt; section (ud1 standing for &amp;ldquo;undefined instruction 1&amp;rdquo;):&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img4.png"&gt;&lt;img alt="IDA Pro showing a jump to an undefined instruction 1" class="color" src="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img4.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It turns out that clang&amp;rsquo;s UBSan inserts these instructions as traps for when the compiler thinks there is undefined behavior occurring, such as if a pointer addition leads to an overflow. This is super interesting, but unfortunately doesn&amp;rsquo;t always give a compiler error. We got lucky and found someone else who ran into this through Google:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I &lt;em&gt;believe&lt;/em&gt; LLVM explicitly generates a ud2 x86 instruction because &amp;ldquo;it determined&amp;rdquo; there&amp;rsquo;s undefined behavior in the C code. So first I wonder which flags you&amp;rsquo;re passing it through zig (i.e. how strict are you being with the settings?) — Abner (@AbnerCoimbre)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And indeed, compiling via &lt;code&gt;zig build test -Drelease-fast&lt;/code&gt; (which turns off UBsan) made the crash go away. So where&amp;rsquo;s the undefined behavior?&lt;/p&gt;
&lt;p&gt;If we squint at the code and assume all pointers, counts, and indices are correct, you might be able to spot it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_glfwSetWindowIconX11&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_GLFWwindow&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;GLFWimage&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// illegal instruction on first iteration?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What is happening here is that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;images[i].pixels[j * 4 + 0]&lt;/code&gt; is returning an &lt;code&gt;unsigned char&lt;/code&gt; (8 bits)&lt;/li&gt;
&lt;li&gt;&lt;del&gt;It is then being shifted left by &lt;code&gt;&amp;lt;&amp;lt; 16&lt;/code&gt; bits. !!! That&amp;rsquo;s further than an 8-bit number can be shifted left by, so that&amp;rsquo;s UB&lt;/del&gt;
&lt;ul&gt;
&lt;li&gt;EDIT: Actually, it turns out that&amp;rsquo;s not exactly right, it&amp;rsquo;s the &lt;code&gt;&amp;lt;&amp;lt; 24&lt;/code&gt; that&amp;rsquo;s the cause of the UB, thanks &lt;a href="https://github.com/Maato"&gt;@Maato&lt;/a&gt; for &lt;a href="https://github.com/glfw/glfw/pull/1986#issuecomment-955784179"&gt;pointing this out and explaining in better detail than I could&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Suddenly, it all makes sense. And &lt;a href="https://godbolt.org/z/ddq75WsYK"&gt;if we load an equal snippet of code into Godbolt&lt;/a&gt; we can see what is happening when we compile without UBSan / the &lt;code&gt;-fsanitize=undefined&lt;/code&gt; flag:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img5.png"&gt;&lt;img alt="Compilation with godbolt with UBSan turned off shows movement into 32-bit EAX register" class="color" src="https://devlog.hexops.org/img/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/img5.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Without UBsan, clang merely uses the 32-bit EAX register as an optimization. It loads the 8-bit number into the 32-bit register, and then performs the left shift. Although the shift exceeds 8 bits, it &lt;em&gt;does not get truncated to zero&lt;/em&gt; - instead it is effectively as if the number was converted to a &lt;code&gt;long&lt;/code&gt; (32 bits) prior to the left-shift operation.&lt;/p&gt;
&lt;p&gt;This explains why nobody has caught this UB in GLFW yet, too: it works by accident! Just because the compiler likes to use 32-bit registers in this context.&lt;/p&gt;
&lt;p&gt;And this change benefits all the languages out there using GLFW: &lt;a href="https://github.com/glfw/glfw/pull/1986"&gt;glfw/glfw#1986&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="defaults-are-_critical_"&gt;Defaults are &lt;em&gt;critical&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;This code, and undefined behavior, has been in GLFW for over 6 years according to &lt;code&gt;git blame&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Anybody using GLFW &lt;em&gt;could have&lt;/em&gt; enabled UBSan in their C compiler. Anybody &lt;em&gt;could have&lt;/em&gt; run into this same crash and debugged it in the last 6 years. But they didn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;In mach-glfw, we compile all of GLFW&amp;rsquo;s C code with Zig (which is also a fully functional C and C++ compiler), with UBSan enabled by default.&lt;/p&gt;
&lt;p&gt;Only because Zig has good defaults, because it places so much emphasis on things being right &lt;em&gt;out of the box&lt;/em&gt;, and because there is such an emphasis on having safety checks for undefined behavior - were we able to catch this undefined behavior that went unnoticed in GLFW for the last 6 years.&lt;/p&gt;
&lt;h2 id="thanks-for-reading"&gt;Thanks for reading&lt;/h2&gt;
&lt;p&gt;All key Mach engine developments will be posted here.&lt;/p&gt;
&lt;p&gt;Follow &lt;a href="https://github.com/hexops/mach"&gt;Mach engine on GitHub&lt;/a&gt;, and if you like what I&amp;rsquo;m doing please consider &lt;a href="https://github.com/sponsors/emidoots"&gt;sponsoring my work&lt;/a&gt;.&lt;/p&gt;</description><author>Hexops' devlog</author><pubDate>Sun, 31 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://devlog.hexops.org/2021/perfecting-glfw-for-zig-and-finding-undefined-behavior/</guid></item><item><title>Building a fast SCSS-like rule expander for CSS using fuzzy parsing</title><link>http://notes.eatonphil.com/building-a-nested-css-rule-expander.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-10-31-building-a-nested-css-rule-expander.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Sun, 31 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/building-a-nested-css-rule-expander.html</guid></item><item><title>All you need to know about the metaverse, in two posts</title><link>https://stop.zona-m.net/2021/10/all-you-need-to-know-about-the-metaverse-in-two-posts/</link><description>&lt;p&gt;Plus another one, about a movie&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 31 Oct 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/all-you-need-to-know-about-the-metaverse-in-two-posts/</guid></item><item><title>OpenBSD</title><link>https://mfashby.net/posts/2021-10-30-openbsd/</link><description/><author>Home on mfashby.net</author><pubDate>Sat, 30 Oct 2021 22:25:43 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-10-30-openbsd/</guid></item><item><title>I'm offering my first Scaled Agile Class</title><link>https://www.brightball.com/articles/im-offering-my-first-scaled-agile-framework-safe-class</link><description>After 20 years in software development, my frustations with watching organizations hurt themselves from bad practices finally boiled over in my article, Reality Driven Development. The response and discussion from Hacker News lead me into the work of Donald Reinertsen, who laid out the math that validated everything I was experiencing. Much of the Scaled Agile Framework is based on his work, which lead into my journey with SAFe 3 years ago. Join me on December 9th for my first class, Leading SAFe 5.1.</description><author>Brightball Articles</author><pubDate>Sat, 30 Oct 2021 17:20:19 GMT</pubDate><guid isPermaLink="true">https://www.brightball.com/articles/im-offering-my-first-scaled-agile-framework-safe-class</guid></item><item><title>Lifecycle of Notes: My Implementation</title><link>http://evantravers.com/articles/2021/10/30/lifecycle-of-notes-my-implementation/</link><description>&lt;p&gt;This is a direct follow up to my previous discussion of the &lt;a href="http://evantravers.com/articles/2021/10/25/nurturing-notes-and-thoughts/"&gt;&amp;quot;strength of notes&amp;quot; and nurturing thought&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As with all aspects of a Creativity System, how you implement such a classification should differ based on your brain and creative goals. As a simple example: I believe that Jerry Seinfield uses two yellow legal pads… the first contains rough drafts of comedy material, and he migrates the &amp;quot;good ones&amp;quot; to a second for refinement. This is a rough classification system. In different episodes of &lt;a href="https://www.youtube.com/watch?v=8sxyYbh5mio&amp;amp;list=PL3NaIVgSlAVI6j6bXResrReMIc6zKBH5q"&gt;LYT-house&lt;/a&gt; I'm pretty sure I have seen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;YAML frontmatter tag for &lt;code&gt;epistemological status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Folders for &amp;quot;drafts&amp;quot; or &amp;quot;hypotheses&amp;quot;&lt;/li&gt;
&lt;li&gt;Emoji in the filename like &lt;code&gt;🚧&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=68huyTJjBF0"&gt;empty nodes on the graph and &lt;code&gt;#state/develop&lt;/code&gt; tags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;completely separate forms/app/systems (e.g. Seinfield's yellow legal pads)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are &lt;em&gt;so&lt;/em&gt; creative and are effective as &lt;a href=""&gt;identification&lt;/a&gt;… you just have to decide what works with your brain and goals… and steal it. 😈&lt;/p&gt;
&lt;p&gt;I have written about &lt;a href="http://evantravers.com/articles/2021/09/30/the-history-of-my-creativity-system/#outputs"&gt;my particular inputs and outputs&lt;/a&gt;. In the light of this my system uses different kinds of note, (the filename and structure) to signify the &amp;quot;strength&amp;quot; of an idea. The two main kinds of notes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;zettel&amp;quot;-style filenames for infant &amp;quot;weak&amp;quot; ideas and connecting thoughts (e.g. &lt;code&gt;202110140716.md&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&amp;quot;Evergreen&amp;quot; style named notes for strong facts and strong opinions (e.g. &lt;code&gt;Boundary of the Known.md&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This lifecycle kind of happened accidentally but it has stuck. In the &lt;a href="http://evantravers.com/articles/2021/09/30/the-history-of-my-creativity-system/#history-and-obstacles"&gt;many transitions of my system&lt;/a&gt; I tried 100% ZK style notes, and then migrated to Named Notes... but only for notes that had titles. This left a lot of my &lt;a href="http://evantravers.com/articles/2020/12/08/smz-updated-drafts-actions/"&gt;capture automations&lt;/a&gt; still creating ZK-style notes and created an organic difference between captured &amp;quot;ZK&amp;quot; style notes, and intentionally crafted evergreen Named Notes.&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#zettels" id="zettels"&gt;&lt;/a&gt;Zettels&lt;/h3&gt;
&lt;p&gt;Before I get a letter… I &lt;em&gt;know&lt;/em&gt; that these aren't proper &amp;quot;zettelkasten&amp;quot; notes. They are just what I call them.&lt;/p&gt;
&lt;p&gt;Zettels for me are the entry point into my note-taking system. Because they don't have a name, folder, or categorization they can just enter the web at any point… cheap, easy capture.&lt;/p&gt;
&lt;p&gt;Zettels (maybe I should come up with another name!) do abide by the note-making rules: a singular, atomic idea, in my voice, interesting to me… and they usually have at least one link to something else. They sometimes aren't more complex than a single sentence.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#examples" id="examples"&gt;&lt;/a&gt;Examples&lt;/h4&gt;
&lt;h5&gt;&lt;a class="anchor" href="#scripture-highlight" id="scripture-highlight"&gt;&lt;/a&gt;Scripture Highlight&lt;/h5&gt;
&lt;p&gt;A single link to a verse and some comments from a sermon or personal study… usually unverified by further study:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filename&lt;/strong&gt;: &lt;code&gt;202108300728.md&lt;/code&gt;&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;In [[1 Kings-19#18|1 Kings 19:18]] God answers Elijah's [[Depression]] by telling him the truth: he's not alone. There are 7000 others.
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;God could have said &amp;quot;I am enough,&amp;quot; or &amp;quot;don't be afraid,&amp;quot; but instead comes with the whisper &amp;quot;you are not the last faithful person.&amp;quot;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;&lt;a class="anchor" href="#book-note" id="book-note"&gt;&lt;/a&gt;Book Note&lt;/h5&gt;
&lt;p&gt;A reflection on a book idea that I have not proven or become convinced of yet:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filename&lt;/strong&gt;: &lt;code&gt;202110010724.md&lt;/code&gt;&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;Bondage or enslaved is the biblical language for we commonly call addiction or addictive personalities today.
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;Lou Priolo. _[[Pleasing People]]_. P &amp;amp; R Publishing, 2006. p52
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;&lt;a class="anchor" href="#errata" id="errata"&gt;&lt;/a&gt;Errata&lt;/h5&gt;
&lt;p&gt;Commentary on another note that didn't belong to that note's singular idea:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filename&lt;/strong&gt;: &lt;code&gt;202109301007.md&lt;/code&gt;&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;Constraint-based programming as described by Bret Victor[^bv] is basically programming by [[Outcomes Over Outputs]].
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;[^bv]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;https://youtu.be/8pTEmbeENF4?t=654&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;&lt;a class="anchor" href="#label" id="label"&gt;&lt;/a&gt;Label&lt;/h5&gt;
&lt;p&gt;A &amp;quot;label&amp;quot; on the &amp;quot;line&amp;quot; between two other notes. These are often made while exploring links… and I find one that I feel like future-me would need a little more context for how these relate or to connect a few things:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Filename&lt;/strong&gt;: &lt;code&gt;202110152107.md&lt;/code&gt;&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;[[ESV/1 Chronicles/1 Chronicles-19#12|1 Chronicles 19:12]] reflects the idea of [[Distributed Command]], [[Simple]], and [[Cover and Move]] from [[Extreme Ownership by Jocko Willink]]
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There's a lot more that gets added as zettels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sermon outlines&lt;/li&gt;
&lt;li&gt;dream logs&lt;/li&gt;
&lt;li&gt;random facts or user insights about the products in my day-job&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Zettels make up the bulk of what gets captured into my system. It's rare for me to enter a &amp;quot;strong&amp;quot; idea right out of the gate. Those &amp;quot;strong&amp;quot; ideas, (usually ones I've been holding for a &lt;em&gt;long&lt;/em&gt; time) get entered as Named Notes.&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#named-notes" id="named-notes"&gt;&lt;/a&gt;Named Notes&lt;/h3&gt;
&lt;p&gt;Notes containing a &lt;strong&gt;strong opinion or strong fact&lt;/strong&gt; become a Named Note or Evergreen Note. My ideas here are &lt;em&gt;so&lt;/em&gt; influenced by Andy Mutaschak's writing on Evergreen notes that you may as well open tab to his wild web of wisdom: &lt;a href="https://notes.andymatuschak.org/Evergreen_notes"&gt;Evergreen Notes - Andy Mutaschak&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A Named Note still follows the rules of atomicity, interestingness, and links… but it has a filename and title that is usable in a sentence and &lt;em&gt;meaningful for me&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#example" id="example"&gt;&lt;/a&gt;Example&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Filename&lt;/strong&gt;: &lt;code&gt;Journaling externalizes thoughts and feelings.md&lt;/code&gt;&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;&lt;span&gt;# Journaling externalizes thoughts and feelings
&lt;/div&gt;&lt;div class="line"&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;By externalizing the thoughts into words, we are able to separate our identity from the thoughts and feelings we are experiencing. This helps us be more objective and take every thought captive. 
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;You can see this pattern in the psalms. So many of them start with an experience or feeling that the psalmist describes… often in pathetic or visceral detail. Then the writer calms, and sets his will to worship and honor the Lord. 
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;---
&lt;/div&gt;&lt;div class="line"&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;- &lt;/span&gt;[[Simultaneous terror and peace]]. 
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;- &lt;/span&gt;[[booknotes/The Bullet Journal Method by Ryder Carroll]] talks a lot about becoming a witness to your own experience through rapid logging.
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Choosing a good name is important. Names that could just be the name of a &lt;code&gt;#tag&lt;/code&gt; (i.e. &amp;quot;Marriage&amp;quot;, &amp;quot;Agile Methodology&amp;quot;) are too generalized. These usually become hub notes or Maps of Content (as Nick Milo styles them.) If the name is usable in a sentence and meaningful to me, it helps me constrain the note to be singular.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-singular" id="fnref-singular"&gt;1&lt;/a&gt;&lt;/sup&gt; Instead of &amp;quot;Marriage&amp;quot; I have a note named &amp;quot;Living in an Understanding Way.&amp;quot; That's a meaningful thought to me… and it might get split into &amp;quot;Give Myself Up For Her&amp;quot; and &amp;quot;Living in an Understanding Way&amp;quot; as the thoughts become more developed and need atomicity.&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#form-dictates-workflow" id="form-dictates-workflow"&gt;&lt;/a&gt;Form Dictates Workflow&lt;/h3&gt;
&lt;p&gt;Because my &amp;quot;weak notes&amp;quot; don't have descriptive filenames, they are harder to &amp;quot;find.&amp;quot; They don't jump out visually in the graph or in backlinks, but they are still present and available for exploration. The &amp;quot;label&amp;quot; style weak notes especially pique my interest when looking at a local graph… those are often great places to start discovering.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image of a local graph in obsidian" src="https://images.evantravers.com/articles/2021/10/local_graph.png" /&gt;&lt;/p&gt;
&lt;p&gt;Occasionally while reviewing &amp;quot;weak&amp;quot; notes or zettels, &lt;a href="http://evantravers.com/articles/2021/10/25/nurturing-notes-and-thoughts/#path-to-maturity"&gt;I discover enough weak opinions to voltron into a strong fact or strong opinion&lt;/a&gt;. It's always exciting. It feels like being able to watch my thoughts forming. &lt;em&gt;Seeing&lt;/em&gt; and experiencing externally the process of thinking has helped me reject some of those new hypotheses without mindlessly accepting them just based on frequency&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-fallacy" id="fnref-fallacy"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#workflow-example-saul-and-fear" id="workflow-example-saul-and-fear"&gt;&lt;/a&gt;Workflow Example: Saul and Fear&lt;/h4&gt;
&lt;p&gt;An example of how all this all played together recently: making my way through 1 Samuel, I got to (one of the passages that amuses me the most) 1 Sam. 10:22:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So they inquired again of the LORD, &amp;quot;Is there a man still to come?&amp;quot; and the LORD said, &amp;quot;Behold, he has hidden himself among the baggage.&amp;quot; (ESV)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I laughed as I usually do at the thought of the giant future king of israel hiding under blankets among the donkeys and camels... and the thought occurred to me: Was Saul's predominant failing Fear?&lt;/p&gt;
&lt;p&gt;I jotted it into a zettel note, just a link to the Bible verse and a question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is Saul's predominant sin fear?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Over the following weeks I continued to read about Saul, and logged six or more little notes on the same line of thought. As I noticed similarities, notes became linked. I started to notice that it wasn't just fear, it was specifically linked to Saul's fear of other's opinion. I eventually rewrote and reshaped the notes into a new Evergreen form based on my strong opinion: &lt;code&gt;Saul was Dominated by Fear&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;&lt;span&gt;# Saul was dominated by fear
&lt;/div&gt;&lt;div class="line"&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;Saul's prevailing [[Sin]] may have been [[Fear]], specifically fear of how people saw him. 
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;He displays fearfulness at the beginning of his story as comic humility[^1022], later in life it manifests as violent paranoia.[^paranoia]
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;Samuel tells Saul that he thought little of himself[^little]… I wonder what this means? Self-esteem? Certainly a self-focus rather than trusting God. Samuel attempts to redirect his attention to the fact God anointed him king.
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;Saul confesses to Samuel that he feared the people's opinion.[^opinion] As further evidence of this, we can point to his dramatic transformation when the people acclaim David above him.[^acclaim]
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;In his final chapter his fear of the Philistine army[^army] drives him to seek out necromancy. His fear kept him from eating, and paralyzes him with terror when ghost Samuel tells him the truth.
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;[^1022]:  [[ESV/1 Samuel/1 Samuel-10#22|1 Samuel 10:22]]
&lt;/div&gt;&lt;div class="line"&gt;[^paranoia]:  [[ESV/1 Samuel/1 Samuel-18#12|1 Sam 18:12]], [[ESV/1 Samuel/1 Samuel-18#29|1 Sam 18:29]]
&lt;/div&gt;&lt;div class="line"&gt;[^little]: [[1 Samuel-15#17|1 Sam 15:17]]
&lt;/div&gt;&lt;div class="line"&gt;[^opinion]: [[ESV/1 Samuel/1 Samuel-15#24|1 Samuel 15:24]]
&lt;/div&gt;&lt;div class="line"&gt;[^acclaim]: [[1 Samuel-18#8|1 Samuel 18:8]]
&lt;/div&gt;&lt;div class="line"&gt;[^army]: [[1 Samuel-28#5|1 Samuel 28:5]]
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'm sure my style and form will continue to develop over time. I am really glad to have stumbled across this idea of Note Lifecycle. I continue to see a version of this thinking in other people's Creativity Systems, and am &lt;strike&gt;stealing&lt;/strike&gt; thinking about how to learn from them. 🤣&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-singular"&gt;
&lt;p&gt;This wonderful idea is completely stolen from the wonderful Andy Mutaschak's note: &lt;a href="https://notes.andymatuschak.org/Prefer_note_titles_with_complete_phrases_to_sharpen_claims"&gt;Prefer note titles with complete phrases to sharpen claims&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-singular"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-fallacy"&gt;
&lt;p&gt;&lt;a href="https://www.thoughtco.com/argumentum-ad-populum-250340"&gt;Appeal to numbers - thoughtco.com&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-fallacy"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Sat, 30 Oct 2021 11:41:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/10/30/lifecycle-of-notes-my-implementation/</guid></item><item><title>Understanding Your Tesla’s Energy Charts</title><link>https://savraj.co/2021/10/30/understanding-your-tesla's-energy-charts.html</link><description>There are a couple charts on the “Energy” tab in the Tesla user interface. They’re packed with interesting info and I thought I’d take a minute to explain what’s going on.</description><pubDate>Sat, 30 Oct 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://savraj.co/2021/10/30/understanding-your-tesla's-energy-charts.html</guid></item><item><title>Scraping GitHub Contributor Emails</title><link>https://nelson.cloud/scraping-github-contributor-emails/?ref=rss</link><description>Scraping GitHub contributor emails, and how you can protect yourself.</description><author>Nelson Figueroa</author><pubDate>Sat, 30 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nelson.cloud/scraping-github-contributor-emails/?ref=rss</guid></item><item><title>Royal Game of Ur (Background)</title><link>https://ztoz.blog/posts/game-of-ur-background/</link><description>&lt;p&gt;&lt;strong&gt;Updated 2021-12-17: A closed-form solution has been submitted&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Royal Game of Ur&lt;/em&gt;, also known as the &lt;em&gt;Game of Twenty Squares&lt;/em&gt;, is one of the oldest known board games, with attestations from 3000 to 250 BC. It was an enduringly popular game, played across multiple civilizations and across the Middle East. As a racing game, two players compete to move their pieces along and off the board, where their own pieces can act as road blocks to their own movement but can also send the other&amp;rsquo;s players pieces back to the beginning. In this article (part I), I present the rules for the game and discuss the game complexity metrics. In part II, I will discuss solving the game by computing the optimal strategy.&lt;/p&gt;
&lt;h1 id="rules"&gt;Rules&lt;/h1&gt;
&lt;p&gt;Contrary to claims that the rules have been fully discovered, we are still unsure how this game was played historically. Indeed, with a lifespan nearing 3000 years and being played by different cultures and geographies, there may have been many variants of the rules and changes over time. We know that in the latter period of time, the board straightened itself out (see figure). The path around the board, significance of the rosettes, and how to interpret dice rolls are all educated guesses. My copy of the game from &lt;a href="https://mastersofgames.com"&gt;Masters Traditional Games&lt;/a&gt; lists three routes and four rule variants. The path denoted in the figure is the &amp;ldquo;RC Bell&amp;rdquo; route.&lt;/p&gt;
&lt;p&gt;&lt;img alt="img" src="game-of-ur-board.png" title="Game of Twenty Squares boards" /&gt;&lt;/p&gt;
&lt;h2 id="standard-version"&gt;&amp;ldquo;Standard&amp;rdquo; version&lt;/h2&gt;
&lt;p&gt;The &amp;ldquo;standard&amp;rdquo; version is the set of rules you are likely to find implemented in electronic versions. This version of the rules was established by Irving Finkel based on archaeological copies of the board, inferences from the Itti-Marduk-balāṭu tablet, and experimentation. I imagine most people became aware of the game through the Youtube match between Irving Finkel and Tom Scott:&lt;/p&gt;

&lt;p&gt;(User tracking should be disabled unless you play the video.)&lt;/p&gt;
&lt;p&gt;Since analyzing a half-dozen variants of the game would be tedious, I&amp;rsquo;ll be focusing on this set of the rules.&lt;/p&gt;
&lt;p&gt;The game&amp;rsquo;s objective is for a player to move their seven pieces along their route and off the board before the other player can do the same.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All pieces begin in the &amp;ldquo;starting pool&amp;rdquo; and are not on the board.&lt;/li&gt;
&lt;li&gt;On a player&amp;rsquo;s turn, they roll four tetrahedral dice to determine how far they can move a piece. Each die allows only a result of zero or one, so the range of results of a throw is between 0 and 4 inclusive. (A zero result is a lost turn.) In Johnstone/standard dice notation &lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;, this is a roll of 4d2 - 4.&lt;/li&gt;
&lt;li&gt;Only one piece can be moved per roll and a piece must move forward. Pieces may be moved from those on the board or in the starting pool. If no pieces can be moved, the player loses the turn.&lt;/li&gt;
&lt;li&gt;A piece cannot move forward onto a piece owned by the same player and only one piece may occupy a square at a time. If a piece lands on a square owned by the opposing player, the opposing player&amp;rsquo;s piece is placed back in the starting pool (off the board). However, the middle rosette square is &amp;ldquo;safe&amp;rdquo; and a piece on this square cannot be ejected from the board.&lt;/li&gt;
&lt;li&gt;If the piece lands on a rosette, the player must roll again. The next move does not need to use the same piece.&lt;/li&gt;
&lt;li&gt;Pieces are removed from the board on an exact roll.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Humorously, in the video Finkel makes a big scene about Tom Scott calculating the odds for a roll of the dice and Finkel proclaims he&amp;rsquo;s never had any use for maths. But in his own paper, he works out the odds for the various rolls.&lt;/p&gt;
&lt;h2 id="itti-marduk-balāṭu---gambling-version"&gt;Itti-Marduk-balāṭu - Gambling version&lt;/h2&gt;
&lt;p&gt;Irving Finkel&amp;rsquo;s &lt;em&gt;On the rules for the Royal Game of Ur&lt;/em&gt; &lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; describes a different, more complex game based on a tablet written by Itti-Marduk-balāṭu. Since the game in the tablet is a modification of a &amp;ldquo;base&amp;rdquo; game, the standard version is (partly) defined by reverse-engineering the tablet&amp;rsquo;s version of the rules. In contrast to the standard version, the tablet version has five labeled pieces and a mechanism to be bet or gamble during play. This version is played on the elongated or revised board.&lt;/p&gt;
&lt;p&gt;Each player has five pieces, labeled Swallow, Storm-bird, Raven, Rooster, and Eagle respectively.&lt;/p&gt;
&lt;p&gt;There are two types of dice: a standard tetrahedral 1d4 and a yes/no die or 1d2. The rules for how to interpret the dice from the tablet allow many options, but for sake of choosing a single option, a player will first roll the 1d4. They &lt;em&gt;may&lt;/em&gt; then roll the yes/no die. If the roll is &amp;lsquo;no&amp;rsquo;, the turn is lost. If the roll is &amp;lsquo;yes&amp;rsquo;, the 1d4 result is transformed to 1-&amp;gt;5, 2-&amp;gt;6, 3-&amp;gt;7, and 4-&amp;gt;10.&lt;/p&gt;
&lt;p&gt;Each player starts with 15 counters and the game pool starts with 20 counters initially.&lt;/p&gt;
&lt;p&gt;Similar to the standard version, players move their pieces around the board and attempt to exit all pieces before their opponent. While playing, players may gain by landing on the rosettes and lose points by skipping over them. A player also loses points based on pieces remaining on the board at the game end. The winner is the one with the most points at the end.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To launch a piece onto the board, a specific role must be made. The swallow requires a 2, the storm-bird a 5, the raven a 6, the rooster a 7, and the eagle a 10. When launched, the swallow appears on the first rosette (position 4), while the others start at positions 5, 6, 7, and 10 respectively.&lt;/li&gt;
&lt;li&gt;Pieces must be launched in sequence. If a player has the swallow and storm-bird in play (or in the completed pool), they can only launch the raven on a 6, even if a 7 or 10 is rolled. If the storm-bird and raven are in play, but the swallow is sent back to the starting pool, the swallow will need to be re-launched before the rooster can be launched.&lt;/li&gt;
&lt;li&gt;When a player lands a piece on the rosette, they win from the game pool a number of counters equal to the piece&amp;rsquo;s value. The swallow has a value of 3, the storm-bird, raven, and rooster have a value of 4, and the eagle has a value of 5.&lt;/li&gt;
&lt;li&gt;If a piece moves over the rosette without landing on it, the player loses a number of counter equal to the piece&amp;rsquo;s value.&lt;/li&gt;
&lt;li&gt;Rosette positions are safe; a piece can not be knocked off the position by another piece.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At the end of the game, a player loses a number of counter equal to the sum of value of pieces on the board.&lt;/p&gt;
&lt;h1 id="state-representation-for-the-standard-version"&gt;State representation for the standard version&lt;/h1&gt;
&lt;p&gt;On the original board, a piece moves from the &amp;ldquo;ready pool&amp;rdquo; (position 0) through the first four safe positions, then through eight contended spaces, and finally through two safe positions before exiting the board to the &amp;ldquo;completed pool&amp;rdquo; (position 15). Thus, a position can be encoded in 4 bits for the 16 potential locations. Since each player has seven pieces, a player&amp;rsquo;s pieces can be encoded in 7 * 4 bits and both players pieces can be encoded in 2 * 7 * 4 or 56 bits.&lt;/p&gt;
&lt;p&gt;Alternatively, we can model the game from the perspective of the squares rather than the pieces. The 14 middle positions (which have a capacity of one) can be modeled in 14 bit and the number of pieces available in the starting pool require 3 bits. (The number of pieces in the ending pool can be calculated based on the difference of the total number of pieces and the number in the starting pool plus on the board.) This requires 17 bits of storage per player or 34 bits total.&lt;/p&gt;
&lt;p&gt;Naively, the state space for this board in the first representation allows \(2^{56}=72,057,594,037,927,936\) possible states, or approximately 72 quadrillion. This is a big number (half-way to the age of the Earth in seconds), but this representation also contains &lt;em&gt;many&lt;/em&gt; symmetrical games (piece 1 on position 3 and piece 2 on position 6 is identical to piece 1 being on position 6 and piece 2 on position 3) and invalid games (multiple pieces on the same position).&lt;/p&gt;
&lt;p&gt;The alternative representation allows for \(2^{34} = 17,179,869,184\) possible states or approximately 17 billion. This representation also contains &lt;em&gt;many&lt;/em&gt; invalid games.&lt;/p&gt;
&lt;p&gt;Can we compute a more precise value for the number of possible states?&lt;/p&gt;
&lt;h2 id="how-many-possible-valid-states-for-the-standard-version"&gt;How many possible, valid states for the standard version?&lt;/h2&gt;
&lt;p&gt;We can approximate the state space by using the &amp;ldquo;ball and urn&amp;rdquo; mental framework. We are trying to place pieces (balls) into board positions (urns). Each player has 16 urns (although some are shared) and all but two urns have a maximum capacity of one ball. The player&amp;rsquo;s pieces are identical to each other but the urns are distinct.&lt;/p&gt;
&lt;p&gt;If we simplify the rules and state that all urns have a maximum capacity of one ball, then we can leverage the helpful &lt;a href="https://www.johndcook.com/TwelvefoldWay.pdf"&gt;Twelvefold Way&lt;/a&gt; table. (There are multiple versions of this table; I find Cook&amp;rsquo;s one of the more readable on the web.) The entry for &amp;ldquo;unlabeled&amp;rdquo; balls, &amp;ldquo;labeled&amp;rdquo; urns, and a maximum capacity of one is equivalent to a &lt;em&gt;n choose k&lt;/em&gt; or binomial coefficient calculation.&lt;/p&gt;
&lt;p&gt;Table: \(\binom{n}{p}\) Values for \(n=16\) and \(p\) pieces&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;\(p\) Pieces&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Approximate State Space Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14,400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td style="text-align: right;"&gt;313,600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,312,400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td style="text-align: right;"&gt;19,079,424&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64,128,064&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td style="text-align: right;"&gt;130,873,600&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This is an approximation and includes invalid states where the two players are contesting the same slot while also excluding cases where multiple places may be in the starting and ending pool, but as we shall see, this is a pretty good approximation. (Since combinations are symmetric, e.g. \(\binom{16}{2} = \binom{16}{14}\), the approximation breaks down as \(p\) increases.) Furthermore, these numbers are fairly small, so it seems practical to have a computer iterate (smartly) through the cases as 131 million is far more reasonable than 17 billion.&lt;/p&gt;
&lt;p&gt;I wrote a &lt;a href="https://gitlab.com/jeffrey_starr/valid-ur-state"&gt;program in Rust&lt;/a&gt; that will generate and count all valid states. I use a recursive, depth-first search approach. Each player is modeled via a vector which represents a placement into a labeled urn. Since some positions allow multiple placements we cannot generates states via a standard lexicographic combination algorithm, but do so via a slight modification of the algorithm that will generate duplicate 0 and 15 positions.&lt;/p&gt;
&lt;p&gt;Within the search, our base case is a solution being found. Due to the functional design, the two player&amp;rsquo;s states are valid on entry to the function, so a valid full solution just requires a check that all placements have been made. If so, we pass the solution to a Visitor class that abstracts away what to do for every solution. (The visitor either prints out the solution or increments counts depending on the program&amp;rsquo;s mode.)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-rust"&gt;&lt;span style="color: #66d9ef;"&gt;fn&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;search_games_dfs&lt;/span&gt;(pieces: &lt;span style="color: #66d9ef;"&gt;usize&lt;/span&gt;, visitor: &lt;span style="color: #66d9ef;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #a6e22e;"&gt;mut&lt;/span&gt; ModalVisitor, p1state: &lt;span style="color: #66d9ef;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #a6e22e;"&gt;mut&lt;/span&gt; Vec&lt;span style="color: #f92672;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #66d9ef;"&gt;usize&lt;/span&gt;&lt;span style="color: #f92672;"&gt;&amp;gt;&lt;/span&gt;, p2state: &lt;span style="color: #66d9ef;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #a6e22e;"&gt;mut&lt;/span&gt; Vec&lt;span style="color: #f92672;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #66d9ef;"&gt;usize&lt;/span&gt;&lt;span style="color: #f92672;"&gt;&amp;gt;&lt;/span&gt;) {
    &lt;span style="color: #66d9ef;"&gt;if&lt;/span&gt; p1state.len() &lt;span style="color: #f92672;"&gt;==&lt;/span&gt; pieces &lt;span style="color: #f92672;"&gt;&amp;amp;&amp;amp;&lt;/span&gt; p2state.len() &lt;span style="color: #f92672;"&gt;==&lt;/span&gt; pieces {
        visitor.found(p1state, p2state)
    } &lt;span style="color: #66d9ef;"&gt;else&lt;/span&gt; {
        &lt;span style="color: #75715e;"&gt;// states are ordered monotonically; if the last move is the starting or ending pool,
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;        &lt;span style="color: #75715e;"&gt;// we can create a new state in the same pool. Otherwise, place in the next potentially
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;        &lt;span style="color: #75715e;"&gt;// valid spot.
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;        &lt;span style="color: #66d9ef;"&gt;let&lt;/span&gt; min_i &lt;span style="color: #f92672;"&gt;=&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;match&lt;/span&gt; (&lt;span style="color: #f92672;"&gt;*&lt;/span&gt;p1state).last() {
            Some(&lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt;) &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt;,
            Some(&lt;span style="color: #ae81ff;"&gt;15&lt;/span&gt;) &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;15&lt;/span&gt;,
            Some(min) &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; min &lt;span style="color: #f92672;"&gt;+&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;1&lt;/span&gt;,
            None &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt;
        };
        &lt;span style="color: #66d9ef;"&gt;let&lt;/span&gt; min_j &lt;span style="color: #f92672;"&gt;=&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;match&lt;/span&gt; (&lt;span style="color: #f92672;"&gt;*&lt;/span&gt;p2state).last() {
            Some(&lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt;) &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt;,
            Some(&lt;span style="color: #ae81ff;"&gt;15&lt;/span&gt;) &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;15&lt;/span&gt;,
            Some(min) &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; min &lt;span style="color: #f92672;"&gt;+&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;1&lt;/span&gt;,
            None &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt;
        };

        &lt;span style="color: #66d9ef;"&gt;for&lt;/span&gt; i &lt;span style="color: #66d9ef;"&gt;in&lt;/span&gt; min_i&lt;span style="color: #f92672;"&gt;..&lt;/span&gt;&lt;span style="color: #ae81ff;"&gt;16&lt;/span&gt; {
            p1state.push(i);
            &lt;span style="color: #66d9ef;"&gt;for&lt;/span&gt; j &lt;span style="color: #66d9ef;"&gt;in&lt;/span&gt; min_j&lt;span style="color: #f92672;"&gt;..&lt;/span&gt;&lt;span style="color: #ae81ff;"&gt;16&lt;/span&gt; {
                p2state.push(j);
                &lt;span style="color: #66d9ef;"&gt;if&lt;/span&gt; is_valid(p1state, p2state) {
                    search_games_dfs(pieces, visitor, p1state, p2state);
                }
                p2state.pop();
            }
            p1state.pop();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we are not at the base case, we either initialize a placement (&lt;code&gt;min_i&lt;/code&gt;, &lt;code&gt;min_j&lt;/code&gt;) at the zero position (no former placements have been made), try to continue at 0 or 15 (the pools that can handle more than one assignment), or the next entry (since all other urns have a maximum capacity of one and we are searching the space in ascending order).&lt;/p&gt;
&lt;p&gt;Then, we attempt all possibilities for player one and player two, verify they are valid (&lt;code&gt;is_valid&lt;/code&gt;), and if so recursively check the next depth. The &lt;code&gt;is_valid&lt;/code&gt; code verifies that placements are distinct (when necessary) within a player&amp;rsquo;s placements and distinct within the eight contended squares.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NB: This is not the most efficient representation of the board. If we used the 14-bit
representation of the board, we could detect collisions by masking off the first four bits,
use the and-bitwise operator and test
against zero. Furthermore, the representation does not allow modeling of more than one piece
within a position, so the first check in the code can be eliminated entirely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-rust"&gt;&lt;span style="color: #66d9ef;"&gt;fn&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;is_valid&lt;/span&gt;(player1: &lt;span style="color: #66d9ef;"&gt;&amp;amp;&lt;/span&gt;[&lt;span style="color: #66d9ef;"&gt;usize&lt;/span&gt;], player2: &lt;span style="color: #66d9ef;"&gt;&amp;amp;&lt;/span&gt;[&lt;span style="color: #66d9ef;"&gt;usize&lt;/span&gt;]) -&amp;gt; &lt;span style="color: #66d9ef;"&gt;bool&lt;/span&gt; {
    &lt;span style="color: #75715e;"&gt;// verify each individual player's pieces are unique or in the starting/ending pools
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;    &lt;span style="color: #66d9ef;"&gt;let&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;mut&lt;/span&gt; seen &lt;span style="color: #f92672;"&gt;=&lt;/span&gt; [&lt;span style="color: #66d9ef;"&gt;false&lt;/span&gt;; &lt;span style="color: #ae81ff;"&gt;16&lt;/span&gt;];

    &lt;span style="color: #75715e;"&gt;// The placement logic ensures that these constraints are respected, so this code could
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;    &lt;span style="color: #75715e;"&gt;// be skipped when called from search_games_dfs. Skipping this check halves the run-time.
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;    &lt;span style="color: #66d9ef;"&gt;for&lt;/span&gt; player &lt;span style="color: #66d9ef;"&gt;in&lt;/span&gt; [player1, player2].iter() {
        &lt;span style="color: #66d9ef;"&gt;for&lt;/span&gt; placement &lt;span style="color: #66d9ef;"&gt;in&lt;/span&gt; player.iter() {
            &lt;span style="color: #66d9ef;"&gt;match&lt;/span&gt; &lt;span style="color: #f92672;"&gt;*&lt;/span&gt;placement {
                &lt;span style="color: #ae81ff;"&gt;0&lt;/span&gt; &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; (),
                &lt;span style="color: #ae81ff;"&gt;15&lt;/span&gt; &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; (),
                place &lt;span style="color: #66d9ef;"&gt;if&lt;/span&gt; seen[place] &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt;
                    &lt;span style="color: #66d9ef;"&gt;return&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;false&lt;/span&gt;, &lt;span style="color: #75715e;"&gt;// duplicate
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;                place &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt;
                    seen[place] &lt;span style="color: #f92672;"&gt;=&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;true&lt;/span&gt;
            }
        }
        seen.fill(&lt;span style="color: #66d9ef;"&gt;false&lt;/span&gt;);  &lt;span style="color: #75715e;"&gt;// reset for next player
&lt;/span&gt;&lt;span style="color: #75715e;"&gt;&lt;/span&gt;    }

    &lt;span style="color: #66d9ef;"&gt;for&lt;/span&gt; player &lt;span style="color: #66d9ef;"&gt;in&lt;/span&gt; [player1, player2].iter() {
        &lt;span style="color: #66d9ef;"&gt;for&lt;/span&gt; placement &lt;span style="color: #66d9ef;"&gt;in&lt;/span&gt; player.iter() {
            &lt;span style="color: #66d9ef;"&gt;match&lt;/span&gt; &lt;span style="color: #f92672;"&gt;*&lt;/span&gt;placement {
                place &lt;span style="color: #66d9ef;"&gt;if&lt;/span&gt; place &lt;span style="color: #f92672;"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;5&lt;/span&gt; &lt;span style="color: #f92672;"&gt;&amp;amp;&amp;amp;&lt;/span&gt; place &lt;span style="color: #f92672;"&gt;&amp;lt;=&lt;/span&gt; &lt;span style="color: #ae81ff;"&gt;12&lt;/span&gt; &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt;
                    &lt;span style="color: #66d9ef;"&gt;if&lt;/span&gt; seen[place] {
                        &lt;span style="color: #66d9ef;"&gt;return&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;false&lt;/span&gt;
                    } &lt;span style="color: #66d9ef;"&gt;else&lt;/span&gt; {
                        seen[place] &lt;span style="color: #f92672;"&gt;=&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;true&lt;/span&gt;
                    },
                _ &lt;span style="color: #f92672;"&gt;=&amp;gt;&lt;/span&gt; ()
            }
        }
    }

    &lt;span style="color: #66d9ef;"&gt;return&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;true&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Table: Programmatic Count of Valid States with \(p\) pieces&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;\(p\) Pieces&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Computed State Space Size&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Branches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;248&lt;/td&gt;
&lt;td style="text-align: right;"&gt;232&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13,112&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24,148&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td style="text-align: right;"&gt;264,304&lt;/td&gt;
&lt;td style="text-align: right;"&gt;714,720&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,606,947&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,131,362&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14,680,840&lt;/td&gt;
&lt;td style="text-align: right;"&gt;61,834,256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td style="text-align: right;"&gt;53,212,388&lt;/td&gt;
&lt;td style="text-align: right;"&gt;255,752,544&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;7&lt;/em&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;137,913,936&lt;/td&gt;
&lt;td style="text-align: right;"&gt;726,864,992&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;280,408,902&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,572,665,124&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td style="text-align: right;"&gt;482,210,816&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,817,358,656&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;740,777,984&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,446,087,488&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Computations are relatively quick. Finding all 138 million solutions for p=7 took less than three minutes (with the first block of checks in &lt;code&gt;is_valid&lt;/code&gt; skipped) on a 2017 &amp;ldquo;Coffee Lake&amp;rdquo; Intel i7.&lt;/p&gt;
&lt;p&gt;The average branching factor at \(p=7\) is 5.3; this means that on a given ply (move), a player is making a potential decision between 5 different options, although depending on the roll an option may not be possible (e.g. the move is blocked by another piece). The branching factor is computed as the sum of at-most-one move from the starting pool, all moves on the board, and no moves from pieces in the ending pool.&lt;/p&gt;
&lt;p&gt;Comparing our earlier approximations and the programmatic counts finds the deltas to be (proportionally) small:&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="game-of-ur-count.png" title="Comparing Programmatic and Binomial State Space Counts" /&gt;&lt;/p&gt;
&lt;p&gt;Instead of leveraging a computer, can we count the number of positions using a closed form equation?&lt;/p&gt;
&lt;h2 id="closed-form"&gt;Closed form?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Whoever wants to go about generating all partitions not only immerses himself in immense labor,
but also must take pains to keep fully attentive, so as not to be grossly deceived.&lt;/p&gt;
&lt;p&gt;&amp;mdash; Leonhard Euler, &lt;em&gt;De Partitione Numerorum&lt;/em&gt; (1750)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A &lt;a href="https://distracted-jackson-c5763c.netlify.app/math/ur.html"&gt;reader&lt;/a&gt; has worked out the following closed form equation:&lt;/p&gt;
&lt;p&gt;$$S = \sum_{p_1 = 0}^{\min(p,m+n)}\sum_{p_2 = 0}^{\min(p,m+n)}\sum_{j = \max(0,p_1-m)}^{\min(p_1,n)}  (p-p_1+1)(p-p_2+1) {n \choose j} {m \choose p_1-j} {m+n-(p_1-j) \choose p_2}$$&lt;/p&gt;
&lt;p&gt;In the above notation, \(p_i\) are the number of pieces from player &lt;em&gt;i&lt;/em&gt; on the board, there are &lt;em&gt;m&lt;/em&gt; shared slots on the board and &lt;em&gt;n&lt;/em&gt; unshared/uncontested slots.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;Alas, I have been unable to find a closed-form solution.&lt;/del&gt; As the Euler quote warns, although one can use the inclusion/exclusion method to iterate toward the correct number, it is very easy to forget a case or include some duplicates. Graphically, here are the conflicting states for p=1 to 3 (an index refers to a lexicographically ordered single player&amp;rsquo;s valid position):&lt;/p&gt;
&lt;p&gt;&lt;img alt="p1" src="conflicts-p1.png" title="Conflicts for p=1" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="p2" src="conflicts-p2.png" title="Conflicts for p=2" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="p3" src="conflicts-p3.png" title="Conflicts for p=3" /&gt;&lt;/p&gt;
&lt;p&gt;As expected, conflicts are symmetric along the main diagonal. However, there are a number of sub-patterns that I&amp;rsquo;ve had trouble modeling. &lt;del&gt;I may return to this problem (or some reader may solve it), but at this point a closed form solution is convenient but not necessary.&lt;/del&gt;&lt;/p&gt;
&lt;h1 id="how-big-are-these-numbers"&gt;How big are these numbers?&lt;/h1&gt;
&lt;p&gt;Not very big, but beyond trivial. Using log-10 notation, the state space size is 8 (or 9 if we round up), which is less than the &lt;a href="https://en.wikipedia.org/wiki/Game_complexity"&gt;10 for Nine men&amp;rsquo;s morris, 20 for Checkers, and 44 for Chess&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A different measurement for a game is the game tree size; this represents the total number of games that can be played. This differs from the state space because there may be multiple paths to a given state. The game tree size is technically infinite for this game (since on a ply the player may roll a zero or players may constantly return each other&amp;rsquo;s pieces to the start). In practice, games tend to be quick and thus avoid becoming bogged down.&lt;/p&gt;
&lt;p&gt;We can approximate the gate tree size by raising the average branch to the average number of plies in a game, or \(b^{d}\). For an individual ply, a player needs to make a single decision of which piece to move; that decision is shared among all pieces on the board and at most one piece in the starting pool the board (since all pieces in the pool are identical). Through enumeration, we have found the average branch factor as 5.3. I don&amp;rsquo;t have statistics for the average number of plies, but in uncontested play, you will need on average 8 plies to move a piece to the end (since the mode is 2) and with 7 pieces you could expect 56 plies. If we estimate 70 as the average number of plies per player, that would be 140 total plies. The log10 of \(5.3^{120}\) is 87. In comparison, Checkers is 40 and Chess is 123.&lt;/p&gt;
&lt;p&gt;These numbers allow us to compare games against each other, but they do not explain what makes a game fun or engaging. For the ancients, this game had the benefit of low cost of entry (there are example boards etched in the floor), rapid play, and allowing of multiple strategies but with an element of chance. The central squares force interaction between the players while the safe squares allow the readying of &amp;ldquo;reserve&amp;rdquo; troops to threaten the other player. These combination of factors, in my view, are what makes the game fun and make the sum better than the parts.&lt;/p&gt;
&lt;h1 id="references"&gt;References&lt;/h1&gt;
&lt;section class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Peterson, Jon. &amp;ldquo;The Origins of Dice Notation.&amp;rdquo; &lt;a href="http://playingattheworld.blogspot.com/2013/08/the-origins-of-dice-notation.html"&gt;Playing at the World&lt;/a&gt; (2013).&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Finkel, Irving L. &amp;ldquo;On the rules for the Royal Game of Ur.&amp;rdquo; Ancient Board Games in Perspective (2007): 16-32.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>ℤ→ℤ</author><pubDate>Fri, 29 Oct 2021 23:40:31 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/game-of-ur-background/</guid></item><item><title>No girl software freedom here</title><link>https://stop.zona-m.net/2021/10/no-girl-software-freedom-here/</link><description>&lt;p&gt;Really?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 29 Oct 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/no-girl-software-freedom-here/</guid></item><item><title>Astro - static site generator made of dreams</title><link>https://arnorhs.dev/posts/2021-10-29/astro-static-site-builder-of-dreams/</link><description>Recently, a new static site generator (SSG) arrived on the block called Astro. Lets talk a bit about SSGs. What they're good for, what they're not and why I think Astro is a bit of a game changer.</description><author>arnorhs blog - arnorhs.dev</author><pubDate>Fri, 29 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arnorhs.dev/posts/2021-10-29/astro-static-site-builder-of-dreams/</guid></item><item><title>Exploring PL/pgSQL part two: implementing a Forth-like interpreter</title><link>http://notes.eatonphil.com/exploring-plpgsql-forth-like.html</link><description>&lt;p class="note"&gt;
  Previously in exploring PL/pgSQL:
  &lt;br /&gt;
  &lt;a href="exploring-plpgsql.html"&gt;Strings, arrays, recursion and parsing JSON&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;In my &lt;a href="https://notes.eatonphil.com/exploring-plpgsql.html"&gt;last post&lt;/a&gt;
I walked through the basics of PL/pgSQL, the embedded procedural
language inside of PostgreSQL. It covered simple functions, recursions
and parsing. But there was something very obviously missing from that
post: a working interpreter.&lt;/p&gt;
&lt;p&gt;So in this post we'll walk through building a Forth-like language from
scratch in PL/pgSQL. We'll be able to write a fibonacci function in
this Forth-like language and have it be evaluated correctly like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;sm.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT sm_run('&lt;/span&gt;
&lt;span class="s2"&gt;DEF fib&lt;/span&gt;
&lt;span class="s2"&gt;  DUP 1 &amp;gt; IF&lt;/span&gt;
&lt;span class="s2"&gt;  1- DUP 1- fib CALL SWAP fib CALL + THEN&lt;/span&gt;
&lt;span class="s2"&gt;  RET&lt;/span&gt;

&lt;span class="s2"&gt;20 fib CALL&lt;/span&gt;
&lt;span class="s2"&gt;EXIT')&amp;quot;&lt;/span&gt;

...

&lt;span class="w"&gt; &lt;/span&gt;sm_run
--------
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6765&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All code is available on &lt;a href="https://github.com/eatonphil/exploring-plpgsql/blob/main/sm.sql"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="forth"&gt;Forth&lt;/h3&gt;&lt;p&gt;&lt;a href="https://www.forth.com/resources/forth-programming-language/"&gt;Forth&lt;/a&gt;
is a stack-oriented language. Literals are pushed onto the stack.
Functions and builtins operate on the stack.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;sm.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT sm_run('3 2 + EXIT')&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Will produce &lt;code&gt;5&lt;/code&gt;. And:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;sm.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT sm_run('3 2 + 1 - EXIT')&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Will produce &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our code will notably not be a real Forth, since there are many
special features of a real Forth. But it will look like one to a
novice Forth programmer like myself.&lt;/p&gt;
&lt;p&gt;You can read more about Forth basics
&lt;a href="https://skilldrick.github.io/easyforth/"&gt;here&lt;/a&gt;. And you can read a
truly stunning, real Forth implementation in
&lt;a href="https://github.com/nornagon/jonesforth/blob/master/jonesforth.S"&gt;jonesforth.S&lt;/a&gt;. Or
you can pick up &lt;a href="https://letoverlambda.com/"&gt;Let Over Lambda&lt;/a&gt; for a
fantastic book on Common Lisp that culminates in a Forth interpreter.&lt;/p&gt;
&lt;h3 id="implementation"&gt;Implementation&lt;/h3&gt;&lt;p&gt;Since the builtin &lt;code&gt;array_length($arr, $dim)&lt;/code&gt; returns &lt;code&gt;NULL&lt;/code&gt; if the
array is &lt;code&gt;NULL&lt;/code&gt; and our dimension is always 1, we'll write a helper.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We'll also need to bring in the &lt;code&gt;hstore&lt;/code&gt; extension so we can map
function names to their positions. (We could use an association list
but those are less programmer-friendly.)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EXTENSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hstore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our interpreter function will take a string to evaluate, splitting the
string on whitespace into tokens.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sm_run&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sm_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regexp_split_to_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'\s+'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Data stack&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hstore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Map of functions to location&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Array we can use for temporary variables&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Current token&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Return pointer stack, always ints but easier to store as text&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Program counter&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We set up a &lt;code&gt;tmps&lt;/code&gt; array because each builtin may need differing
number of temporary variables and PL/pgSQL makes ad-hoc variables
cumbersome (or at least an easier way exists outside my knowledge).&lt;/p&gt;
&lt;p&gt;And we store the return pointer stack as a text array so that we can
use &lt;code&gt;sm_alength&lt;/code&gt; on it even though values in this array will always be
integers.&lt;/p&gt;
&lt;p&gt;Next we'll start an infinite loop to evaluate the program. The only thing
that will stop the input is the &lt;code&gt;EXIT&lt;/code&gt; builtin that will return from
this function with the top of the stack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOTICE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'[Debug] Current token: %. Current stack: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'PC out of bounds.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'EXIT'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If no other condition is met (the token is not a builtin), we push it
onto the data stack and increment the program counter.&lt;/p&gt;
&lt;h3 id="conditionals"&gt;Conditionals&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;IF&lt;/code&gt; builtin pops the top of the stack. If it is true evaluation
continues. If it is false evaluation skips ahead until after a &lt;code&gt;THEN&lt;/code&gt;
builtin.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;sm.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT sm_run('1 1 1 = IF 2 THEN EXIT')&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Produces &lt;code&gt;2&lt;/code&gt;. But&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;sm.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT sm_run('1 1 0 = IF 2 THEN EXIT')&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Produces &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="implementation"&gt;Implementation&lt;/h3&gt;&lt;p&gt;Joining the &lt;code&gt;EXIT&lt;/code&gt; condition in the interpeter loop we get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'IF'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab last item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'THEN'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Skip past THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;ELSE&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'THEN'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Just skip past it&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'EXIT'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="other-builtins"&gt;Other builtins&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;DUP&lt;/code&gt; builtin makes a copy of the top of the stack. The &lt;code&gt;SWAP&lt;/code&gt;
builtin swaps the order of the top two items on the stack. And the
&lt;code&gt;1-&lt;/code&gt; builtin subtracts 1 from the top of the stack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'DUP'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab item&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Add it to the stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'1-'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab item&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Rewrite top of stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'SWAP'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Swap the two&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's important that every builtin handle incrementing the program
counter and skipping to the beginning of the loop. Because some
builtins increment the program counter under different conditions
(like &lt;code&gt;IF&lt;/code&gt; above).&lt;/p&gt;
&lt;p&gt;The last few builtins are the simplest: arithmetic operations that
produce integers or booleans.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'+'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab two items from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Replace last item on stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="function-definitions"&gt;Function definitions&lt;/h3&gt;&lt;p&gt;Functions here will differ from Forth, borrowing elements of machine
code. Return pointers will be stored in a dedicated return pointer
stack. We could store it on the data stack but that would require more
effort on the part of the programmer to restore the stack. Calling
&lt;code&gt;RET&lt;/code&gt; inside a function pops a return pointer off the return pointer
stack.&lt;/p&gt;
&lt;p&gt;Here's a simple function definition: &lt;code&gt;DEF plus + RET&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'DEF'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- function name&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- starting pc&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'RET'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;-- RAISE NOTICE '[Debug] skipping past: %.', tokens[pc];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;ELSE&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hstore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- continue past 'RET'&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There doesn't seem to be a way to combine a NULL hstore value and a
non-NULL hstore value. So that's why we need that special case.&lt;/p&gt;
&lt;h3 id="return"&gt;Return&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;RET&lt;/code&gt; builtin pops a value off the return pointer stack and jumps
to it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'RET'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab last return pointer&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Drop last return pointer from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Jump to last return pointer&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="function-calls"&gt;Function calls&lt;/h3&gt;&lt;p&gt;Forming the other half of function calls is the &lt;code&gt;CALL&lt;/code&gt; builtin. This
places the program counter (plus one, past the &lt;code&gt;CALL&lt;/code&gt; token) onto the
return pointer stack and jumps to the position of the function if it
exists.&lt;/p&gt;
&lt;p&gt;A simple function call for the above &lt;code&gt;plus&lt;/code&gt; function might be: &lt;code&gt;2 3
plus CALL&lt;/code&gt; and would produce &lt;code&gt;5&lt;/code&gt; on the top of the stack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'CALL'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Grab item&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Remove one item from stack&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sm_alength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Store return pointer&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Fail if function not defined&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'No such function, %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;-- Otherwise jump to function&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOTICE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'[Debug] Jumping to: %:%.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defs&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tmps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's it! All done the basic instructions needed. Store all that code in &lt;code&gt;sm.sql&lt;/code&gt; and grab the &lt;code&gt;test.sh&lt;/code&gt; code from the previous post:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;./test.sh
sudo&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;postgres&lt;span class="w"&gt; &lt;/span&gt;psql&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%s;\n%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And try out our port of recursive fibonacci:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;sm.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT sm_run('&lt;/span&gt;
&lt;span class="s2"&gt;DEF fib&lt;/span&gt;
&lt;span class="s2"&gt;  DUP 1 &amp;gt; IF&lt;/span&gt;
&lt;span class="s2"&gt;  1- DUP 1- fib CALL SWAP fib CALL + THEN&lt;/span&gt;
&lt;span class="s2"&gt;  RET&lt;/span&gt;

&lt;span class="s2"&gt;20 fib CALL&lt;/span&gt;
&lt;span class="s2"&gt;EXIT')&amp;quot;&lt;/span&gt;

...

&lt;span class="w"&gt; &lt;/span&gt;sm_run
--------
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6765&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Happy PL/pgSQL- and Forth-ish-ing!&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Latest post is up! Writing a Forth(-inspired language) implementation from scratch in PL/pgSQL. Because who doesn't want to be able to run stack machine code from SELECT statements in PostgreSQL?&lt;a href="https://t.co/sbxhuDp1J9"&gt;https://t.co/sbxhuDp1J9&lt;/a&gt; &lt;a href="https://t.co/9nrHEIhRPa"&gt;pic.twitter.com/9nrHEIhRPa&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1453958284109500417?ref_src=twsrc%5Etfw"&gt;October 29, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Fri, 29 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/exploring-plpgsql-forth-like.html</guid></item><item><title>All reject our pointless fridge overlords</title><link>https://stop.zona-m.net/2021/10/all-reject-our-pointless-fridge-overlords/</link><description>&lt;p&gt;More nannies, than overlords, actually. But pointless, they are.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 29 Oct 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/all-reject-our-pointless-fridge-overlords/</guid></item><item><title>WeToddle retrospective</title><link>https://bytepawn.com/wetoddle-retrospective.html</link><description>&lt;p&gt;The idea behind WeToddle came from the Baby Fanclub group we have on Messenger, which has most of our family in it. It turns out some ex-Facebook people had a similar idea in 2019, raised $3M, spent a 2 years on it, and then gave up because it didn’t go anywhere (presumably).&lt;br /&gt;&lt;br /&gt; &lt;img alt="WeToddle Android" src="/images/wetoddle_android.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Fri, 29 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/wetoddle-retrospective.html</guid></item><item><title>How to setup the modern frontend toolchain in Django</title><link>https://tommasoamici.com/blog/how-to-setup-the-modern-frontend-toolchain-in-django/</link><description>So you want to deploy a traditional Django application, but you also want Typescript, Tailwind, and all the good that modern frontend offers?&lt;hr /&gt;&lt;a href="https://tommasoamici.com/blog/how-to-setup-the-modern-frontend-toolchain-in-django/"&gt;Read more&lt;/a&gt;</description><author>Tommaso Amici</author><pubDate>Thu, 28 Oct 2021 20:12:12 GMT</pubDate><guid isPermaLink="true">https://tommasoamici.com/blog/how-to-setup-the-modern-frontend-toolchain-in-django/</guid></item><item><title>Improving Fuzz Testing with Zig Allocators</title><link>https://www.ryanliptak.com/blog/improving-fuzz-testing-with-zig-allocators/</link><description>Using Zig allocators to improve the results of fuzz testing</description><author>ryanliptak.com</author><pubDate>Thu, 28 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.ryanliptak.com/blog/improving-fuzz-testing-with-zig-allocators/</guid></item><item><title>The Parable of A Rude Guest</title><link>https://www.marginalia.nu/log/33-rude-guests/</link><description>&lt;p&gt;You are invited to a dinner party. After talking for a while food is served on the table. You pounce. &amp;ldquo;Haha, suckers!&amp;rdquo;, you think, and load all the food on your plate and leave nothing but scraps for the hosts. You feel victorious. Serves them right for inviting you into their home. You wolf down the food with ravenous appetite while they look on.&lt;/p&gt;
&lt;p&gt;That was tasty, but now you got a piece of meat stuck between your teeth so you go to the bathroom and borrow some floss and use one the hosts&amp;rsquo; toothbrushes. You also use the toilet but don&amp;rsquo;t flush because you don&amp;rsquo;t think you are going to use it again.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Thu, 28 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/33-rude-guests/</guid></item><item><title>Git Cheatsheet - handy commands for every situation</title><link>https://gourav.io/blog/git-cheatsheet</link><description>Useful git commands for beginners like reverting commits, work between branches, manage PRs, and more</description><author>Gourav Goyal</author><pubDate>Thu, 28 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://gourav.io/blog/git-cheatsheet</guid></item><item><title>2021–10–28: Fixing broken Pinephone, aka Pinephone repairability</title><link>https://xnux.eu/log/#051</link><author>megi's PinePhone Development Log</author><pubDate>Thu, 28 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#051</guid></item><item><title>Hitler can now dine in italian restaurants</title><link>https://stop.zona-m.net/2021/10/hitler-can-now-dine-in-italian-restaurants/</link><description>&lt;p&gt;But never mind him: worry about trust in COVID19 management, instead.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 27 Oct 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/hitler-can-now-dine-in-italian-restaurants/</guid></item><item><title>Pico Ada Libraries Release 0.7.0</title><link>https://synack.me/blog/rp2040-hal-release-0-7-0.html</link><description>This is another big release, with a USB device controller driver, ADC DMA support, and boot2 rewritten in Ada. Many other small bug fixes and usability improvements are included. The detailed changelog can be found on the documentation site. Thanks to Daniel King, Fabien Chouteau, and Holger Rodriguez for patches, testing, advice, and feedback during this release cycle.</description><author>Jeremy Grosser</author><pubDate>Wed, 27 Oct 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://synack.me/blog/rp2040-hal-release-0-7-0.html</guid></item><item><title>An Eventful Few Months</title><link>https://benovermyer.com/blog/2021/10/an-eventful-few-months/</link><description>&lt;p&gt;It's been an eventful few months. Since my last blog post back in August, I've had Lasik surgery, had a house deal fall through, discovered a new hobby, rediscovered an old one, and edged deeper into the Apple ecosystem. That's not even counting all the various developments in my professional life.&lt;/p&gt;
&lt;p&gt;Lasik was expensive and the procedure was deeply uncomfortable, but worth it. For most of my life I've been near-blind without glasses. For the last month, though, I've had perfect vision. I still catch myself marveling at how awesome it is to have vision like this.&lt;/p&gt;
&lt;p&gt;The house deal... well, that sucked and was expensive, but it ultimately made me realize the value of renting indefinitely. So that'll keep happening.&lt;/p&gt;
&lt;p&gt;After abandoning Blizzard forever, I looked around for a new game to devote my lazy hours to. I landed on Final Fantasy XIV. After joining the Free Company "Mystic Exiles," I realized I'd found a new home. The Exiles are a great group of people - really friendly, really helpful, and genuinely caring. It feels like a second family. I have never had an MMORPG guild experience like that before. Plus, the game itself is a ton of fun. At time of writing, I'm barely into the content from the second expansion, and the fourth expansion is only a couple weeks away from launch. This will keep my attention for awhile, I imagine.&lt;/p&gt;
&lt;p&gt;The old hobby that I rediscovered is Warhammer 40k. A friend of mine invited me to a tournament he's hosting at his game store in Iowa, and I decided to dust off (literally) my boxes of miniatures and re-enter the fold. It's cost quite a lot of money to get all the new rulebooks, miniatures, and equipment I need to participate in the tournament, but I'm excited about the whole hobby again.&lt;/p&gt;
&lt;p&gt;Almost a month ago I bought a new iPhone 13 Pro and gave my iPhone 11 Pro to my wife. Since she was on Android before, and we chiefly used Facebook Messenger to communicate, this opened up a host of new options for us. Now we use mostly iMessage and avoid Facebook if at all possible. It's a little annoying to not have iMessage on Windows, but at least when I'm using my Mac I can type out messages to her.&lt;/p&gt;
&lt;p&gt;As for all of the developments in my professional life, well, that could be a whole 'nother blog post all on its own. Suffice it to say that the technology that I spend most of my dayjob hours on has changed dramatically, and I'm addressing some interesting new challenges. I'm really starting to dig Hashicorp Nomad as an alternative to Kubernetes.&lt;/p&gt;
&lt;p&gt;So that's a bit of what I've been up to the past couple months.&lt;/p&gt;</description><author>Ben Overmyer's Site</author><pubDate>Wed, 27 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://benovermyer.com/blog/2021/10/an-eventful-few-months/</guid></item><item><title>Day Two</title><link>https://utf9k.net/blog/day-two/</link><description>Thoughts as I enter the third job of my career in the software industry</description><author>utf9k</author><pubDate>Wed, 27 Oct 2021 02:01:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/day-two/</guid></item><item><title>The Unceasing Cessna Hacienda</title><link>https://www.damninteresting.com/the-unceasing-cessna-hacienda/</link><description>&lt;p&gt;&lt;img alt="" height="267" src="https://www.damninteresting.com/wp-content/uploads/2021/10/cover-002-400x267.jpg" style="width: 400px;" title="" width="400" /&gt;Warren &amp;#8220;Doc&amp;#8221; Bayley was a man of the people. When he and his wife Judy opened their Las Vegas resort in 1956, Bayley had no plans to compete with the flashier, corporate casinos at the center of the Strip. Instead, the Hacienda Hotel catered to families, as well as to locals who wanted a night out minus the tourists. When connoisseurs sneered, &amp;#8220;You can either go to Las Vegas or to the Hacienda,&amp;#8221; Bayley embraced the distinction with pride, and he didn&amp;#8217;t even mind when the nickname &amp;#8220;Hayseed Heaven&amp;#8221; took hold. Hayseeds were hard-working folks who deserved a vacation, too, and his steady bookings proved it.&lt;/p&gt;
&lt;p&gt;But margins were low, and Bayley was always on the lookout for new ways to drum up publicity for his so-called &amp;#8220;low roller&amp;#8221; operation. He didn&amp;#8217;t care where the ideas came from. Staff at the Hacienda were considered family, and Bayley regularly asked the advice of maids, bellboys, cooks, and anyone else who might have something useful to offer. So when a slot machine mechanic and former Army pilot named Bob Timm suggested breaking a world record—specifically, the world record for the longest endurance flight in a piloted plane—Bayley was willing to listen.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.damninteresting.com/the-unceasing-cessna-hacienda/"&gt;Continue reading &amp;#x25b6;&lt;/a&gt;&lt;/p&gt;</description><author>Damn Interesting</author><pubDate>Tue, 26 Oct 2021 21:26:58 GMT</pubDate><guid isPermaLink="true">https://www.damninteresting.com/the-unceasing-cessna-hacienda/</guid></item><item><title>Wikivoyage has disappeared from DuckDuckGo</title><link>https://gaganpreet.in/posts/duckduckgo-wikivoyage/</link><description>&lt;p&gt;&lt;strong&gt;Update (2021-10-31)&lt;/strong&gt;: DuckDuckGo now includes results for Wikivoyage again, as do Bing and Yahoo.&lt;/p&gt;
&lt;p&gt;I have been planning a long due vacation and I was doing some research into the places I wanted to visit. My goto travel guide is Wikivoyage: it&amp;rsquo;s well organised, text-heavy (instead of slideshow-heavy alternatives) and skimming the guide of a region will generally give a good idea of what to expect.&lt;/p&gt;
&lt;p&gt;So imagine my surprise when I enter a query with the keyword &amp;ldquo;Wikivoyage&amp;rdquo; on DuckDuckGo:&lt;/p&gt;</description><author>Gaganpreet Arora</author><pubDate>Tue, 26 Oct 2021 13:43:04 GMT</pubDate><guid isPermaLink="true">https://gaganpreet.in/posts/duckduckgo-wikivoyage/</guid></item><item><title>Is 3D printing good only if it's active?</title><link>https://stop.zona-m.net/2021/10/is-3d-printing-good-only-if-its-active/</link><description>&lt;p&gt;Maybe. Or maybe not.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 26 Oct 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/is-3d-printing-good-only-if-its-active/</guid></item><item><title>Book review - The Elements of User Experience</title><link>https://river.me/blog/book-review-elements-user-experience/</link><description>A review of &amp;ldquo;The Elements of User Experience&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 26 Oct 2021 10:17:38 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-elements-user-experience/</guid></item><item><title>Book Review: Language of the Spirit, An Introduction to Classical Music</title><link>https://nicolaiarocci.com/book-review-language-of-the-spirit-an-introduction-to-classical-music/</link><description>&lt;p&gt;In this introduction to classical music, Jan Swafford explains the different
musical periods and their differences. Each period has its introductory
chapter, followed by chapters dedicated to the most influential composers of
the era. The choice is comprehensive and well cared for, with the most relevant
names well-investigated both in biography and works. For each composer,
Swafford also offers some listening suggestions. Biographies thicken as we get
into the contemporary era. Here, some are limited to just a page or two while
others, obviously those of the author&amp;rsquo;s preferred composers, remain as thorough
as those of the older periods.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 26 Oct 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-language-of-the-spirit-an-introduction-to-classical-music/</guid></item><item><title>Red Mulberry Growlog #3</title><link>https://bartlomiejmika.com/posts/2021/red-mulberry-growlog-3/</link><description>&lt;p&gt;Into the over world from the underworld! The mulberries were grown in the basement for the majority of their existence and now are able to live in the outside for the first time. In this &lt;a href="https://bartlomiejmika.com/tags/grow-journal/"&gt;grow journal&lt;/a&gt; I&amp;rsquo;ll write about my experience bring the red mulberries into the outside and growing them over summer.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Tue, 26 Oct 2021 06:55:06 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/red-mulberry-growlog-3/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #43</title><link>https://justingarrison.com/123dev/2021-10-26-123dev-43/</link><description>&lt;img alt="Sabrina crumples her laptop and throws it away" src="https://justingarrison.com/img/123dev/43-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="continuous-frustration"&gt;Continuous frustration&lt;/h2&gt;
&lt;p&gt;Continuous integration and deployment systems have gone through a lot of changes. What started as shared bash scripts and Makefiles changed into central systems that could be shared by multiple developers and teams. This shift allows for more repeatable, scalable, and more secure systems.&lt;/p&gt;
&lt;p&gt;As systems optimize globally they can negatively impact local optimizations. This is a usually a win for an organization because they consolidate expertise and reduce overhead costs, but they reduce flexibility and can become a bottleneck.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 26 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-26-123dev-43/</guid></item><item><title>2021–10–26: Pinephone HDMI hot-plug-detection HW bug fixed</title><link>https://xnux.eu/log/#050</link><author>megi's PinePhone Development Log</author><pubDate>Tue, 26 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#050</guid></item><item><title>Understand a problem individually, then share it.</title><link>https://adamcraven.com/writing/understand-a-problem-individually-then-share-it/</link><description/><author>Writing on Adam Craven</author><pubDate>Mon, 25 Oct 2021 23:09:48 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/understand-a-problem-individually-then-share-it/</guid></item><item><title>Note 58</title><link>https://qubyte.codes/notes/1635191881767</link><description>&lt;p&gt;A little autumn weather isn’t putting folk off the sea.&lt;/p&gt;

    &lt;img alt="Three people in coats sit on the beach facing the ocean. In the distance there are people boating, windmills on the horizon, and clear sky peaking through the cloud." src="https://qubyte.codes/images/1635191837674.jpeg" /&gt;</description><author>Qubyte Codes</author><pubDate>Mon, 25 Oct 2021 22:58:01 GMT</pubDate><guid isPermaLink="true">https://qubyte.codes/notes/1635191881767</guid></item><item><title>Could age verified phones for kids work? SHOULD they?</title><link>https://stop.zona-m.net/2021/10/could-age-verified-phones-for-kids-work-should-they/</link><description>&lt;p&gt;If they have the right technology yes. Wait&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 25 Oct 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/could-age-verified-phones-for-kids-work-should-they/</guid></item><item><title>Nurturing Notes and Thoughts</title><link>http://evantravers.com/articles/2021/10/25/nurturing-notes-and-thoughts/</link><description>&lt;p&gt;Not all ideas are equal. Ideas have different sources, confidence levels, provability and permanence. We use different words to describe our ideas: Hunches, notions, opinions, theories, concepts, abstractions, beliefs, hypothesis, facts, laws. Our thoughts don't stay the same either. Our minds change over time.&lt;/p&gt;
&lt;p&gt;If there are all these ways to think about ideas… why do we use the same note-making structure to represent this variety?&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt="showing two graphs, both with a potential link. One is unlabeled, the other labels its nodes with degrees of confidence. I'm insinutating the potential link is more valid when you know the validity of the nodes." src="https://images.evantravers.com/articles/2021/10/unlabeled_vs_labeled.jpg" /&gt;
  &lt;figcaption&gt;
    Would you feel more confident making that link not knowing the veracity of the two ideas, or by being able to see that both ideas are "strong?"
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This has become the most impactful adaptation to my Creativity System: a simple classification system that has levels of &amp;quot;strength&amp;quot; for an idea. A &amp;quot;weak&amp;quot; idea is subject to doubt and disproval while a &amp;quot;strong&amp;quot; idea is core to my thinking.&lt;/p&gt;
&lt;p&gt;There is an argument for only including strong opinions or strong facts in your Creativity System. By this logic every link in your notes is valid and strong. While this is tempting, we &lt;em&gt;still&lt;/em&gt; need a place to capture and nurture new ideas. We would be creating a boundary arbitrarily in your Creativity System. However we slice and dice it, we have one Creativity System from inception to output.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#growing-your-notes" id="growing-your-notes"&gt;&lt;/a&gt;Growing Your Notes&lt;/h2&gt;
&lt;p&gt;&lt;img alt="showing a progression from weak to strong, representing notes" src="https://images.evantravers.com/articles/2021/10/growth.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Note-making is idea &lt;em&gt;development&lt;/em&gt;. To create and discover we must capture new and risky ideas and connections, not just recording the safe and the known. There is great value in capturing unproven thoughts so we can nurture them to maturity.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-scientific" id="fnref-scientific"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;There are many ways to implement notes with a lifecycle, but all such systems should consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;allow easy capture of new ideas&lt;/li&gt;
&lt;li&gt;easy identification of the strength of an idea&lt;/li&gt;
&lt;li&gt;clear path for weak ideas to become strong&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a class="anchor" href="#capture-the-interesting" id="capture-the-interesting"&gt;&lt;/a&gt;Capture the Interesting&lt;/h2&gt;
&lt;p&gt;&lt;img alt="everything should have a home" src="https://images.evantravers.com/articles/2021/10/capture.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Our Creativity Systems should support capturing and placing all kinds of notes into their own &amp;quot;home&amp;quot;, both weak and strong.&lt;/p&gt;
&lt;p&gt;If we restrict our system to strong ideas we limit our ability to capture new and wonderful ideas. A huge part of a Creativity System is to allow us to capture and nuture anything that grabs and holds our interest.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#clear-identification" id="clear-identification"&gt;&lt;/a&gt;Clear Identification&lt;/h2&gt;
&lt;p&gt;&lt;img alt="hunch &amp;lt; observation &amp;lt; insight &amp;lt; hypothesis &amp;lt; theory &amp;lt; law" src="https://images.evantravers.com/articles/2021/10/identification.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Our systems should have some sort of way of discerning what is a weak note, and what is a strong note. There are many such ways to do this (I'll detail mine in a later post) but it should make sense to you and be natural to your system and your goals.&lt;/p&gt;
&lt;p&gt;Having this sort of identification can help us deal with a subtle but deadly cognitive bias: Humans believe we are critical of new ideas. The cold reality: our minds &lt;strong&gt;default to believing all we hear or read&lt;/strong&gt;. We would like to think that when encountering a new idea we consider its truthfulness before believing… but it is far easier to just accept what we are given. We optionally (and rarely) consider a new idea's truthfulness after our minds have already accepted it.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-belief" id="fnref-belief"&gt;2&lt;/a&gt;&lt;/sup&gt; By encoding new &amp;quot;weak&amp;quot; ideas with a structure that denotes their fragility, we can help ourselves defeat this bias and navigate critically in new areas of thought.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#path-to-maturity" id="path-to-maturity"&gt;&lt;/a&gt;Path to Maturity&lt;/h2&gt;
&lt;p&gt;Once we have a way to capture all interesting ideas, and a clear way to discern the different kinds of thoughts and notes, we will often find notes traveling up or down the lifecycle. Once all our thoughts and notes are swimming around in the same sea, there are many lovely ways that they can grow, be disproven, or continue to flourish. Just revisiting a note after the passage of time can change its position in our minds.&lt;/p&gt;
&lt;p&gt;Sometimes while working I'll find strong surrounding arguments through search or links which will &amp;quot;promote&amp;quot; the idea in my mind.&lt;/p&gt;
&lt;p&gt;&lt;img alt="1. a note appears. 2. a note is connected to stronger notes. 3. the note gets more strength from its stronger connections" src="https://images.evantravers.com/articles/2021/10/maturation.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Sometimes while researching and writing I'll suddenly see a strong opinion that links together lots of weaker facts. The pattern suddenly shifts, and I'll have a new hypothesis to start studying.&lt;/p&gt;
&lt;p&gt;&lt;img alt="diagram showing how you can find a stronger idea by finding lots of weaker thoughts that all connect." src="https://images.evantravers.com/articles/2021/10/theorycrafting.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;There are many ways that allowing weak and strong ideas to co-exist in our notes can improve our thinking. I've started &amp;quot;debate&amp;quot; notes, linked notes arguing either side of a controversy. I've found patterns in my own thinking that were then encoded as a &amp;quot;weak&amp;quot; note, studied, and ultimately rejected because there's no proof.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#" id=""&gt;&lt;/a&gt;***&lt;/h2&gt;
&lt;p&gt;In previous versions of my note-taking systems I either restricted my system to only &amp;quot;worthy&amp;quot; or &amp;quot;strong&amp;quot; notes, or I threw &lt;em&gt;everything&lt;/em&gt; into the system but without a way to tell what was weak and what was strong. When only strong notes were in my system my thinking never grew, I only reinforced prior thoughts. When all thoughts were un-evaluated it was hard to navigate. By capturing everything while discerning weak and strong thoughts my Creativity System and my creative thinking has benefitted dramatically.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-scientific"&gt;
&lt;p&gt;There is a very good comparison here to the scientific method: hypothesis being proven by experiment into theories that stand the test of time until law… but there are &lt;em&gt;many&lt;/em&gt; such metaphors appropriate for different Creativity Systems. I didn't want to limit people's imaginations to only one. &lt;a class="footnote-backref" href="#fnref-scientific"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-belief"&gt;
&lt;p&gt;I cannot for the life of me remember where I first heard this. Proverbs 14:15 says it better than I could:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fools believe every word they hear, but wise people think carefully about everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While the &lt;a href="https://en.wikipedia.org/wiki/Replication_crisis"&gt;replication crisis&lt;/a&gt; makes me leary of &lt;em&gt;every&lt;/em&gt; social experiement, there are some works out there that highlight this idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://explorable.com/understanding-and-belief"&gt;Understanding and Belief - explorable.com&lt;/a&gt; has a good summary of the Gilbert experiment.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pubmed.ncbi.nlm.nih.gov/8366418/"&gt;You can't not believe everything you read - D T Gilbert 1, R W Tafarodi, P S Malone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.scholars.northwestern.edu/en/publications/the-consequences-of-reading-inaccurate-information"&gt;The Consequences of Reading Inaccurate Information - David Neil Rapp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;a class="footnote-backref" href="#fnref-belief"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Mon, 25 Oct 2021 11:09:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/10/25/nurturing-notes-and-thoughts/</guid></item><item><title>How to be a Game Changer: Intro</title><link>http://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro</link><description>&lt;blockquote&gt;
  &lt;p&gt;In battle, do not think you have to win. Think rather that you do not have to lose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Ginchin Funakoshi, founder of Shotokan karate-do
    &lt;blockquote&gt;

    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We may be rarely if at all involved in battles today, but “games” are all around us, and every interaction we participate in can be thought of as a game, with its own rules, frameworks, and expectations about the participants. Such games exist in negotiations, relationships, and other interactions.&lt;/p&gt;

&lt;p&gt;For example, imagine we are driving in a city, and we follow all the signs and rules of motion. In a sense, we’re playing the “traffic game”. It is a game which allows people in vehicles to move safely from point A to point B. We have options on how to play this game. We may actively change lanes in order to try to move faster through the road and arrive sooner. Alternatively, we may decide to stay in our lane, and take longer to reach the destination, but avoid the complexity and risk of switching lanes, resulting in a more relaxed, safer drive.&lt;/p&gt;

&lt;p&gt;Or we may look at the whole vehicular traffic, and ask ourselves “Why should we play this game at all?” and come up with contrarian answers. We might decide not to play the traffic game with a car, but with a motorcycle, to be even faster and more flexible, trading off safety. We might consider a bicycle, e-bike, scooter, skateboard, public transit, or even walking. We are already starting to think like a game changer.&lt;/p&gt;

&lt;p&gt;To go a step further, we may even decide we do not like any of these options and move to another city, or a town, or even a cabin in the woods. We are now a game changer, as we’ve changed the game that at least some people in society play - ourselves. Yet by opting out, we affect not just us, but the whole urban vehicular traffic for everyone else.&lt;/p&gt;

&lt;p&gt;We may ask, what makes anyone a game-changer? How can I become one? Should I be a game changer? Which games should I change?&lt;/p&gt;

&lt;p&gt;&lt;img alt="the game changer path" class="img-responsive" src="http://dimitarsimeonov.com/assets/image/2021_gamechanger/01path.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Let’s figure it out together. &lt;a href="https://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro"&gt;This is the first post&lt;/a&gt; from a sequence of 13 posts which will explore the topic in more detail, and plenty of examples. I will be posting one short post every week, and we’ll cover examples such as situations with limited resources, artificial scarcity, status, sales, cynicism, slippery slopes, obstacles, arbitraje, repeated games, the scapegoat game, no-exit environments and others.&lt;/p&gt;

&lt;p&gt;If you’d like to follow along, subscribe to receive these posts over email, &lt;a href="https://twitter.com/themitak"&gt;follow me on Twitter&lt;/a&gt;, where I’ll mention each post, or simply check back here once a week.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Sylvain Kieffer for spotting grammar typos in this post.&lt;/em&gt;&lt;/p&gt;</description><author>D13V</author><pubDate>Mon, 25 Oct 2021 04:23:29 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/10/25/how-to-be-a-game-changer-intro</guid></item><item><title>Bot Apologetics</title><link>https://www.marginalia.nu/log/32-bot-apologetics/</link><description>&lt;p&gt;There has been a bit of discussion over on Gemini recently regarding poorly behaved bots. I feel I need to add some perspective from the other side; as a bot operator (even though I don&amp;rsquo;t operate Gemini bots).&lt;/p&gt;
&lt;p&gt;Writing a web spider is pretty easy on paper. You have your standards, and you can test against your own servers to make sure it behaves before you let it loose.&lt;/p&gt;
&lt;p&gt;You probably don&amp;rsquo;t want to pound the server into silicon dust, so you add a crawl delay and parallelize the crawling, and now you have code that&amp;rsquo;s a lot harder to comprehend. This is likely the cause of some weird bot behavior, including mishandling of redirect loops or repeated visits to the same address. Multi-threaded orchestration based on a rapidly mutating data set is difficult to get right (the working set of the spider by necessity changes as it goes). You can iron a lot of this out locally, but some problems won&amp;rsquo;t crop up until you really push the limits with real-world scenarios.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Mon, 25 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/32-bot-apologetics/</guid></item><item><title>The Line</title><link>https://june.kim/the-line/</link><author>june.kim</author><pubDate>Mon, 25 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/the-line/</guid></item><item><title>An introduction to Monzo’s data stack</title><link>https://lukesingham.com/intro-monzo-data-stack/</link><description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://medium.com/data-monzo/an-introduction-to-monzos-data-stack-827ae531bc99"&gt;Data@Monzo Medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The modern data stack is a collection of rapidly evolving technologies that together provide a platform for analytics.&lt;/p&gt;
&lt;p&gt;While we at Monzo already have quite a modern data stack, there is always more to learn and room for improvement. This post summarises the core components of our modern data platform, some of the challenges we currently face, and what we want to change.&lt;/p&gt;
&lt;p&gt;We follow two principles to guide our choices on the data platform and how we structure our data team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Centralised data management&lt;/li&gt;
&lt;li&gt;Decentralised value creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Centralised data management allows companies to have a 360 degree view of their customers and their business. It unlocks data-driven decision making which is key to competing in today’s markets. Next to having all the data in one place, centralised data management also enables organisational efficiencies. Many data operations and data governance problems become a lot easier to solve if everyone in a company treats data in the same way.&lt;/p&gt;
&lt;p&gt;Decentralised value creation means we have a discipline of data folks that work embedded across the entire company. This is sometimes known as a &lt;a href="https://monzo.com/blog/2021/08/10/spinning-up-an-analytics-engineering-team"&gt;hub and spoke model&lt;/a&gt;. The teams closest to the problem space are the ones creating their data products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An overview of our data architecture&lt;/strong&gt;&lt;br /&gt;
&lt;img alt="A diagram of Monzo’s data architecture where data flows from the Monzo application flows through to BigQuery where data pipelines are created and load analytics into a data visualisation tool" src="https://lukesingham.com/content/images/2021/monzo-data-platform-architecture.png" /&gt;&lt;/p&gt;
&lt;p&gt;At a very high level, most of our data is in the form of event data being produced in our backend services. These events are streamed via ‘the firehose’ (using Kafka and NSQ) to specific append-only projects in BigQuery.&lt;/p&gt;
&lt;p&gt;Data developers create data pipelines from these events using &lt;a href="https://www.getdbt.com/"&gt;dbt&lt;/a&gt; (by dbt Labs, formerly Fishtown Analytics). We use dbt to incorporate software engineering best practices into our data work.&lt;/p&gt;
&lt;p&gt;Once a data developer has created a pipeline, they also create dashboards and get insights in Looker. It’s worth expanding on what we mean by ‘data developer’ here. Most of the data work is done by the data discipline, however anyone in the company can jump into the data pipelines and make dashboards. For a more detailed view of how we empower people to use data check out &lt;a href="https://monzo.com/blog/2021/08/10/spinning-up-an-analytics-engineering-team"&gt;Borja’s post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s dive a little deeper into each component and where we’re going with it.&lt;/p&gt;
&lt;h1 id="data-loaders"&gt;Data loaders &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#data-loaders"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Analytics Event Processor and Shipper is a tool we built that takes events from backend services posted onto the firehose, and analytics events from the app or internal tooling. There are two microservices involved:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Analytics Event Processor consumes two NSQ topics &lt;code&gt;firehose&lt;/code&gt; and &lt;code&gt;client_analytics&lt;/code&gt;. The Processor then enriches the payload of the event. For example, an event containing &lt;code&gt;account_id&lt;/code&gt; is expanded into an account object. Then the Processor sanitises the payload to remove personally identifiable information (PII).&lt;/li&gt;
&lt;li&gt;Analytics Event Shipper is responsible for writing both sanitised and raw events to BigQuery event tables. It creates BigQuery tables as necessary.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s worth noting that the event tables in BigQuery have a column for the payload. This allows the schema to change. A data developer can then extract columns using &lt;a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#json_functions"&gt;JSON_EXTRACT functions&lt;/a&gt; in BigQuery.&lt;/p&gt;
&lt;p&gt;We use Fivetran for loading data from several systems, including for our accounting and web analytics data.&lt;/p&gt;
&lt;p&gt;We also load a few miscellaneous bits of data, for example data dumps from third parties directly loaded into BigQuery or Google Cloud Storage. However, we aim to keep bespoke approaches to a minimum.&lt;/p&gt;
&lt;p&gt;A challenge we face, particularly with our events data is ownership. Frequently, teams that generate the event data in our production stack are not the teams consuming the data. When a data developer picks that event data up in BigQuery, they may not have all of the domain context and the data can have many issues, for example missing some of the data needed for a particular analysis. Not knowing who to contact about the data slows us down. We will solve this by having clear ownership of all our data, from our production stack to our analytics stack (see the data discovery and metadata management section later in this post 👇). Where possible we also include a data developer within data-generating teams.&lt;/p&gt;
&lt;h1 id="data-build-tool-(dbt)"&gt;Data Build Tool (dbt) &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#data-build-tool-(dbt)"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;dbt, made by dbt Labs, has become central to the way we interact with data at Monzo. dbt allows users to run models in a &lt;a href="https://docs.getdbt.com/docs/guides/managing-environments#how-do-i-maintain-different-environments-with-dbt"&gt;separate development environment&lt;/a&gt;. This is huge when working with data; to know you can quickly iterate and test in a sandpit environment without impacting production data. You can tell dbt is a successful part of our platform when not only our data team is using the tool, but also our software engineers and business analysts too. It hasn’t been a seamless experience though. Using anything at scale usually brings its fair share of technical challenges.&lt;/p&gt;
&lt;p&gt;Speed of running dbt has been a real issue. Currently we have over 4700 models in our dbt project with ~600k lines of SQL. When you have engineers using something slow, inevitably you will end up with workarounds, like our &lt;a href="https://github.com/monzo/ddbt"&gt;go-lang re-write of the dbt command line tool&lt;/a&gt;. However, having multiple competing tools isn’t ideal either and we don’t plan to rival dbt Labs any time soon!&lt;/p&gt;
&lt;p&gt;We forked dbt in 2019 and placed the setup in a Docker container. At the time this seemed like a good idea for the speed at which we wanted to get security and productivity features into our data workflows. Docker containers are great for consistency between local and production environments and help new Monzonauts get to grips with Monzo data.&lt;/p&gt;
&lt;p&gt;We’ve built a few custom features including &lt;code&gt;dbt upstream prod&lt;/code&gt; and &lt;code&gt;indirect_ref&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dbt upstream prod&lt;/code&gt; improves speed and saves money. When running dbt it is common to run with the upstream model operator &lt;code&gt;dbt run -m +tableD&lt;/code&gt;. This command runs all of the data pipeline in your development environment up to the common source tables used in production. In the example below you can see this runs 5 models.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://lukesingham.com/content/images/2021/dbt-upstream-operator.png" /&gt;&lt;/p&gt;
&lt;p&gt;With our upstream production feature, the dbt run will read upstream dependencies from production tables. In this case, the same aim is achieved by running 1 model.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://lukesingham.com/content/images/2021/dbt-upstream-prod.png" /&gt;&lt;/p&gt;
&lt;p&gt;When a data developer is working on two tables of the data pipeline, our upstream feature intelligently figures out what part of the pipeline should be built in the development environment and which tables can be read from production. In the example diagram below, the developer has made edits to tableB and tableD. At the command line the developer enters &lt;code&gt;upstream prod -m tableB tableD&lt;/code&gt;. Our upstream feature figures out tableC should also be run in the development environment because one of its upstream dependencies, tableB, has been modified.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://lukesingham.com/content/images/2021/dbt-upstream-prod-multiple-models.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;indirect_ref&lt;/code&gt; helps us make sure Monzonauts only access the data they need. Some teams work with separate BigQuery projects where only a few people need to have access to that data. But how do we use aggregates or anonymous versions of that data? &lt;code&gt;indirect_ref&lt;/code&gt; solves this problem by introducing the concept of 'interface tables' which sit at the end of a sensitive project’s data pipeline and act like an API dataset. The interface table is the only node that downstream consumers can access. Additionally, the interface table explicitly declares its downstream consumers. So if a downstream team uses the interface model, they will modify the dbt config in the interface model.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://lukesingham.com/content/images/2021/interface-model-config.png" /&gt;&lt;/p&gt;
&lt;p&gt;This explicitly forms a contract between the interface and consumer. As the interface model is owned by the team working with sensitive data, &lt;a href="https://github.blog/2017-07-06-introducing-code-owners/"&gt;GitHub’s code owners automatically tags&lt;/a&gt; the producing team in the pull request.&lt;/p&gt;
&lt;p&gt;Recently we did some benchmarking of how we could speed up dbt by a significant amount. We found:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;using an M1, the new Apple laptops with Apple’s own CPUs improved speed by 3x&lt;/li&gt;
&lt;li&gt;upgrading from dbt 0.15.0 -&amp;gt; 0.20.0 resulted in another &lt;a href="https://discourse.getdbt.com/t/release-v0-20-0-margaret-mead/2621#performance-5"&gt;3x speed increase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;moving dbt out of a container resulted in a 2x speed increase for those using a Intel CPU MacBook Pro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combining these will mean a 9x speed increase for Monzonauts with an M1 and an upgrade to dbt 😅&lt;/p&gt;
&lt;p&gt;These benchmark results give us a good set of actions for our next project on dbt. We will move away from using Docker containers and refactor our customisations into plugins. We’d love to raise these as PRs on the dbt project or open source them as separate tools (👋 dbt!). Once we’ve refactored, we’ll upgrade to the latest version of dbt to gain the speed improvements and many features that have been added since v0.15.0.&lt;/p&gt;
&lt;h1 id="git-ci%2Fcd"&gt;Git CI/CD &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#git-ci%2Fcd"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;All our code is version controlled and we use GitHub for &lt;a href="https://github.com/monzo/"&gt;our code repositories&lt;/a&gt;. We use a mono-repo approach, which ensures consistency of tooling, CI checks and increases discoverability. Data developers test the code, but parts of the testing are also automated as part of CI so we can merge changes with confidence.&lt;/p&gt;
&lt;p&gt;Our main challenge here using a mono repo has been the speed of CI checks. In the last year we reduced CI checks from ~30mins down to ~5mins. However as the complexity of our pipeline and number of models grow we will likely need to return to this as a problem. We also want to use CI/CD to start raising data standards. For example by setting minimum thresholds in our CI checks we can raise documentation, testing and metadata standards across Monzo.&lt;/p&gt;
&lt;h1 id="schedule-orchestration"&gt;Schedule orchestration &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#schedule-orchestration"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;We use Airflow to schedule, run and monitor the refresh of our data warehouse. Most of our models run on a nightly schedule, with some tables being ‘near real time’, rebuilding every 15 minutes. We use dbt to compile the &lt;a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph"&gt;Directed Acyclic Graphs (DAG)&lt;/a&gt; of data models, we then automatically convert this DAG to the format Airflow expects, &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/concepts/dags.html"&gt;a DAG of tasks&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="notifications"&gt;Notifications &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#notifications"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If everything ran smoothly all the time that would be great, but it doesn’t! We make use of &lt;a href="https://docs.getdbt.com/reference/model-configs#apply-configurations-to-one-model-only"&gt;dbt model tags&lt;/a&gt; and a bit of Python glue work to post model run statuses to a #data-monitoring Slack channel. Here’s what that looks like in one of our model configs:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://lukesingham.com/content/images/2021/slack-notification-dbt-config.png" /&gt;&lt;/p&gt;
&lt;p&gt;The Slack post tags the person who last modified the code and provides a few handy links to help resolve the issue.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://lukesingham.com/content/images/2021/dbt-slack-alert.png" /&gt;&lt;/p&gt;
&lt;p&gt;With a penchant for automating the boring things, we have quite a few bots. Other notable examples are the core models bot that helps the data team keep track of the daily run of our most business critical tables. We also have a stale tables bot which scans for tables that haven’t been updated in a given period of time, then archives them and ultimately deletes them.&lt;/p&gt;
&lt;p&gt;Airflow is a well-supported and widely-used Apache project that has served us well. However, managing a self-hosted instance is a single point of failure and regularly requires maintenance. To reduce the work with self-hosting we will likely move to using &lt;a href="https://cloud.google.com/composer/"&gt;Google Cloud Composer&lt;/a&gt; in 2022.&lt;/p&gt;
&lt;h1 id="data-discovery-and-metadata-management"&gt;Data discovery and metadata management &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#data-discovery-and-metadata-management"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Data discovery is a more nascent component of the modern data stack. We’ve been using dbt docs — however with &amp;gt;4700 models the manifest.json and catalog.json is &amp;gt;300mbs, so the user experience isn’t great, particularly in the remote environment where some users may have slow internet. Part of our data governance is to have accountability for datasets: who should I contact regarding this dataset?&lt;/p&gt;
&lt;p&gt;The Data Platform Engineering team is currently working on a solution to collect, store and surface metadata in an accessible way. We want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;make our end to end data discoverable — our event data sits outside of dbt docs until it gets consumed into a data pipeline, but how do you discover the events in the first place?&lt;/li&gt;
&lt;li&gt;have a single interface to consume the metadata, for example Amundsen, Marquez or DataHub&lt;/li&gt;
&lt;li&gt;make sure metadata doesn’t only apply to assets in our data warehouse — it also includes our backend database (CassandraDB) and our visualisation assets (charts and dashboards)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="cloud-compute-%2F-data-warehouse"&gt;Cloud compute / data warehouse &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#cloud-compute-%2F-data-warehouse"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A data platform needs a database. Google Cloud Platform (GCP) is a crucial part of Monzo’s data platform. BigQuery, is GCP’s serverless, highly scalable, and cost-effective multi-cloud data warehouse. At our scale, we pay a significant amount to Google every month so we are constantly working on ways to reduce our bill. Sometimes these are as easy as a configuration change for a model to execute in a different BigQuery project, or helpful tooling that detects these opportunities and notifies the data team of them. Other times it can involve a complete analytics engineering rethink of our data models (some are ~60 terabytes rebuilt daily).&lt;/p&gt;
&lt;p&gt;For more information, check out this &lt;a href="https://cloud.google.com/customers/monzo/"&gt;case study from Google on how we use BigQuery&lt;/a&gt; for analytics.&lt;/p&gt;
&lt;h1 id="data-visualisation"&gt;Data visualisation &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#data-visualisation"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;We use Looker for all of our dashboards. With automations like reports being sent to slack channels when particular conditions are met, Looker helps make Monzo a data-driven organisation. What struck me when joining Monzo was that the main company Key Performance Indicators (KPIs) were sent out to every employee every week. I’d never seen such transparency with a company’s performance. One of Monzo’s values is to &lt;a href="https://monzo.com/blog/2017/03/10/transparent-by-default"&gt;default to transparency&lt;/a&gt; and with this transparency brings a focus on the key outcomes we want to achieve. By default, everyone who works at Monzo has access to Looker and over 80% of our staff are active users.&lt;/p&gt;
&lt;p&gt;A small amount of custom data visualisation occurs in Google Sheets and in Google Colab (Google’s hosted Jupyter notebook solution).&lt;/p&gt;
&lt;p&gt;The main challenge has been the responsiveness of Looker at our scale. Despite Looker’s importance, nobody in the company owns it explicitly. This has resulted in continual degradation of cleanliness, structure and speed over the last 4 years. To date we have run a couple of ad-hoc projects to optimise Looker cleanliness and loading for end users. We’ve also created automation scripts to clean up broken dashboards, charts and detect columns in LookML that no longer exist.&lt;/p&gt;
&lt;h1 id="machine-learning-platform"&gt;Machine Learning platform &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#machine-learning-platform"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The Machine Learning (ML) platform deserves a separate Machine Learning Infrastructure blog post — watch this space! Briefly, our ML stack broadly sits at the intersection of Analytics Infrastructure (GCP), where we house and analyse our data, and the Backend (AWS), where we deploy our business logic. Our long-term strategy is to enable data scientists and backend engineers alike to safely and easily use machine learning as a tool, where appropriate, across the entire spectrum of problems we are tackling at Monzo.&lt;/p&gt;
&lt;p&gt;Some organisations have data scientists make predictive models and once happy with them, software engineers then need to reimplement them in the production code base. At Monzo we decided this would slow the deployment of ML too much. So we built scripts that template a Python ML microservice using Sanic as the API webserver that our data scientists and ML engineers can use to deploy to production. We also use CoLab for quick and dirty exploration.&lt;/p&gt;
&lt;p&gt;Influenced by &lt;a href="https://docs.feast.dev/"&gt;Feast’s architectural overview&lt;/a&gt;, the Feature Store is a common component for production ML that automates the journey of shipping features between our analytics (BigQuery) and production (Cassandra) databases. For a detailed writeup on &lt;a href="http://nlathia.github.io/2020/12/Building-a-feature-store.html"&gt;this component see Neal’s post on the topic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For more information on ML at Monzo see these posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://neal-lathia.medium.com/machine-learning-faster-ce404dc4cd2"&gt;Machine Learning Faster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/monzo-bank/machine-learning-at-monzo-d83a7d1a71e1"&gt;Machine Learning at Monzo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://nlathia.github.io/2020/12/Building-a-feature-store.html"&gt;Building a feature store&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="recommendations"&gt;Recommendations &lt;a class="direct-link" href="https://lukesingham.com/intro-monzo-data-stack/#recommendations"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Finally, if you’re interested in reading more on the topic, we love the thought leadership on modern analytics tooling from &lt;a href="https://roundup.getdbt.com/"&gt;Tristan Handy in the analytics roundup&lt;/a&gt; and accompanying podcast, &lt;a href="https://benn.substack.com/"&gt;Benn Stencil’s substack&lt;/a&gt;, &lt;a href="https://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt; (in particular the &lt;a href="https://martinfowler.com/articles/data-monolith-to-mesh.html"&gt;Data Mesh post by Zhamak Dehghani&lt;/a&gt;), and the &lt;a href="https://www.dataengineeringpodcast.com/"&gt;Data Engineering Podcast&lt;/a&gt; to mention a few.&lt;/p&gt;
&lt;p&gt;If you’re interested in working for the data team at Monzo, &lt;a href="https://monzo.com/careers/"&gt;we’re hiring!&lt;/a&gt;&lt;/p&gt;</description><author>Luke Singham</author><pubDate>Mon, 25 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lukesingham.com/intro-monzo-data-stack/</guid></item><item><title>Rendering on the Apple M1 Max Chip</title><link>https://blog.yiningkarlli.com/2021/10/takua-on-m1-max.html</link><description>&lt;p&gt;&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;

&lt;div class="tableofcontents"&gt;
    &lt;div class="tableofcontents-row"&gt;
        &lt;div class="tableofcontents-column2"&gt;
            &lt;div class="tableofcontents-content"&gt;
                1. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-introduction"&gt;Introduction&lt;/a&gt;&lt;br /&gt;
                2. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-disclaimers"&gt;Disclaimers&lt;/a&gt;&lt;br /&gt;
                3. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-the-m1-max-chip"&gt;The M1 Max Chip&lt;/a&gt;&lt;br /&gt;
                4. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-application-to-ray-tracing"&gt;Application to Ray Tracing&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column2"&gt;
            &lt;div class="tableofcontents-content"&gt;
                5. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-results"&gt;Results&lt;/a&gt;&lt;br /&gt;
                6. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-conclusion"&gt;Conclusion&lt;/a&gt;&lt;br /&gt;
                7. &lt;a href="/2021/10/takua-on-m1-max#2021-10-25-bonus-images-and-acknowledgements"&gt;Bonus Images and Acknowledgements&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div id="2021-10-25-introduction"&gt;&lt;/div&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Over the past year, I ported my hobby renderer, Takua Renderer, to 64-bit ARM.
I wrote up the entire process and everything I learned as a three-part blog post series covering topics ranging from assembly-level comparison between x86-64 and arm64, to deep dives into various aspects of Apple Silicon, to a comparison of x86-64’s SSE and arm64’s Neon vector instructions.
In the intro to part 1 of my arm64 series, I wrote about my &lt;a href="/2021/05/porting-takua-to-arm-pt1.html#2021-05-29-motivation"&gt;motivation for exploring arm64&lt;/a&gt;, and in the &lt;a href="/2021/07/porting-takua-to-arm-pt2.html#2021-07-31-conclusion"&gt;conclusion to part 2&lt;/a&gt; of my arm64 series, I wrote the following about the Apple M1 chip:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There’s really no way to understate what a colossal achievement Apple’s M1 processor is; compared with almost every modern x86-64 processor in its class, it achieves significantly more performance for much less cost and much less energy. The even more amazing thing to think about is that the M1 is Apple’s low end Mac processor and likely will be the slowest arm64 chip to ever power a shipping Mac; future Apple Silicon chips will only be even faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, those future Apple Silicon chips are now here!
Last week (relative to the time of posting), Apple announced new 14 and 16-inch MacBook Pro models, powered by the new Apple M1 Pro and Apple M1 Max chips.
Apple reached out to me last week immediately after the announcement of the new MacBook Pros, and as a result, for the past week I’ve had the opportunity to use a prerelease M1 Max-equipped 2021 14-inch MacBook Pro as my daily computer.
So, to my extraordinary surprise, this post is the unexpected Part 4 to what was originally supposed to be a two-part series about Takua Renderer on arm64.
This post will serve as something of a coda to my Takua Renderer on arm64 series, but will also be fairly different in structure and content to the previous three parts.
While the previous three parts dove deep into extremely technical details about arm64 assembly and Apple Silicon and such, this post will focus on a single question: now that professional-grade Apple Silicon chips exist in the wild, &lt;em&gt;how well do high-end rendering workloads run on workstation-class arm64&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/macbookpro14.jpg"&gt;&lt;img alt="Figure 1: The new 2021 14-inch MacBook Pro with an Apple M1 Max chip, running Takua Renderer." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/preview/macbookpro14.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div id="2021-10-25-disclaimers"&gt;&lt;/div&gt;
&lt;h2 id="disclaimers"&gt;Disclaimers&lt;/h2&gt;

&lt;p&gt;Before we dive in, I want to get a few important details out of the way.
First, this post is not really a product review or anything like that, and I will not be making any sort of endorsement or recommendation on what you should or should not buy; I’ll just be writing about my experiences so far.
Many amazing tech reviewers exist out there, and if what you are looking for is a general overview and review of the new M1 Pro and M1 Max based MacBook Pros, I would suggest you go check out reviews by The Verge, Anandtech, MKBHD, Dave2D, LinusTechTips, and so on.
Second, as with everything in this blog, the contents of this post represent only my personal opinion and do not in any way represent any kind of official or unofficial position, endorsement, or opinion on any matter from my employer, Walt Disney Animation Studios.
When Apple reached out to me, I received permission from Disney Animation to go ahead on a purely personal basis, and beyond that nothing with this entire process involves Disney Animation.
Finally, Apple is not paying me or giving me anything for this post; the 14-inch MacBook Pro I’ve been using for the past week is strictly a loaner unit that has to be returned to Apple at a later point.
Similarly, Apple has no say over the contents of this post; Apple has not even seen any version of this post before publishing.
What is here is only what I think!&lt;/p&gt;

&lt;div id="2021-10-25-the-m1-max-chip"&gt;&lt;/div&gt;
&lt;h2 id="the-m1-max-chip"&gt;The M1 Max Chip&lt;/h2&gt;

&lt;p&gt;Now that a year has passed since the first Apple Silicon arm64 Macs were released, I do have my hobby renderer up and running on arm64 with everything working, but I’ve only rendered relatively small scenes so far on arm64 processors.
The reason I’ve stuck to smaller scenes is because high-end workstation-class arm64 processors so far just have not existed; while large server-class arm64 processors with large core counts and tons of memory do exist, these server-class processors are mostly found in huge server farms and supercomputers and are not readily available for general use.
For general use, the only arm64 options so far have been low-power single-board computers like the Raspberry Pi 4 that are nowhere near capable of running large rendering workloads, or phones and tablets that don’t have software or operating systems or interfaces suitable for professional 3D applications, or M1-based Macs.
I have been using an M1 Mac Mini for the past year, but while the M1 performance-wise punches way above what a 15 watt TDP typically would suggest, the M1 only supports up to 16 GB of RAM and only represents Apple’s &lt;em&gt;entry&lt;/em&gt; into Apple Silicon based Macs.
The M1 Pro and M1 Max, however, are are Apple’s first high powered arm64-based chips targeted at professional workloads, meant for things like high-end rendering and many other creative workloads; by extension, the M1 Pro and M1 Max are also the first arm64 chips of their class in the world with wide general availability.
So, in this post, answering the question “how well do high-end rendering workloads run on workstation-class arm64” really means examining how well the M1 Pro and M1 Max can do rendering.&lt;/p&gt;

&lt;p&gt;Spoiler: the answer is &lt;em&gt;extremely well&lt;/em&gt;; all of the renders in the post were rendered on the 14-inch MacBook Pro with an M1 Max chip.
Here is a screenshot of Takua Renderer running on the 14-inch MacBook Pro with an M1 Max chip:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/takua-on-m1max.jpg"&gt;&lt;img alt="Figure 2: Takua Renderer running on arm64 macOS 12 Monterey, on a 14-inch MacBook Pro with an M1 Max chip." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/takua-on-m1max.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 14-inch MacBook Pro I’ve been using for the past week is equipped with the maximum configuration in every category: a full M1 Max chip with a 10-core CPU, 32-core GPU, 64 GB of unified memory, and 8 TB of SSD storage.
However, for this post, I’ll only focus on the 10-core CPU and 64 GB of RAM, since Takua Renderer is currently CPU-only (more on that later); for a deep dive into the M1 Pro and M1 Max’s entire system-on-a-chip, I’d suggest taking a look at &lt;a href="https://www.anandtech.com/show/17019/apple-announced-m1-pro-m1-max-giant-new-socs-with-allout-performance"&gt;Anandtech’s great initial impressions&lt;/a&gt; and later &lt;a href="https://www.anandtech.com/show/17024/apple-m1-max-performance-review"&gt;in-depth review&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first M1 Max spec that jumped out at me is the 64 GB of unified memory; having this amount of memory meant I could finally render some of the largest scenes I have for my hobby renderer.
To test out the M1 Max with 64 GB of RAM, I chose the forest scene from my &lt;a href="https://blog.yiningkarlli.com/2018/10/bidirectional-mipmap.html"&gt;Mipmapping with Bidirectional Techniques&lt;/a&gt; post.
This scene has enormous amounts of complex geometry; almost every bit of vegetation in this scene has highly detailed displacement mapping that has to be stored in memory, and the large amount of textures in this scene is what drove me to implement a texture caching system in my hobby renderer in the first place.
In total, this scene requires just slightly under 30 GB of memory just to store all of the subdivided, tessellated, and displaced scene geometry, and requires an additional few more GB for the texture caching system (the scene can render with just a 1 GB texture cache, but having a larger texture cache helps significantly with performance).&lt;/p&gt;

&lt;p&gt;I have only ever published two images from this scene: the &lt;a href="https://blog.yiningkarlli.com/content/images/2018/Oct/forest.cam0.0.jpg"&gt;main forest path view&lt;/a&gt; in the mipmapping blog post, and a closeup of a tree stump as the title image on my personal website.
I originally had several more camera angles set up that I wanted to render images from, and I actually did render out 1080p images.
However, to showcase the detail of the scene better, I wanted to wait until I had 4K renders to share, but unfortunately I never got around to doing the 4K renders.
The reason I never did the 4K renders is because I only have one large personal workstation that has both enough memory and enough processing power to actually render images from this scene in a reasonable amount of time, but I needed this workstation for other projects.
I also have a few much older spare desktops that do have just barely enough memory to render this scene, but unfortunately, those machines are so loud and so slow and produce so much heat that I prefer not to run them at all if possible, and I especially prefer not running them on long render jobs when I have to work-from-home in the same room!
However, over the past week, I have been able to render a bunch of 4K images from my forest scene on the M1 Max 14-inch MacBook Pro; quite frankly, being able to do this on a laptop is incredible to me.
Here is the title image from my personal website, but now rendered at 4K resolution on the M1 Max 14-inch MacBook Pro:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/forest.cam2.0.jpg"&gt;&lt;img alt="Figure 3: Forest scene title image from my personal website. Rendered using Takua Renderer on a M1 Max 14-inch MacBook Pro. Click through for full 4K version." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/preview/forest.cam2.0.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The M1 Max-based MacBook Pro is certainly not the first laptop to ever ship with 64 GB of RAM; the previous 2019 16-inch MacBook Pro was also configurable up to 64 GB of RAM, and there are crazy PC laptops out there that can be configured up even higher.
However, this is where the M1 Max and M1 Pro’s CPU performance comes into play: while previous laptops could support 64 GB of RAM and more, actually utilizing large amounts of RAM was difficult since previous laptop CPUs often couldn’t keep up!
Being able to fit a large data set into memory is one thing, but being able to run processing fast enough to actually make use of large data sets in a reasonable amount of time is the other half of the puzzle.
My wife has a 2019 16-inch MacBook Pro with 32 GB of memory, which is &lt;em&gt;just&lt;/em&gt; enough to render my forest scene.
However, as seen in &lt;a href="#2021-10-25-results"&gt;the benchmark results later in this post&lt;/a&gt;, the 2019 16-inch MacBook Pro’s Intel Core-i7 9750H CPU with 6 cores and 12 threads is over twice as slow as the M1 Max at rendering this scene &lt;em&gt;at best&lt;/em&gt;, and can be even slower depending on thermals, power, and more.
Rendering each of the images in this post took a few hours on the M1 Max, but on the Core-i7 9750H, the renders have to become overnight jobs with the 16-inch MacBook Pro’s fans running at full speed.
With only a week to write this post, a few hours per image versus an overnight job per image made the difference between having images ready for this post versus not having any interesting renders to show at all!&lt;/p&gt;

&lt;p&gt;Actually, the M1 Max isn’t just fast for a chip in a laptop; the M1 Max is stunningly competitive even with &lt;em&gt;desktop&lt;/em&gt; workstation CPUs.
For the past few years, the large personal workstation that I offload large projects onto has been a machine with dual Intel Xeon E5-2680 workstation processors with 8 cores / 16 threads each for a total of 16 cores and 32 threads.
Even though the Xeon E5-2680s are ancient at this point, this workstation’s performance is still on-par with that of the current Intel-based 2020 27-inch iMac.
The M1 Max is faster then the dual-Xeon E5-2680 workstation at rendering my forest scene, and considerably so.
But of course, a comparison with aging Sandy Bridge era Xeons isn’t exactly a fair sporting competition; the M1 Max has almost a decade of improved processor design and die shrinks to give it an advantage.
So, I also tested the M1 Max against… the current generation 2019 Mac Pro, which uses a Intel Xeon W-3245 CPU with 16 cores and 32 threads.
As expected, the M1 Max loses to the 2019 Mac Pro… &lt;em&gt;but not by a lot&lt;/em&gt;, and for a fraction of the power used.
The Intel Xeon W-3245 has a 205 watt TDP just for the CPU alone and has to be utilized in a huge desktop tower with an extremely elaborate custom-engineered cooling solution, whereas the M1 Max 14-inch MacBook Pro has a reported whole-system TDP of just 60 watts!&lt;/p&gt;

&lt;p&gt;How does Apple pack so much performance with such little energy consumption into their arm64 CPU designs?
A number of factors come into play here, ranging from partnering with TSMC to manufacture on cutting-edge 5 nm process nodes to better microarchitecture design to better software and hardware integration; outside of Apple’s processor engineering labs, all anyone can really do is just hypothesize and guess.
However, there are some good guesses out there!
Several plausible theories have to do with the choice to use the arm64 instruction set; the argument goes that having been originally designed for low-power use cases, arm64 is better suited for efficient energy consumption than x86-64, and scaling up a more efficient design to huge proportions can mean more capable chips that use less power than their traditional counterparts.
Another theory revolving around the arm64 instruction set has to do with microarchitecture design considerations.
The M1, M1 Pro, and M1 Max’s high-performance “Firestorm” cores &lt;a href="https://www.anandtech.com/show/16226/apple-silicon-m1-a14-deep-dive/2"&gt;have been observed&lt;/a&gt; to have an absolutely humongous reorder buffer, which enables extremely deep out-of-order execution capabilities; modern processors attain a lot of their speed by reordering incoming instructions to do things like hide memory latency and bypass stalled instruction sequences.
The M1 family’s high-performance cores posses an out-of-order window that is around twice as large as that in Intel’s current Willow Cove microarchitecture and around three times as large as that in AMD’s current Zen3 microarchitectures.
Having a huge reordering buffer supports the M1 family’s high-performance cores also having a high level of instruction-level parallelism enabled by extremely wide instruction execution and extremely wide instruction decoding.
While wide instruction decoding is certainly possible on x86-64 and other architectures, scaling wide instruction-issue designs in a low power budget is generally accepted to be a very challenging chip design problem.
The theory goes that arm64’s fixed instruction length and relatively simple instructions make implementing extremely wide decoding and execution far more practical for Apple, compared with what Intel and AMD have to do in order to decode x86-64’s variable length, often complex compound instructions.&lt;/p&gt;

&lt;div id="2021-10-25-application-to-ray-tracing"&gt;&lt;/div&gt;
&lt;h2 id="application-to-ray-tracing"&gt;Application to Ray Tracing&lt;/h2&gt;

&lt;p&gt;So what does any of the above have to do with ray tracing?
One concrete application has to do with opacity mapping in a ray tracing renderer.
Opacity maps are used to produce finer geometric detail on surfaces by using a texture map to specify whether a part of a given surface should actually exist or not.
Implementing opacity mapping in a ray tracer creates a surprisingly large number of design considerations that need to be solved for.
For example, texture lookups are usually done as part of a renderer’s shading system, which in a ray tracer only runs after ray intersection has been carried out.
However, evaluating whether or not a given hit point against a surface should be ignored or not &lt;em&gt;after&lt;/em&gt; exiting the entire ray traversal system leads to massive inefficiencies due to the need to potentially re-enter the entire ray traversal system from scratch again.
As an example: imagine a tree where all of the leaves are modeled as rectangular cards, and the shape of each leaf is produced using an opacity map on each card.
If the renderer wants to test if a ray hits any part of the tree, and the renderer is architected such that opacity map lookups only happen in the shading system, then the renderer may need to cycle back and forth between the traversal and shading systems for every leaf encountered in a straight line path through the tree (and trees have a lot of leaves!).
An alternative way to handle opacity hits is to allow for direct texture map lookups or to evaluate opacity procedurally from within the traversal system itself, such that the renderer can immediately decide whether to accept a hit or not without having to exit out and run the shading system; this approach is what most renderers use and is what ray tracing libraries like Embree and Optix largely expect.
However, this method produces a different problem: tight inner loop ray traversal code is now potentially dependent on slow texture fetches from memory!
Both of these approaches to implementing opacity mapping have downsides and potential performance impacts, which is why often times just modeling detail into geometry instead of using opacity mapping can actually result in &lt;em&gt;faster&lt;/em&gt; ray tracing performance, despite the heavier geometry memory footprint.
However, opacity mapping is often a lot easier to set up compared with modeling detail into geometry, and this is where a deep out-of-order buffer coupled with good branch prediction can make a big difference in ray tracing performance; these two tools combined can allow the processor to proceed with a certain amount of ray traversal work without having to wait for opacity map decisions.
Problems similar to this, coupled with the lack of out-of-order and speculative execution on GPUs, play a large role in why GPU ray tracing renderers often have to be architecture fairly differently from CPU ray tracing renderers, but that’s a topic for another day.&lt;/p&gt;

&lt;p&gt;I give the specific example above because it turns out that the M1 Max’s deep reordering capabilities seem to make a fairly noticeable difference in my Takua Renderer’s performance when opacity maps are used extensively!
In the following rendered image, the ferns have an extremely detailed, complex appearance that depends heavily on opacity maps to cut out leaf shapes from simple underlying geometry.
In this case, I found that the slowdown introduced by using opacity maps in a render on the M1 Max is proportionally much lower than the slowdown introduced when using opacity maps in a render on the x86-64 machines that I tested.
Of course, I have no way of knowing if the above theory for why the M1 Max seems to handle renders that use opacity maps better is correct, but whichever way, the end results look very nice and renders faster than on any other computer that I have!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/forest.cam3.0.jpg"&gt;&lt;img alt="Figure 4: Detailed close-up of a fern in the forest scene. Rendered using Takua Renderer on a M1 Max 14-inch MacBook Pro. Click through for full 4K version." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/preview/forest.cam3.0.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In terms of whether the M1 Pro or the M1 Max is better for CPU rendering, I only have the M1 Max to test, but my guess is that there shouldn’t actually be too large of a difference as long as the scene fits in memory.
However, the above guess comes with a major caveat revolving around memory bandwidth.
Where the M1 Pro and M1 Max differ is in the maximum number of GPU cores and maximum amount of unified memory configurable; the M1 Pro can go up to 16 GPU cores and 32 GB of RAM, while the M1 Max can go up to 32 GPU cores and 64 GB of RAM.
Outside of the GPU and maximum amount of memory, the M1 Pro and M1 Max chips actually share identical CPU configurations: both of them have a 10-core arm64 CPU with 8 high-performance cores and 2 energy-efficient cores, implementing a custom in-house Apple-designed microarchitecture.
However, for some workloads, I would not be surprised if the M1 Max is actually slightly faster since the M1 Max also has twice the memory bandwidth over the M1 Pro (400 GB/s on M1 Max versus 200 GB/s M1 Pro); this difference comes from the M1 Max having twice the number of memory controllers.
While consumer systems such as game consoles and desktop GPUs often do ship with memory bandwidth numbers comparable or even better than the M1 Max’s 400 GB/s, seeing these levels of memory bandwidth in even workstation CPUs is relatively unheard of.
For example, AMD’s monster flagship Ryzen Threadripper 3990X is currently the most powerful high-end desktop CPU on the planet (outside of server processors), but the 3990X’s maximum memory bandwidth tops out at 95.37 GiB/s, or 165.944 GB/s; seeing the M1 Max MacBook Pro ship with over twice the memory bandwidth compared to the Threadripper 3990X is pretty wild.
The M1 Max also has twice the amount of system-level cache as the M1 Pro; on the M1 family of chips, the system-level cache is loosely analogous to L3 cache on other processors, but serves the entire system instead of just the CPU cores.&lt;/p&gt;

&lt;p&gt;Production-grade CPU ray tracing is a process that depends heavily on being able to pin fast CPU cores at close to 100% utilization for long periods of time, while accessing extremely large datasets from system memory.
In an ideal world, intensive computational tasks should be structured in such a way that data can be pulled from memory in a relatively coherent, predictable manner, allowing the CPU cores to rely on data in cache over fetching from main memory as much as possible.
Unfortunately, making ray tracing coherent enough to utilize cache well is an extremely challenging problem.
Operations such as BVH traversal, which finds the closest point in a scene that a ray intersects, essentially represent an arbitrarily random walk through potentially vast amounts of geometry stored in memory, and any kind of incoherent walk through memory makes overall CPU performance dependent on memory performance.
As a result, operations like BVH traversal tend to be heavily bottlenecked by memory latency and memory bandwidth.
I expect that the M1 Max’s strong memory bandwidth numbers should provide a some performance boost for rendering compared to the M1 Pro.
A complicating factor, however, is &lt;a href="https://www.anandtech.com/show/17024/apple-m1-max-performance-review/2"&gt;how the additional memory bandwidth on the M1 Max is utilized&lt;/a&gt;; not all of it is available to just the CPU, since the M1 Max’s unified memory needs to also serve the system’s GPU, neural processing systems, and other custom onboard logic blocks.
The actual real-world impact should be easily testable by rendering the same scene on a M1 Pro and a M1 Max chip both with 32 GB of RAM, but in the week that I’ve had to test the M1 Max so far, I haven’t had the time or ability to be able to carry out this test on my own.
Stay tuned; I’ll update this post if I am able to try this test soon!&lt;/p&gt;

&lt;p&gt;I’m very curious to see if the increased memory bandwidth on the M1 Max will make a difference over the M1 Pro on this forest scene in particular, due to how dense some of the geometry is and therefore how deep some of the BVHs have to go.
For example, every single pine needle in this next image is individually modeled geometry, and every tree trunk has sub-pixel-level tessellation and displacement; being able to render this image on a MacBook Pro instead of a giant workstation is incredible:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/forest.cam1.0.jpg"&gt;&lt;img alt="Figure 5: Forest canopy made up of pine trees, with every pine needle modeled as geometry. Rendered using Takua Renderer on a M1 Max 14-inch MacBook Pro. Click through for full 4K version." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/preview/forest.cam1.0.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous posts about running Takua Renderer on arm64 processors, I included performance testing results across a wide variety of machines ranging from the Raspberry Pi 4B to the M1 Mac Mini all the way up to my dual Intel Xeon E5-2680 workstation.
However, all of those tests weren’t necessarily indicative of what real world rendering performance on huge scenes would be like, since all of those tests had to use scenes that were small enough to fit in to a M1 Mac Mini’s 16 GB memory footprint.
Now that I have access to a M1 Max MacBook Pro with 64 GB of memory, I can present some initial performance comparisons with larger machines rendering my forest scene.
I think these results are likely more indicative of what real-world production rendering performance looks like, since the forest scene is the closest thing I have to true production complexity (I haven’t ported the Disney’s Moana Island data set to work in my renderer yet).&lt;/p&gt;

&lt;p&gt;The machines I tested this time are a 2021 14-inch MacBook Pro with an Apple M1 Max chip with 10 cores (8 performance, 2 efficiency) and 10 threads, a 2019 16-inch MacBook Pro with an Intel Core i7-9750H CPU with 6 cores and 12 threads, a 2019 Mac Pro with an Intel Xeon W-3245 CPU with 16 cores and 32 threads, and a Linux workstation with dual Intel Xeon E5-2680 CPUs with 8 cores and 16 threads per CPU for a total of 16 cores and 32 threads.
The Xeon E5-2680 workstation is, quite franky, ancient, and makes for something of a strange comparison point, but it’s the main workstation that I use for personal rendering projects at the moment, so I included it.
I don’t exactly have piles of the latest server and workstation chips just laying around my house, so I had to work with what I got!
However, I was also able to borrow access to a Windows workstation with an AMD Threadripper 3990X CPU, which weighs in with 64 cores and 128 threads.
I figured that the Threadripper 3990X system is not at all a fair comparison point for the exact opposite reason why the Xeon E5-2680 is not a fair comparison point, but I thought I’d throw it in anyway out of sheer curiosity.
Notably, the regular Apple M1 chip does not make an appearance in these tests, since the forest scene doesn’t fit in memory on the M1.
I also borrowed a friend’s Razer Blade 15 to test, but wound up not using it since I discovered that it has the same Intel Core i7-9750H CPU as the 2019 16-inch MacBook Pro, but only has half the memory and therefore can’t fit the scene.&lt;/p&gt;

&lt;p&gt;In the case of the two MacBook Pros, I did all tests twice: once with the laptops plugged in, and once with the laptops running entirely on battery power.
I wanted to compare plugged-in versus battery performance because of Apple’s claim that the new M1 Pro/Max based MacBook Pros perform the same whether plugged-in or on battery.
This claim is actually a huge deal; laptops traditionally have had to throttle down CPU performance when unplugged to conserve battery life, but the energy efficiency of Apple Silicon allows Apple to no longer have to do this on M1-family laptops.
I wanted to verify this claim for myself!&lt;/p&gt;

&lt;div id="2021-10-25-results"&gt;&lt;/div&gt;
&lt;h2 id="results"&gt;Results&lt;/h2&gt;

&lt;p&gt;In the results below, I present three tests using the forest scene.
The first test measures how long Takua Renderer takes to run subdivision, tessellation, and displacement, which has to happen before any pixels can actually be rendered.
The subdivision/tessellation/displacement process has an interesting performance profile that looks very different from the performance profile of the main path tracing process.
Subdivision within a single mesh is not easily parallelizable, and even with a parallel implementation, scales very poorly beyond just a few threads.
Takua Renderer attempts to scale subdivision widely by running subdivision on multiple meshes in parallel, with each mesh’s subdivision task only receiving an allocation of at most four threads.
As a result, the subdivision step actually benefits slightly more from single-threaded performance over a larger number of cores and greater multi-threaded performance.
The second test is rendering the main view of the forest scene from my mipmapping blog post, at 1920x1080 resolution.
I chose to use 1920x1080 resolution since most of the time this is a more common maximum resolution to be using while working on artistic iteration.
The third test is rendering the fern view of the forest scene from Figure 2 of this post, at final 4K 3840x2160 resolution.
For both of the main rendering tests, I only ran the renderer for 8 samples per pixel, since I didn’t want to sit around for days to collect all of the data.
For each test, I did five runs, discarded the highest and lowest results, and averaged the remaining three results to get the numbers below.
Wall time (as in a clock on a wall) measures the actual amount of real-world time that each test took, while core-seconds is an approximation of how long each test would have taken running on a single core.
So, wall time can be thought of as a measure of total computation &lt;em&gt;power&lt;/em&gt;, whereas core-seconds is more a measure of computational &lt;em&gt;efficiency&lt;/em&gt;; in both cases, lower numbers are better:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Forest Subdivision/Displacement&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max (Plugged in):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;128 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1280 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max (Battery):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;128 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1280 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H (Plugged in):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;289 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 3468 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H (Battery):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;307 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 3684 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon W-3245:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;179 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 5728 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;222 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 7104 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;AMD Threadripper 3990X:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;146 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 18688 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Forest Rendering (Main Camera)&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, 8 spp, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max (Plugged in):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;127.143 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1271.4 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max (Battery):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;126.421 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1264.2 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H (Plugged in):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;288.089 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 3457.1 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H (Battery):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;347.898 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 4174.8 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon W-3245:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;106.332 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 3402.6 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;158.255 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 5064.2 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;AMD Threadripper 3990X:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;38.887 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 4977.5 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Forest Rendering (Fern Camera)&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;3840x2160, 8 spp, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max (Plugged in):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;478.247 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 4782.5 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max (Battery):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;496.384 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 4963.8 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H (Plugged in):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1084.504 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 13014.0 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H (Battery):&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1219.59 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 14635.1 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon W-3245:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;345.292 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 11049.3 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;576.279 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 18440.9 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;AMD Threadripper 3990X:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;108.2596 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 13857.2 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;When rendering the main camera view, the 2021 14-inch MacBook Pro used on average about 7% of its battery charge, while the 2019 16-inch MacBook Pro used on average about 39% of its battery charge.
When rendering the fern view, the 2021 14-inch MacBook Pro used on average about 19% of its battery charge, while the 2019 16-inch MacBook Pro used on average about 48% of its battery charge.
Overall by every metric, the 2021 14-inch MacBook Pro achieves an astounding victory over the 2019 16-inch MacBook Pro: a little over twice the performance for a fraction of the total power consumption.
The 2021 14-inch MacBook Pro also lives up to Apple’s claim of identical performance plugged in and on battery power, whereas in the results above, the 2019 16-inch MacBook Pro suffers anywhere between a 25% to 50% performance hit just from switching to battery power.
The 2021 14-inch MacBook Pro’s performance win is even more astonishing when considering that the 2019 16-inch MacBook Pro is the previous flagship that the new M1 Pro/Max MacBook Pros are the direct successors to.
Seeing this kind of jump in a single hardware generation is unheard of in modern tech and represents a massive win for both Apple and for the arm64 ISA.
The M1 Max also handily beats the old dual Intel Xeon E5-2680 that I am currently using by a comfortable margin; for my personal workflow, this means that I can now do everything that I previously needed a large loud power-hungry workstation for on the 2021 14-inch MacBook Pro, and I can do everything &lt;em&gt;faster&lt;/em&gt; on the 2021 14-inch MacBook Pro too.&lt;/p&gt;

&lt;p&gt;The real surprises to me came with the 2019 Mac Pro and the Threadripper 3990X workstation.
In both of those cases, I expected the M1 Max to lose, but the 2021 14-inch MacBook Pro came surprisingly close to the 2019 Mac Pro’s performance in terms of wall time.
Even more importantly as a predictor of future scalability, the M1 Max’s efficiency as measured by core-seconds comes in at far far superior to both the Intel Xeon W-3245 and the AMD Threadripper 3900X.
Imagining what a hypothetical future Apple Silicon iMac or Mac Pro with an even more scaled up M1 variant, or perhaps some kind of multi-M1 Max chiplet or multisocket solution, is extremely exciting!
I think that with the upcoming Apple Silicon based large iMac and Mac Pro, Apple has a real shot at beating both Intel and AMD’s highest end CPUs to win the absolute workstation performance crown.&lt;/p&gt;

&lt;p&gt;Of course, what makes the M1 Max’s performance numbers possible is the M1 Max’s energy efficiency; this kind of performance-per-watt is simply unparalleled in the desktop (meaning non-mobile, not desktop form factor) processor world.
The M1 architecture’s energy efficiency is what allows Apple to scale the design out into the M1 Pro and M1 Max and hopefully beyond.
Below is a breakdown of energy utilization for each of the rendering tests above; the total energy used for each render is the wall clock render time multiplied by the maximum TDP of each processor to get watt-seconds, which is then translated to watt-hours.
I assume maximum TDP for each processor since I ran Takua Renderer with processor utilization set to 100%.
For the two MacBook Pros, I’m just reporting the plugged-in results.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Forest Rendering (Main Camera)&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, 8 spp, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;60 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;2.1191 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;45 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;3.6011 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon W-3245:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;205 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;6.0550 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;11.4295 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;AMD Threadripper 3990X:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;280 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;3.0246 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Forest Rendering (Fern Camera)&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;3840x2160, 8 spp, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Max:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;60 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;7.9708 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;45 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;13.5563 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon W-3245:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;205 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;19.6625 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;41.6202 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;AMD Threadripper 3990X:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;280 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;8.4202 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;At least for my rendering use case, the Apple M1 Max is easily the most energy efficient processor, even without taking into account that the 60 W TDP of the M1 Max is for the entire system-on-a-chip including CPU, GPU, and more, while the TDPs for all of the other processors are &lt;em&gt;just&lt;/em&gt; for a CPU and don’t take into account the rest of the system.
The M1 Max manages to beat the 2019 16-inch MacBook Pro’s Intel Core i7-9750H in absolute performance by a factor of two whilst using anywhere between a two-thirds to half of the energy, and the M1 Max comes close to matching the 2019 Mac Pro’s absolute performance while using about a third of the energy.
Of course the comparison with the Intel Xeon E5-2680 workstation isn’t exactly fair since the M1 Max is manufactured using a 5 nm process while the ancient Intel Xeon E5-2580s were manufactured on a 35 nm process a decade ago, but I think the comparison still underscores just how far processors have advanced over the past decade leading up to the M1 Max.
The only processor that really comes near the M1 Max in terms of energy efficiency is the AMD Threadripper 3990X, which makes sense since the AMD Threadripper 3990X and the M1 Max are the closest cousins in this list in terms of manufacturing process; both are using leading-edge TSMC photolithography.
However, on a whole, the M1 Max is still more efficient than the AMD Threadripper 3990X, and again, the AMD Threadripper 3990X TDP is for just a CPU, not an entire SoC!
Assuming near-linear scaling, a hypothetical M1-derived variant that is scaled up 4.5 times to a 270 W TDP should be able to handily defeat the AMD Threadripper 3990X in absolute performance.&lt;/p&gt;

&lt;p&gt;The wider takeaway here though is that in order to give the M1 Max some real competition, one has to skip laptop chips entirely and reach for not just high end desktop chips, but for server-class workstation hardware to really beat the M1 Max.
For workloads that push the CPU to maximum utilization for sustained periods of time, such as production-quality path traced rendering, the M1 Max represents a fundamental shift in what is possible in a laptop form factor.
Something even more exciting to think about is how the M1 Max really is the &lt;em&gt;middle&lt;/em&gt; tier Apple Silicon solution; presumably the large iMac and Mac Pro will push things into even more absurd territory.&lt;/p&gt;

&lt;div id="2021-10-25-conclusion"&gt;&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;So those are my initial thoughts on the Apple M1 Max chip and my initial experiences with getting my hobby renderer up and running on the 2021 14-inch MacBook Pro.
I’m extremely impressed, and not just with the chip!
This post mostly focused on the chip itself, but the rest of the 2021 MacBook Pro lineup is just as impressive.
For rendering professionals and enthusiasts alike, one aspect of the 2021 MacBook Pros that will likely be just as important as the processor is the incredible screen.
The 2021 MacBook Pros ship with what I believe is an industry first: a micro-LED backlit 120 Hz display with an extended dynamic range that can go up to 1600 nits peak brightness.
The screen is absolutely gorgeous, which is a must for anyone who spends their time generating pixels with a 3D renderer!
One thing on my to-do list was to add extended dynamic range support to &lt;a href="https://tom94.net"&gt;Thomas Müller&lt;/a&gt;’s excellent &lt;a href="https://github.com/Tom94/tev"&gt;tev image viewer&lt;/a&gt;, which is a popular tool in the rendering research community.
However, it turns out that Thomas already added extended dynamic range support, and it looks amazing on the 2021 MacBook Pro’s XDR display.&lt;/p&gt;

&lt;p&gt;In this post I didn’t go into the M1 Max’s GPU at all, even though the GPU in many ways might actually be even more interesting than the CPU (which is saying a lot considering how interesting the CPU is).
On paper at least, the M1 Max’s GPU aims for roughly mobile NVIDIA GeForce RTX 3070 performance, but how the M1 Max and a mobile NVIDIA GeForce RTX 3070 actually will compare for ray traced rendering is difficult to say without actually conducting some tests.
On one hand, the M1 Max’s unified memory architecture grants its GPU far more memory than any NVIDIA mobile GPU by a huge margin, and the M1 Max’s unified memory architecture opens up a wide variety of interesting optimizations that are otherwise difficult to do when managing separate pools of CPU and GPU memory.
On the other hand though, the M1 Max’s GPU lacks the dedicated hardware ray tracing acceleration that modern NVIDIA and AMD GPUs and the upcoming Intel discrete GPUs all have, and in my experience so far, dedicated hardware ray tracing acceleration makes a huge difference in GPU ray tracing performance.
Maybe Apple will add hardware ray tracing acceleration in the future; Metal already has software ray tracing APIs, and there already is a precedent for Apple Silicon including dedicated hardware for accelerating relatively niche, specific professional workflows.
As an example, the M1 Pro and M1 Max include hardware ProRes acceleration for high-end video editing.
Over the next year, I am undertaking a large-scale effort to port the entirety of Takua Renderer to work on GPUs through CUDA on NVIDIA GPUs, and through Metal on Apple Silicon devices.
Even though I’ve just gotten started on this project, I’ve already learned a lot of interesting things comparing CUDA and Metal compute; I’ll have much more to say on the topic hopefully soon!&lt;/p&gt;

&lt;p&gt;Beyond the CPU and GPU and screen, there are still even more other nice features that the new MacBook Pros have for professional workflows like high-end rendering, but I’ll skip going through them in this post since I’m sure they’ll be thoroughly covered by all of the various actual tech reviewers out on the internet.&lt;/p&gt;

&lt;div id="2021-10-25-bonus-images-and-acknowledgements"&gt;&lt;/div&gt;
&lt;h2 id="bonus-images-and-acknowledgements"&gt;Bonus Images and Acknowledgements&lt;/h2&gt;

&lt;p&gt;To conclude for now, here are two more bonus images that I rendered on the M1 Max 14-inch MacBook Pro.
I originally planned on just rendering the earlier three images in this post, but to my surprise, I found that I had enough time to do a few more!
I think that kind of encapsulates the M1 Pro and M1 Max MacBook Pros in a nutshell: I expected incredible performance, but was surprised to find even my high expectations met and surpassed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/forest.cam4.0.jpg"&gt;&lt;img alt="Figure 6: A mossy log, ferns, and debris on the forest floor. Rendered using Takua Renderer on a M1 Max 14-inch MacBook Pro. Click through for full 4K version." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/preview/forest.cam4.0.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/forest.cam5.0.jpg"&gt;&lt;img alt="Figure 7: Sunlight transmitting through pine leaves in the forest canopy. Rendered using Takua Renderer on a M1 Max 14-inch MacBook Pro. Click through for full 4K version." src="https://blog.yiningkarlli.com/content/images/2021/Oct/takua-on-m1-max/preview/forest.cam5.0.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A huge thanks to everyone at Apple that made this post possible!
Also a big thanks to Rajesh Sharma and Mark Lee for catching typos and making some good suggestions.&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Mon, 25 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/10/takua-on-m1-max.html</guid></item><item><title>Why Robots Deserve Rights</title><link>https://nonhuman.party/post/why_give_rights_to_robots/</link><description>Upon introducing the Non-Human Party to excited fans of democracy, a common question is what it really means to give rights to Tamagotchis; robots; or IoT toasters − &amp;ldquo;they can&amp;rsquo;t vote!</description><author>Non-Human Party</author><pubDate>Sun, 24 Oct 2021 17:58:08 GMT</pubDate><guid isPermaLink="true">https://nonhuman.party/post/why_give_rights_to_robots/</guid></item><item><title>Reading books</title><link>https://cobbaut.blogspot.com/2021/10/reading-books.html</link><description>&lt;p&gt;This post is meant to encourage me to &lt;a href="http://cobbaut.blogspot.com/search/label/books" target="_blank"&gt;read&lt;/a&gt; a bit more (paper books). By the way, I thought I was reading four books simultaneously, but when I put them next to each other it turned out to be seven.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNdkj7XU-nbqraHK2Q1zho6GsbKW7F9UnBjKjGtY3SPPQvZsq7eVxzP1FoQ5qM11mp3z-iV9l0UlL6mJTRimJ3wxWqdDOHxR_EQf5HbsaOaVRhLSNaZetOnMco0ClIsRG8KAir/s1600/sevenbooks_20211023.jpg" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNdkj7XU-nbqraHK2Q1zho6GsbKW7F9UnBjKjGtY3SPPQvZsq7eVxzP1FoQ5qM11mp3z-iV9l0UlL6mJTRimJ3wxWqdDOHxR_EQf5HbsaOaVRhLSNaZetOnMco0ClIsRG8KAir/w400-h86/sevenbooks_20211023.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Form left to right (writer-title(year) pages read-total pages):&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: courier;"&gt;Daniele Benedettelli - Creating Cool Mindstorms NXT Robots(2008) 24-575&lt;br /&gt;Leo Tolstoj - Oorlog en Vrede(1869, NL translation 1973) 115-462&lt;br /&gt;Dirk De Wachter - De kunst van het ongelukkig zijn(2019) 35-101&lt;br /&gt;Allen/Fonagy/Bateman - Mentaliseren(2008/2019 edition) 30-368&lt;br /&gt;LEGO and philosophy(2017) 56-226&lt;br /&gt;Charlie Mackesy - The Boy, the mole, the fox and the horse(2019)&lt;br /&gt;Michael Collins - Carrying the Fire(1974) - finished&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Personal goal: finish at least three more of these before 2022.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I just finished &lt;b&gt;Michael Collins - Carrying the Fire&lt;/b&gt; and it took me five weeks, which I consider a bit too long for a 470-ish pages book. It was a very good book though. If you're into space travel, then I would definitely recommend it. It also proves how the Sixties was vastly different to today, for example of &lt;a href="https://en.wikipedia.org/wiki/NASA_Astronaut_Group_3" target="_blank"&gt;the fourteen&lt;/a&gt; astronauts selected in 1963, four died during training. Two of &lt;a href="https://en.wikipedia.org/wiki/NASA_Astronaut_Group_2" target="_blank"&gt;the nine&lt;/a&gt; also died. Such numbers are unacceptable in 2021, even for 'dangerous' jobs.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The &lt;b&gt;Daniele Benedettelli&lt;/b&gt; book is about programming &lt;a href="https://en.wikipedia.org/wiki/Finite-state_machine" target="_blank"&gt;Finite State Machines&lt;/a&gt; using Lego robots. I don't know much about programming, but this looks like fun. Thing is I need to build some Lego robots (like &lt;a href="https://www.youtube.com/watch?v=bs-xb1P-MNY" target="_blank"&gt;this one&lt;/a&gt;) to continue this book.&lt;/p&gt;&lt;p&gt;And I probably need to start from page 1 again in &lt;a href="https://en.wikipedia.org/wiki/War_and_Peace" target="_blank"&gt;War and Peace&lt;/a&gt; because of the many characters that I forgot.&lt;/p&gt;&lt;p&gt;Some other books that I read the past three years are:&lt;/p&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-family: courier;"&gt;Celestin-Westreich/Celestin - Observeren en Rapporteren&lt;br /&gt;Dick Swaab - Ons creatieve brein&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-family: courier;"&gt;Dirk De Wachter - Borderline Times&lt;br /&gt;Dirk De Wachter - De wereld van De Wachter&lt;br /&gt;Etienne Vermeersch - Over God&lt;br /&gt;Etienne Vermeersch - Provencaalse gesprekken&lt;br /&gt;Jan Van de Craats - Basisboek wiskunde&lt;br /&gt;Jude Woodward - The US vs China&lt;br /&gt;Paul Verhaeghe - Autoriteit&lt;br /&gt;Paul Verhaeghe - Identiteit&lt;br /&gt;&lt;a href="https://xkcd.com/" target="_blank"&gt;Randall Munroe&lt;/a&gt; - Thing Explainer&lt;br /&gt;Randall Munroe - What If&lt;br /&gt;Rebecca Smethurst - Space, 10 things you should now&lt;br /&gt;Robert Bly - De Wildeman&lt;br /&gt;Terry Goodkind - Law of Nines&lt;br /&gt;Terry Goodkind - Severed Souls&lt;br /&gt;Terry Goodkind - The first Confessor&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier;"&gt;&lt;span style="font-family: courier;"&gt;Terry Goodkind - The Omen Machine&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-family: courier;"&gt;&lt;span style="font-family: courier;"&gt;&lt;span&gt;Terry Goodkind - The Third Kingdom&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Terry Goodkind - Warheart&lt;br /&gt;Thomas D'ansembourg - Stop met aardig zijn&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;Most of it non-fiction apparently. I really enjoyed 'Borderline Times' and both books of Paul Verhaeghe and Dick Swaab. I couldn't really get into Robert Bly or Thomas D'Ansembourg (but &lt;a href="https://en.wikipedia.org/wiki/Nonviolent_Communication" rel="nofollow"&gt;collaborative communication&lt;/a&gt; gives me a lot of insight in people).&lt;br /&gt;&lt;/p&gt;</description><author>Paul Cobbaut's blog</author><pubDate>Sun, 24 Oct 2021 17:11:59 GMT</pubDate><guid isPermaLink="true">https://cobbaut.blogspot.com/2021/10/reading-books.html</guid></item><item><title>Scandinavias Biggest Rapper – Einár</title><link>https://blog.vfiles.no/posts/scandinavias-biggest-rapper-einar/</link><description>&lt;p&gt;Nils Erik Einar Grönberg AKA. &lt;strong&gt;Einár&lt;/strong&gt; was a Swedish rapper. The 19 year old rap sensation was shot to death late Thursday night October 21, 2021. Einàr was the most played artist on &lt;a href="https://en.wikipedia.org/wiki/Spotify"&gt;Spotify&lt;/a&gt; in Sweden 2019. I discovered his music in the early 2019 with the track &lt;a href="https://sv.wikipedia.org/wiki/Katten_i_trakten"&gt;&amp;ldquo;Katten i trakten&amp;rdquo;&lt;/a&gt; on repeat. The track was on his debut album &lt;a href="https://sv.wikipedia.org/wiki/F%C3%B6rsta_klass_%28musikalbum%29"&gt;&lt;em&gt;Forsta klass&lt;/em&gt;&lt;/a&gt; that topped the Swedish charts in 2019, without the need of any &lt;a href="https://en.wikipedia.org/wiki/Record_label#Major_labels"&gt;(major) record labels&lt;/a&gt; help.&lt;/p&gt;</description><author>Posts on A blog</author><pubDate>Sun, 24 Oct 2021 14:16:37 GMT</pubDate><guid isPermaLink="true">https://blog.vfiles.no/posts/scandinavias-biggest-rapper-einar/</guid></item><item><title>What's on My Desk?</title><link>https://www.morling.dev/blog/whats-on-my-desk/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_the_screen"&gt;The Screen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_compute"&gt;Compute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_cameras"&gt;Cameras&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_work_in_progress_teleprompter"&gt;Work in Progress: Teleprompter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_lighting"&gt;Lighting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_audio"&gt;Audio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_whats_next"&gt;What’s Next?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="imageblock"&gt;
&lt;div class="content"&gt;
&lt;img alt="desk complete" src="https://www.morling.dev/images/desk_complete.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;I’ve been working from home exclusively for the last nine years,
but it was only last year that I started to look into ways for expanding my computer set-up and go beyond the usual combination of having a laptop with your regular external screen.
The global COVID-19 pandemic, the prospect of having more calls with colleagues than ever (no physical meetings), and the constantly increasing need for recording talks for online conferences and meet-ups made me reevaluate things and steadily improve and fine tune my set-up, in particular in regards to better video and audio quality.&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Sun, 24 Oct 2021 11:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/whats-on-my-desk/</guid></item><item><title>ARM on the desktop is here to stay</title><link>https://jasoneckert.github.io/myblog/arm-on-the-desktop/</link><description>&lt;p&gt;&lt;img alt="ARM Powered logo" src="arm.png#right" title="ARM Powered logo" /&gt;&lt;/p&gt;
&lt;p&gt;Back in the 1990s, &lt;em&gt;&lt;strong&gt;Reduced Instruction Set Computing (RISC)&lt;/strong&gt;&lt;/em&gt; architecture was widely seen as the future. UNIX workstations from Silicon Graphics and Sun Microsystems boasted powerful RISC CPUs at the time, but were prohibitively expensive and died out in the early 2000s as a result. Only the &lt;em&gt;&lt;strong&gt;Acorn RISC Machine (ARM)&lt;/strong&gt;&lt;/em&gt; architecture used in low-power devices (e.g. PDAs) survived past the early 2000s, and only because of the rise of mobile devices that relied on battery power.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Sun, 24 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/arm-on-the-desktop/</guid></item><item><title>Exploring PL/pgSQL: Strings, arrays, recursion, and parsing JSON</title><link>http://notes.eatonphil.com/exploring-plpgsql.html</link><description>&lt;p class="note"&gt;
  Next in exploring PL/pgSQL:
  &lt;br /&gt;
  &lt;a href="exploring-plpgsql-forth-like.html"&gt;Implementing a Forth-like interpreter&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;PostgreSQL comes with a builtin imperative programming language called
PL/pgSQL. I used to think this language was scary because it has a bit
more adornment than your usual language does. But looking deeper, it's
actually reasonably pleasant to program in.&lt;/p&gt;
&lt;p&gt;In this post we'll get familiar with it by working with strings,
arrays and recursive functions. We'll top it all off by building a
parser for a subset of JSON (no nested objects, no arrays, no unicode,
no decimals).&lt;/p&gt;
&lt;p&gt;The goal here is not production-quality code (an amazing JSON library
is already built into PostgreSQL) but simply to get more familiar with
the PL/pgSQL language.&lt;/p&gt;
&lt;p&gt;All code for this post is available on &lt;a href="https://github.com/eatonphil/exploring-plpgsql"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="creating-functions"&gt;Creating functions&lt;/h3&gt;&lt;p&gt;Functions are declared like tables. Here's a very simple one that
returns the length of a string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's not a very useful function because &lt;code&gt;length&lt;/code&gt; already exists but
the point is to see a basic custom function.&lt;/p&gt;
&lt;p&gt;All statements in PL/pgSQL must end in a semicolon. Arguments do not
have to be named. If they are not named they get default names of &lt;code&gt;$1&lt;/code&gt;
to &lt;code&gt;$N&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="named/unnamed-arguments"&gt;Named/unnamed arguments&lt;/h4&gt;&lt;p&gt;Here's how the function could be written without named arguments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="out-declarations"&gt;Out declarations&lt;/h4&gt;&lt;p&gt;PL/pgSQL also allows you to declare which variables will be returned
in the function argument list. They call it OUT parameters but as far
as I can tell it is not like OUT parameters in C# where you are
modifying the value of a variable in an external scope.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is still equivalent to the first function and is basically a shortcut for:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Whether you declare OUT or not you still must include &lt;code&gt;RETURNS &amp;lt;type&amp;gt;&lt;/code&gt;
in the function signature otherwise even if you call &lt;code&gt;RETURN&lt;/code&gt; in the
body, the result will just be ignored.&lt;/p&gt;
&lt;p&gt;Don't worry about case sensitivity too much. It's really only
important, as in typical SQL, for mixed-case table and column
names. But we won't be dealing with that situation in this article
focused on programming PL/pgSQL.&lt;/p&gt;
&lt;h4 id="testing-it-out"&gt;Testing it out&lt;/h4&gt;&lt;p&gt;Once the function is created, you can call it like &lt;code&gt;SELECT
slength('foo');&lt;/code&gt;. So here's a helper script to load a SQL file and run
a command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;./test.sh
sudo&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;postgres&lt;span class="w"&gt; &lt;/span&gt;psql&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%s;\n%s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;+x&lt;span class="w"&gt; &lt;/span&gt;./test.sh
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After storing the above &lt;code&gt;slength&lt;/code&gt; code in &lt;code&gt;slength.sql&lt;/code&gt; we can run a test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;slength&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;SELECT slength('foo')&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slength&lt;/span&gt;
&lt;span class="c1"&gt;---------&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Easy!&lt;/p&gt;
&lt;h3 id="numbers-and-recursion"&gt;Numbers and recursion&lt;/h3&gt;&lt;p&gt;Ok now that we've got the basics of function definition down and a way
to test the code, let's write a fibonacci program.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;./fib.sql
CREATE&lt;span class="w"&gt; &lt;/span&gt;OR&lt;span class="w"&gt; &lt;/span&gt;REPLACE&lt;span class="w"&gt; &lt;/span&gt;FUNCTION&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;i&lt;span class="w"&gt; &lt;/span&gt;int&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RETURNS&lt;span class="w"&gt; &lt;/span&gt;int&lt;span class="w"&gt; &lt;/span&gt;AS&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;
BEGIN
&lt;span class="w"&gt;  &lt;/span&gt;IF&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;OR&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;THEN
&lt;span class="w"&gt;    &lt;/span&gt;RETURN&lt;span class="w"&gt; &lt;/span&gt;i&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;END&lt;span class="w"&gt; &lt;/span&gt;IF&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;RETURN&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;i&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;+&lt;span class="w"&gt; &lt;/span&gt;fib&lt;span class="o"&gt;(&lt;/span&gt;i&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
END&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;LANGUAGE&lt;span class="w"&gt; &lt;/span&gt;plpgsql&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Everything in the if test is normal SQL WHERE clause syntax. This
makes it very easy for folks familiar with SQL to pick up conditionals
in PL/pgSQL.&lt;/p&gt;
&lt;p&gt;And there's no special syntax to allow function recursion. Nice!&lt;/p&gt;
&lt;p&gt;Run and test this function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./fib.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT fib(10)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;fib
-----
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;55&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Getting the hang of it?&lt;/p&gt;
&lt;h3 id="strings-and-arrays"&gt;Strings and arrays&lt;/h3&gt;&lt;p&gt;You may have noticed that &lt;code&gt;length&lt;/code&gt; used in &lt;code&gt;slength&lt;/code&gt; is a builtin
PostgreSQL function for dealing with strings. All builtin functions in
PostgreSQL can be used in PL/pgSQL.&lt;/p&gt;
&lt;p&gt;In order to get familiar with using arrays in PL/pgSQL let's write a
&lt;code&gt;string_to_array&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;string_to_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;string_to_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is one way to do it by modify array values directly by index. We
need to coalesce because calling &lt;code&gt;array_length&lt;/code&gt; on an empty array
returns &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another way to do this is by calling the builtin function &lt;code&gt;array_append&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;string_to_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can test and run both:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./string_to_array.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT string_to_array('foo')&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;string_to_array
-----------------
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;f,o,o&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./string_to_array2.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT string_to_array('foo')&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;string_to_array
-----------------
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;f,o,o&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course the builtin alternative might be &lt;code&gt;SELECT
regexp_split_to_array('foo')&lt;/code&gt; but we need the practice.&lt;/p&gt;
&lt;h3 id="custom-compound-types"&gt;Custom compound types&lt;/h3&gt;&lt;p&gt;If we're going to lex and parse JSON, we're going to want to return an
array of tokens from the lexer. A token will need to contain the type
(e.g. number, string, syntax) and the string value of the token
(e.g. &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;{&lt;/code&gt;, &lt;code&gt;my great key&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;PostgreSQL allows us to create compound types that we can then use as
the base of an array:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;json_token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CASCADE&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;json_token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;,
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;text&lt;/span&gt;
&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We need to add &lt;code&gt;CASCADE&lt;/code&gt; here because functions will have this type in
their signature and it otherwise makes PostgreSQL unhappy to delete
the type used in a function before deleting the function.&lt;/p&gt;
&lt;p&gt;We can create literals of this type like &lt;code&gt;SELECT ('number',
'12')::json_token)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now we're ready to build out the lexer.&lt;/p&gt;
&lt;h3 id="lexing"&gt;Lexing&lt;/h3&gt;&lt;p&gt;The lexers job is to clump together groups of characters into tokens.&lt;/p&gt;
&lt;p&gt;I'm going to describe this function in literate code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This function takes a string in and returns an array of json tokens.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Index in loop&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Current character in loop&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Current accumulated characters&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We need to declare all variables up front.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The main loop just looks at all characters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- Handle syntax characters&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'syntax'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First we look if the character is a syntax character. If it is we
append it to the array of tokens, increment the index, and go back to
the start of the main loop.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- Handle whitespace&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regexp_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'^\s+'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we check for whitespace characters. If replacing all whitespace
characters returns an empty string then we know it's whitespace. We
could also have done something like &lt;code&gt;IF c = ' ' OR c = '\n'
... THEN&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;Same as before though if we find whitespace characters we move on
(don't accumulate them) and restart the main loop.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- Handle strings&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we loop through any strings we find and accumulate them as tokens
before restarting the main loop.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- Handle numbers&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'^[0-9]+$'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'number'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we look for integers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Unknown character: %, at index: %; already found: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lastly if none of those lexing handlers match, we give up! Then the
loop is done and the function is too.&lt;/p&gt;
&lt;p&gt;There's no &lt;code&gt;RETURN&lt;/code&gt; statement because we already declared an &lt;code&gt;OUT&lt;/code&gt;
variable.&lt;/p&gt;
&lt;p&gt;If we test and run this now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./json.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT json_lex('{\&amp;quot;flubberty\&amp;quot;: 12, \&amp;quot;nice\&amp;quot;: \&amp;quot;a\&amp;quot;}')&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                                                                &lt;/span&gt;json_lex
----------------------------------------------------------------------------------------------------------------------------------------
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;(syntax,{)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(string,flubberty)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(syntax,:)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(number,12)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(syntax,\&amp;quot;,\&amp;quot;)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(string,nice)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(syntax,:)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(string,a)&amp;quot;&lt;/span&gt;,&lt;span class="s2"&gt;&amp;quot;(syntax,})&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's messy but it worked! Now on to parsing.&lt;/p&gt;
&lt;h3 id="parsing"&gt;Parsing&lt;/h3&gt;&lt;p&gt;Our parser will only accept JSON objects. JSON objects will be defined
as an array of key-value pairs. Custom types make this nice again.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One thing PostgreSQL does not make nice is sum types or parametric
types. But even if the value here is stored as text it can be easily
cast to a number by the user. And again, we're not going to support
nested objects/arrays. But using &lt;code&gt;hstore&lt;/code&gt; for key-values might be the
better alternative if we wanted to build a real JSON parser.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- Current token in tokens loop&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'syntax'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Invalid JSON, must be an object, got: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First up in the parser is variable declarations and validating that
this list of tokens represents a JSON object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;WHILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'syntax'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'syntax'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'JSON key-value pair must be followed by a comma or closing brace, got: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we loop to find each key-value pair. If one has already been
found, we need to find a comma before the next pair.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'JSON object must start with string key, got: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'syntax'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'JSON object must start with string key followed by colon, got: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'number'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;CONTINUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Invalid key-value pair syntax, got: %.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we just look for the key, colon, value syntax and fail if we
don't see it. And that's it! Very simple when not dealing with arrays
and nested objects.&lt;/p&gt;
&lt;h3 id="helpers"&gt;Helpers&lt;/h3&gt;&lt;p&gt;Lastly it would just be nice to have a single function that calls lex and parse:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And another function to look up a value in a parsed object by key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json_key_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;FOREACH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ARRAY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kvs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json_token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LOOP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Key not found.'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we're all set!&lt;/p&gt;
&lt;h3 id="testing"&gt;Testing&lt;/h3&gt;&lt;p&gt;Let's try some bad syntax (missing a comma between pairs):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./json.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT json_get(json_from_string('{\&amp;quot;flubberty\&amp;quot;: 12 \&amp;quot;nice\&amp;quot;: \&amp;quot;a\&amp;quot;}'), 'ipo')&amp;quot;&lt;/span&gt;
ERROR:&lt;span class="w"&gt;  &lt;/span&gt;JSON&lt;span class="w"&gt; &lt;/span&gt;key-value&lt;span class="w"&gt; &lt;/span&gt;pair&lt;span class="w"&gt; &lt;/span&gt;must&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;followed&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;comma&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;closing&lt;span class="w"&gt; &lt;/span&gt;brace,&lt;span class="w"&gt; &lt;/span&gt;got:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;string,nice&lt;span class="o"&gt;)&lt;/span&gt;.
CONTEXT:&lt;span class="w"&gt;  &lt;/span&gt;PL/pgSQL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;json_parse&lt;span class="o"&gt;(&lt;/span&gt;json_token&lt;span class="o"&gt;[]&lt;/span&gt;,integer&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;RAISE
PL/pgSQL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;json_from_string&lt;span class="o"&gt;(&lt;/span&gt;text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;RETURN
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sweet, it fails correctly.&lt;/p&gt;
&lt;p&gt;Now correct syntax but missing key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./json.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT json_get(json_from_string('{\&amp;quot;flubberty\&amp;quot;: 12, \&amp;quot;nice\&amp;quot;: \&amp;quot;a\&amp;quot;}'), 'ipo')&amp;quot;&lt;/span&gt;
ERROR:&lt;span class="w"&gt;  &lt;/span&gt;Key&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;found.
CONTEXT:&lt;span class="w"&gt;  &lt;/span&gt;PL/pgSQL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;json_get&lt;span class="o"&gt;(&lt;/span&gt;json_key_value&lt;span class="o"&gt;[]&lt;/span&gt;,text&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;RAISE
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally, correct syntax and existing key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./test.sh&lt;span class="w"&gt; &lt;/span&gt;./json.sql&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SELECT json_get(json_from_string('{\&amp;quot;flubberty\&amp;quot;: 12, \&amp;quot;nice\&amp;quot;: \&amp;quot;a\&amp;quot;}'), 'flubberty')&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;json_get
----------
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Huzzah! Now hopefully PL/pgSQL is a little less scary to you, whether
or not you decide to use it.&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;For everyone dying to write imperative code in PostgreSQL, I wrote a post about PL/pgSQL 👽 It starts with implementing simple string and array functions, to recursive Fibonacci, to a small JSON parsing library. A nice little language with a great stdlib!&lt;a href="https://t.co/m4Tff99N6R"&gt;https://t.co/m4Tff99N6R&lt;/a&gt; &lt;a href="https://t.co/2ZMJn2foNa"&gt;pic.twitter.com/2ZMJn2foNa&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1452339113131139072?ref_src=twsrc%5Etfw"&gt;October 24, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Sun, 24 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/exploring-plpgsql.html</guid></item><item><title>Models of Programming</title><link>https://lambdaland.org/posts/2021-09-25_models_of_programming_draft2/</link><description>&lt;p&gt;+++&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Last week I was studying outside of a lecture hall where someone was teaching an introductory course on computer programming. There was a lot that I overheard that I disagreed with; this essay is an attempt to help me crystallize what exactly I disagreed with.&lt;/em&gt;&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sun, 24 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-09-25_models_of_programming_draft2/</guid></item><item><title>Entropy in Data Science</title><link>https://bytepawn.com/entropy-in-data-science.html</link><description>&lt;p&gt;I discuss 4 uses of entropy in Data Science: (i) cross entropy as a loss function for training neural network classifiers (ii) entropy as a splitting criterion for building decision trees (iii) entropy for evaluating clustering algorithms (iv) entropy for understanding relationships in tabular data. &lt;br /&gt;&lt;br /&gt;&lt;img alt="Information gain" src="/images/decision_tree_split.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 24 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/entropy-in-data-science.html</guid></item><item><title>RAII Footguns in Rust and C++</title><link>https://nikhilism.com/post/2021/raii-footguns-rust-cpp/</link><description>I&amp;rsquo;ve been getting back into C++ at Skydio, and I&amp;rsquo;ve twice lost several hours to debugging weird code behavior because of an RAII footgun in the language. A similar footgun is present in Rust, and I&amp;rsquo;ve been bitten by that too, so I figured I&amp;rsquo;d write down both.
RAII classes are often used to keep resources alive or hold locks for a given scope. There observable side-effects usually occur only in the constructor and destructor.</description><author>Nikhil's blog</author><pubDate>Sat, 23 Oct 2021 20:06:14 GMT</pubDate><guid isPermaLink="true">https://nikhilism.com/post/2021/raii-footguns-rust-cpp/</guid></item><item><title>CCXT Python Tutorial (2026): Fetch OHLCV, Ticker &amp;amp; Order Book Data (With Examples)</title><link>https://blog.adnansiddiqi.me/getting-started-with-ccxt-crypto-exchange-library-and-python/</link><description>&lt;p&gt;Last updated: April 2026 Looking for a CCXT Python tutorial with real examples? In this guide, you’ll learn how to use CCXT to fetch OHLCV data, ticker information, and order book data using simple Python code. I already used the CCXT Library in my Airflow-related post here. In this post, I will discuss the library and how you can use it to pull different types of data from exchanges or trading automation. The demo can be seen here. What You’ll Learn Fetch OHLCV (candlestick) data Get real-time ticker prices Access order book data Connect to Binance using Python What is CCXT CryptoCurrency eXchange Trading Library, aka CCXT, is a JavaScript / Python / PHP library for cryptocurrency trading and e-commerce with support for many bitcoin/ether/altcoin exchange markets and merchant APIs. It connects with more than 100 exchanges. One of the best features of this library is that it is exchange-agnostic: whether you use Binance or FTX, the signature of routines is the same. It is available in Python, JavaScript, and PHP. It supports both public and private APIs offered by different exchanges, but I am only going to discuss a few public API endpoints. Development Setup How to Install CCXT pip install ccxt &amp;#160; Alright, the library is installed, let&amp;#8217;s connect to an exchange. I am connecting to the Binance exchange for now. Connect to the exchange The very first and obvious step is to connect to the exchange first; it is pretty easy. # Connect binance binance = ccxt.binance() Loading Markets The exchange is connected; now it&amp;#8217;s time to load all markets offered by the exchange. Usually, it is not used but it can give you a good idea of whether a certain pair is available or not. Fetching Ticker Data Let&amp;#8217;s grab the info related to BTC/USDT. btc_ticker = binance.fetch_ticker('BTC/USDT') btc_ticker and it returns: {'symbol': 'BTC/USDT', 'timestamp': 1634909924391, 'datetime': '2021-10-22T13:38:44.391Z', 'high': 65564.0, 'low': 62000.0, 'bid': 63363.89, 'bidVolume': 1.32351, 'ask': 63363.9, 'askVolume': 0.27138, 'vwap': 63105.52714333, 'open': 65470.0, 'close': 63363.9, 'last': 63363.9, 'previousClose': 65469.99, 'change': -2106.1, 'percentage': -3.217, 'average': None, 'baseVolume': 53395.84423, 'quoteVolume': 3369572897.3972454, 'info': {'symbol': 'BTCUSDT', 'priceChange': '-2106.10000000', 'priceChangePercent': '-3.217', 'weightedAvgPrice': '63105.52714333', 'prevClosePrice': '65469.99000000', 'lastPrice': '63363.90000000', 'lastQty': '0.00843000', 'bidPrice': '63363.89000000', 'bidQty': '1.32351000', 'askPrice': '63363.90000000', 'askQty': '0.27138000', 'openPrice': '65470.00000000', 'highPrice': '65564.00000000', 'lowPrice': '62000.00000000', 'volume': '53395.84423000', 'quoteVolume': '3369572897.39724530', 'openTime': 1634823524391, 'closeTime': 1634909924391, 'firstId': 1109039843, 'lastId': 1110609081, 'count': 1569239}} Apart from Ticker and Timestamp, it returns data points like Open, High, Low, Close, Volume, Bid and Ask Price, etc. Fetching OHLCV data Fetching OHLCV data is pretty easy; all you have to do is: btc_usdt_ohlcv = binance.fetch_ohlcv('BTC/USDT','1d',limit=100) Here, the first argument is the trading pair, the second is duration, which tells whether you need day-level data, minutes, or a month. Each OHLCV entry represents a candlestick. Since the difference in time duration is a day, the data will always be in 24 hours of difference starting from 12 AM, Midnight UTC. The last parameter, limit  is optional. When you print it, the data is returned in the following format: [[1634774400000, 66001.4, 66639.74, 62000.0, 62193.15, 68538.64537], [1634860800000, 62193.15, 63732.39, 62000.0, 62680.01, 24059.82478]] The data returns in the format: TIMESTAMP, OPEN, HIGH, LOW, CLOSE, and VOLUME. Let&amp;#8217;s make it interesting. I will pull BTC/USDT price from Binance and FTX exchange and plot the graph of close values. How about Close vs Volume? Pretty dirty, right? As if a kid sketched something on paper, but then this is called volatility! Fetching Orderbook Now let&amp;#8217;s pull up the order book from Binance and FTX. orderbook_binance_btc_usdt = binance.fetch_order_book('BTC/USDT') orderbook_ftx_btc_usdt = ftx.fetch_order_book('BTC/USDT') bids_binance = orderbook_binance_btc_usdt['bids'] asks_binanace = orderbook_binance_btc_usdt['asks'] df_bid_binance = pd.DataFrame(bids_binance, columns=['price','qty']) df_ask_binance = pd.DataFrame(asks_binanace, columns=['price','qty']) Let&amp;#8217;s plot the graph! fig, (ax1, ax2) = plt.subplots(2, figsize=(10, 8), dpi=80) ax1.plot(df_bid_binance['price'], df_bid_binance['qty'],label='Binance',color='g') ax1.fill_between(df_bid_binance['price'], df_bid_binance['qty'],color='g') ax2.plot(df_ask_binance['price'], df_ask_binance['qty'],label='FTX',color='r') ax2.fill_between(df_bid_binance['price'], df_bid_binance['qty'],color='r') ax1.set_title('Ask Price vs Quantity for Binance') ax2.set_title('Bid Price vs Quantity for Binance') plt.show() And it produces&amp;#8230; &amp;#160; Cool, isn&amp;#8217;t it? Conclusion So, as you know now, CCXT is a powerful library for working with crypto exchange data in Python. It makes it easier to connect multiple exchanges without making any significant changes in the code and is widely used for trading bots and data analysis. This was a brief intro to the CCXT library. I just scratched the surface. If you are interested, you may create a trading bot based on this library. Several have done it before. Like always, the code is available on GitHub. If you like this post, then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/getting-started-with-ccxt-crypto-exchange-library-and-python/"&gt;CCXT Python Tutorial (2026): Fetch OHLCV, Ticker &amp; Order Book Data (With Examples)&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Sat, 23 Oct 2021 12:55:28 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/getting-started-with-ccxt-crypto-exchange-library-and-python/</guid></item><item><title>Help me share my NEXT million words</title><link>https://stop.zona-m.net/2021/10/funding-2021-2022/</link><description>&lt;p&gt;We live in a digital age. We&amp;rsquo;d better understand &lt;strong&gt;for real&lt;/strong&gt; how it works. All of us.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 23 Oct 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/funding-2021-2022/</guid></item><item><title>How to change visibility in massive GitHub organizations like EpicGames?</title><link>https://avestura.dev/blog/change-visibility-in-massive-github-orgs</link><description>How to publicize and un-publicize your visibility in massive GitHub orgs?</description><author>Avestura's Blog</author><pubDate>Sat, 23 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://avestura.dev/blog/change-visibility-in-massive-github-orgs</guid></item><item><title>Custom Metrics Reports on GitLab</title><link>https://tommasoamici.com/blog/custom-metrics-reports-on-gitlab/</link><description>GitLab allows you to record Metrics Reports and will display a report on merge requests so that it’s easier and faster to identify changes without having to check the entire log&lt;hr /&gt;&lt;a href="https://tommasoamici.com/blog/custom-metrics-reports-on-gitlab/"&gt;Read more&lt;/a&gt;</description><author>Tommaso Amici</author><pubDate>Fri, 22 Oct 2021 19:08:41 GMT</pubDate><guid isPermaLink="true">https://tommasoamici.com/blog/custom-metrics-reports-on-gitlab/</guid></item><item><title>TIL how to use Ansible-inventory for native ssh-connections</title><link>https://www.zufallsheld.de/2021/10/22/til-how-to-use-ansible-inventory-for-native-ssh-connections/</link><description>&lt;p&gt;Actually I did not learn this today but already some years ago. Sometimes I need to connect to remote servers that have no hosts-file or dns-entry. But I don&amp;#8217;t want to remember &lt;span class="caps"&gt;IP&lt;/span&gt;-addresses!&lt;/p&gt;
&lt;p&gt;Luckily I do have these servers defined in Ansible&amp;#8217;s hosts-file and I could theoretically …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Fri, 22 Oct 2021 16:23:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/10/22/til-how-to-use-ansible-inventory-for-native-ssh-connections/</guid></item><item><title>SSH Port Forwarding Guide</title><link>https://geo.rocks/post/ssh-port-forwarding-guide/</link><description>&lt;p&gt;&lt;img alt="Port forwarding" src="https://geo.rocks/img/port-forwarding.jpg" title="Port forwarding" /&gt;&lt;/p&gt;
&lt;p&gt;Setting up proper port forwarding for SSH connections, servers or just test purposes is crucial but isn&amp;rsquo;t as straight forward as your connection should be. This post aims to be a quick guide for different use cases.&lt;/p&gt;</description><author>Geography &amp;amp; Coding</author><pubDate>Fri, 22 Oct 2021 10:50:03 GMT</pubDate><guid isPermaLink="true">https://geo.rocks/post/ssh-port-forwarding-guide/</guid></item><item><title>Recharging a cell phone battery outside of the phone</title><link>https://voussoir.net/writing/motorola_c139</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a class="tag_link" href="/writing/tags/electronics"&gt;[electronics]&lt;/a&gt; &lt;a class="tag_link" href="/writing/tags/today_i_did_this"&gt;[today_i_did_this]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Recharging a cell phone battery outside of the phone&lt;/h1&gt;
&lt;p&gt;Here is a Motorola C139 cell phone from about 2008. I rediscovered it in a drawer last year.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/phone_1.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/phone_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/phone_2.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/phone_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The charger for this device has long been misplaced. It uses a barrel plug smaller than any I've ever seen, with an outer diameter of about 3mm.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/phone_3.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/phone_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I thought it would be fun to turn this old phone back on just for nostalgia's sake, but without the charger and not wanting to tear it open to access the charger pins, I just put it back in the drawer.&lt;/p&gt;
&lt;p&gt;Here is a TP4056 battery charger that I bought on &lt;a href="https://www.ebay.com/itm/113876620225"&gt;ebay&lt;/a&gt; a few weeks ago to explore another project idea.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/tp4056.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/tp4056.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While I was fiddling with it, I realized this might be just what I needed to get the Motorola working again.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/charging_1.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/charging_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/charging_2.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/charging_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And that worked! This thirteen year old battery is still able to take a charge.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/success_1.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/success_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/success_2.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/success_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/success_3.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/success_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The only problem now is that the phone is really boring to use and I have no purpose for it. Other than to play breakout.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/motorola_c139/success_4.jpg"&gt;&lt;img alt="" src="/writing/motorola_c139/thumbs/success_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;P.S. I accidentally fried this TP4056 shortly after taking these photos. I picked it up with the wires still dangling out of those pins and I think I shorted something. Smoke came out and everything. Luckily it came in a two-pack.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/motorola_c139/motorola_c139.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/ad8a56682eeacd1aef4a3b4144d33337a560808c"&gt;&lt;time datetime="2021-10-22T00:17:48-07:00"&gt;2021-10-22&lt;/time&gt; Add motorola_c139.md.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Fri, 22 Oct 2021 10:17:48 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/motorola_c139</guid></item><item><title>A steering wheel for fake autonomous cars</title><link>https://stop.zona-m.net/2021/10/a-steering-wheel-for-fake-autonomous-cars/</link><description>&lt;p&gt;Cool it&amp;rsquo;s cool. Driverless, not really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 22 Oct 2021 06:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/a-steering-wheel-for-fake-autonomous-cars/</guid></item><item><title>Shaking N-gram needles from large haystacks</title><link>https://www.marginalia.nu/log/31-ngram-needles/</link><description>&lt;p&gt;A recurring problem when searching for text is identifying which parts of the text are in some sense useful. A first order solution is to just extract every word from the text, and match documents against whether they contain those words. This works really well if you don&amp;rsquo;t have a lot of documents to search through, but as the corpus of documents grows, so does the number of matches.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to bucket the words based on where they appear in the document, but this is not something I&amp;rsquo;m doing at the moment and not something I will implement in the foreseeable future.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 22 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/31-ngram-needles/</guid></item><item><title>Removing invisible unread GitHub notifications</title><link>https://kinoshita.eti.br/2021/10/22/removing-invisible-unread-github-notifications.html</link><description>&lt;p&gt;Some months ago I noticed that even after I marked all my GitHub notifications
as read, the unread icon displayed at the right top corner was still showing as
if I had unread notifications.&lt;/p&gt;



&lt;figure class="feature"&gt;

 &lt;img alt="GitHub Notifications icon always-on mode" height="" src="https://kinoshita.eti.br/assets/posts/2021-10-22-removing-invisible-unread-github-notifications/notifications.png" title="GitHub Notifications icon always-on mode" width="" /&gt;
 &lt;figcaption&gt;GitHub Notifications icon always-on mode&lt;/figcaption&gt;

&lt;/figure&gt;


&lt;p&gt;I tried changing the filters, waiting for a new notification to appear so that
I could mark it as read, all hoping that icon would then change. But no matter
what I tried in the GitHub UI, the icon was still there.&lt;/p&gt;</description><author>About on kinow</author><pubDate>Fri, 22 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://kinoshita.eti.br/2021/10/22/removing-invisible-unread-github-notifications.html</guid></item><item><title>Censorship or... EFFICIENT slowness?</title><link>https://stop.zona-m.net/2021/10/censorship-or...-efficient-slowness/</link><description>&lt;p&gt;Let&amp;rsquo;s talk about it. Slowly, please.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 22 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/censorship-or...-efficient-slowness/</guid></item><item><title>Apple ∑Mn</title><link>https://blog.vfiles.no/posts/apple-m-1-2-3-4-5/</link><description>&lt;p&gt;Its October! That is also a synonym for a new &lt;a href="https://en.wikipedia.org/wiki/List_of_Apple_Inc._media_events#Apple_Event_%28October_18,_2021%29"&gt;Apple event&lt;/a&gt;, well it is actually either October or September if I have to be precise. This October was my attention wrapped around the new &lt;a href="https://en.wikipedia.org/wiki/System_on_a_chip"&gt;SoCs&lt;/a&gt; in Apple Silicons line-up, to be more precise the upgrade of Apple&amp;rsquo;s 2020 &lt;a href="https://en.wikipedia.org/wiki/Apple_M1"&gt;M1&lt;/a&gt; chip, &lt;a href="https://en.wikipedia.org/wiki/Apple_M1_Pro"&gt;Apple M1 Pro&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Apple_M1_Max"&gt;Apple M1 Max&lt;/a&gt;. Where Apple continues to make the naming conventions more confusing with direct connection to the names of &lt;a href="https://en.wikipedia.org/wiki/IPhone"&gt;iPhones&lt;/a&gt; models. So, to break it down for ordinary people &lt;code&gt;Pro&lt;/code&gt; is like iPhone 13 Pro and &lt;code&gt;Max&lt;/code&gt; is like iPhone 13 Pro Max but without the Pro part. It&amp;rsquo;s super easy to understand, right? Well its a bit more intricate than that. Apple has showed with the M1 chip that the transition over from &lt;a href="https://en.wikipedia.org/wiki/Apple%E2%80%93Intel_architecture"&gt;Intel-based x86&lt;/a&gt; CPUs to the own in-house designed microprocessors running on the &lt;a href="https://en.wikipedia.org/wiki/AArch64#ARMv8.4-A"&gt;Arm&lt;/a&gt; instruction set don&amp;rsquo;t need to be so problematic, do you &lt;a href="https://www.theverge.com/2020/6/10/21285866/mac-arm-processors-windows-lessons-transition-coexist"&gt;follow Microsoft&lt;/a&gt;? Apple M1 wasn&amp;rsquo;t that groundbreaking in terms of performance, but more of a unexpectedly good transition to Arm.&lt;/p&gt;</description><author>Posts on A blog</author><pubDate>Thu, 21 Oct 2021 15:01:42 GMT</pubDate><guid isPermaLink="true">https://blog.vfiles.no/posts/apple-m-1-2-3-4-5/</guid></item><item><title>Building an Emacs lisp VM in Rust</title><link>https://coredumped.dev/2021/10/21/building-an-emacs-lisp-vm-in-rust/</link><description>Updated: 2023-01-06
About a year ago I was bitten by the PL bug. It started with reading Crafting Interpreters and discovering the wonders hidden under the hood of a compiler. I am also been a big fan of Emacs, and this started to get me interested in how its interpreter works. At the same time, I was reading the Rust book and trying to understand the concepts there. This all came to a head, and I decided to write an Emacs Lisp interpreter called rune in Rust.</description><author>Core Dumped</author><pubDate>Thu, 21 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://coredumped.dev/2021/10/21/building-an-emacs-lisp-vm-in-rust/</guid></item><item><title>2021–10–21: Pinephone Pro – levinboot payload selection</title><link>https://xnux.eu/log/#049</link><author>megi's PinePhone Development Log</author><pubDate>Thu, 21 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#049</guid></item><item><title>Making a series outline</title><link>https://liza.io/making-a-series-outline/</link><description>&lt;p&gt;I thought my first series of novellas would revolve around the same world, but otherwise stand alone from each other.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Wed, 20 Oct 2021 20:02:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/making-a-series-outline/</guid></item><item><title>IVOW fights biases in artificial intelligence</title><link>https://stop.zona-m.net/2021/10/ivow-fights-biases-in-artificial-intelligence/</link><description>&lt;p&gt;Good, but maybe also another &amp;ldquo;way paved with good intentions&amp;rdquo;.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 20 Oct 2021 13:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/ivow-fights-biases-in-artificial-intelligence/</guid></item><item><title>I met with the wolves</title><link>https://nicolaiarocci.com/i-met-with-the-wolves/</link><description>&lt;p&gt;I sit under a wild apple tree at the edge of the clearing. Like a plant,
I absorb the mild October sun. At the same time, I attentively listen to the
sounds of the forest. Suddenly I hear a stomping of dry leaves about twenty
meters ahead of me, slightly to my right. A wolf emerges out of the thicket. He
stops for a moment, glances around, then starts crossing the clearing. A few
moments and another wolf appears. After a brief pause, he follows his pal. They
parade right in front of me, sinuous, silent, and feral. They do not see or
hear me, and yet I am right there, by their side, in plain sight&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. It is all
over in a few seconds. The couple leaves the clearing and disappears into the
forest. I stand sitting there, shocked and in complete awe.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 20 Oct 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/i-met-with-the-wolves/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #42</title><link>https://justingarrison.com/123dev/2021-10-20-123dev-42/</link><description>Welcome to 123dev from Revue. This is the first issue I migrated from Buttondown. You may need to adjust your mail filters because the sending address has changed.</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 20 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-20-123dev-42/</guid></item><item><title>Moving a MySQL/MariaDB database between servers using SSH</title><link>https://boyter.org/posts/move-mysql-database-between-servers/</link><description>&lt;p&gt;I&amp;rsquo;m in the middle of moving searchcode.com&amp;rsquo;s database to another server while upgrading it. This means I need to copy it somehow. It&amp;rsquo;s especially difficult in this case because the server its sitting on does not have enough disk space to do my usual &lt;code&gt;mysqldump&lt;/code&gt; then copy the file across using SCP.&lt;/p&gt;
&lt;p&gt;I investigated splitting the database using &lt;code&gt;--where&lt;/code&gt; clauses allowing me to get chunks of the database across, but that also means adding things like &lt;code&gt;--skip-create-options&lt;/code&gt; and &lt;code&gt;--skip-add-drop-table&lt;/code&gt; and I ended up having to redo it a few times, with dumps in between.&lt;/p&gt;</description><author>Ben E. C. Boyter</author><pubDate>Wed, 20 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/move-mysql-database-between-servers/</guid></item><item><title>Eager to know more about Einride pods</title><link>https://stop.zona-m.net/2021/10/eager-to-know-more-about-einride-pods/</link><description>&lt;p&gt;Some of my driverless visions keep getting closer. Maybe.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 20 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/eager-to-know-more-about-einride-pods/</guid></item><item><title>Syncing Mobile Photos with Photoprism</title><link>https://blog.randombits.host/syncing-mobile-photos-with-photoprism/</link><description>&lt;figure class="kg-card kg-image-card kg-card-hascaption"&gt;&lt;img alt="Photoprism Logo" class="kg-image" src="https://demo-cdn.photoprism.org/static/img/logo-avatar.svg" /&gt;&lt;figcaption&gt;Photoprism Logo&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href="https://demo.photoprism.org/?ref=blog.randombits.host"&gt;Photoprism&lt;/a&gt; is the self-hosted photo/video library that does it all for me. It has labeling of images so you can search by picture content, it has calendar and map features for more natural finding of the pictures you&amp;apos;re trying to find and it lets&lt;/p&gt;</description><author>Random Bits</author><pubDate>Wed, 20 Oct 2021 00:35:05 GMT</pubDate><guid isPermaLink="true">https://blog.randombits.host/syncing-mobile-photos-with-photoprism/</guid></item><item><title>Italia II - Firenze</title><link>https://faingezicht.com/photos/2021/10/19/florence/</link><description>When planning our trip, I was somewhat disappointed that we'd only spend a day in Florence. Knowing it's a place that I'll end up going back to on my own, I didn't try too hard to convince my friends to change our plans - realistically the villa thing won't happen again, but the museums will be waiting for me in a few years.

Next time, I'll spend a bit more than 12 hours there.

_This is the second in a series shared throughout Fall 2021. When you're done here, make sure to also check out parts &lt;a href="/photos/2021/10/14/umbria/"&gt;one&lt;/a&gt;, &lt;a href="/photos/2021/10/31/roma/"&gt;three&lt;/a&gt;, &lt;a href="/photos/2021/11/12/positano/"&gt;four&lt;/a&gt;, and &lt;a href="/photos/2021/12/02/italia-revisited/"&gt;five&lt;/a&gt;._</description><author>Avy Faingezicht</author><pubDate>Tue, 19 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/10/19/florence/</guid></item><item><title>Malay manuscripts show the right way to digitize culture</title><link>https://stop.zona-m.net/2021/10/malay-manuscripts-show-the-right-way-to-digitize-culture/</link><description>&lt;p&gt;First digitize, then TELL everybody.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 19 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/malay-manuscripts-show-the-right-way-to-digitize-culture/</guid></item><item><title>EVANGELION:3.0+1.01 Thrice Upon a Time</title><link>https://olshansky.info/movie/evangelion_3.0+1.01_thrice_upon_a_time/</link><description>Olshansky's review of EVANGELION:3.0+1.01 Thrice Upon a Time</description><author>🦉 olshansky 🦁</author><pubDate>Mon, 18 Oct 2021 20:46:26 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/evangelion_3.0+1.01_thrice_upon_a_time/</guid></item><item><title>No escorts allowed: How I fought spammers</title><link>https://gaganpreet.in/posts/build-content-moderation/</link><description>&lt;p&gt;Warning: This post features text that might not be safe for work.&lt;/p&gt;
&lt;h2 class="relative group" id="background"&gt;Background &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#background"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Incorporating anti-spam measures are the least of concerns while building a user generated content (UGC) platform. Unless you&amp;rsquo;re paranoid and spend precious time building moderation features in your platform, there will come a day when you sorely miss those tools. This is a story about exactly how this happened and how I dealt with it at Polarsteps.&lt;/p&gt;</description><author>Gaganpreet Arora</author><pubDate>Mon, 18 Oct 2021 17:28:37 GMT</pubDate><guid isPermaLink="true">https://gaganpreet.in/posts/build-content-moderation/</guid></item><item><title>Testing The Silk Platform - Hands-On Technical Analysis of High-Performance I/O in the Cloud</title><link>https://tanelpoder.com/posts/testing-the-silk-platform/</link><description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt; 3 years after writing this article, I &lt;a href="https://tanelpoder.com/posts/testing-the-silk-platform-in-2024/"&gt;tested the latest Silk Platform in 2024&lt;/a&gt;, on Google Cloud Platform, with impressive results. In my tests, I got to over 20 GiB/s with large reads, 1.3M IOPS with small 8kB random reads and 10 GiB/s sustained write rate! Both articles are worth a read.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hands-on technical analysis of a novel data platform for high-performance block I/O in the cloud&lt;/em&gt;,
&lt;br /&gt;
&lt;em&gt;tested by &lt;a href="https://tanelpoder.com/about/"&gt;Tanel Poder&lt;/a&gt;, a database consultant and a long-time computer performance geek.&lt;/em&gt;&lt;/p&gt;</description><author>Tanel Poder Blog</author><pubDate>Mon, 18 Oct 2021 13:30:00 GMT</pubDate><guid isPermaLink="true">https://tanelpoder.com/posts/testing-the-silk-platform/</guid></item><item><title>Executable JavaDoc Code Snippets</title><link>https://www.morling.dev/blog/executable-javadoc-code-snippets/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_including_snippets_from_your_test_directory"&gt;Including Snippets From Your Test Directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_summary"&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It has been just a few weeks since the &lt;a href="https://www.infoq.com/news/2021/09/java17-released/"&gt;release of Java 17&lt;/a&gt;, but the first changes scheduled for Java 18 begin to show up in early access builds.
One feature in particular that excites me as a maintainer of different Java libraries is &lt;a href="https://openjdk.java.net/jeps/413"&gt;JEP 413&lt;/a&gt; ("Code Snippets in Java API Documentation").&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Mon, 18 Oct 2021 09:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/executable-javadoc-code-snippets/</guid></item><item><title>Experience vs Theory: Lake Tahoe Edition</title><link>https://olshansky.info/posts/2021-10-17-experience-vs-theory-lake-tahoe-edition/</link><description>I remember thinking I had the foundation to tackle any problem that came my way around the time that I was graduating from College. Having…</description><author>🦉 olshansky 🦁</author><pubDate>Mon, 18 Oct 2021 03:36:12 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/posts/2021-10-17-experience-vs-theory-lake-tahoe-edition/</guid></item><item><title>Experimenting with column- and row-oriented datastructures</title><link>http://notes.eatonphil.com/experimenting-with-column-and-row-oriented-datastructures.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-10-18-experimenting-with-column-and-row-oriented-datastructures.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Mon, 18 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/experimenting-with-column-and-row-oriented-datastructures.html</guid></item><item><title>2021–10–18: Pinephone Pro – support merged into my kernel tree</title><link>https://xnux.eu/log/#048</link><author>megi's PinePhone Development Log</author><pubDate>Mon, 18 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#048</guid></item><item><title>100 articles</title><link>https://bytepawn.com/100-articles.html</link><description>&lt;p&gt;A review and introspect on the first 100 articles written on Bytepawn. &lt;br /&gt;&lt;br /&gt;&lt;img alt="100" src="/images/100.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Mon, 18 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/100-articles.html</guid></item><item><title>Mach Engine: The future of graphics (with Zig)</title><link>https://devlog.hexops.org/2021/mach-engine-the-future-of-graphics-with-zig/</link><description>&lt;p&gt;In the coming months, we&amp;rsquo;ll begin to have truly cross-platform low-level graphics, with the ability to cross compile GPU-accelerated applications written in Zig from any OS and deploy to desktop, mobile, and (in the future) web.&lt;/p&gt;
&lt;h2 id="mach-engine"&gt;Mach engine&lt;/h2&gt;
&lt;img alt="Mach: Game engine &amp;amp; graphics toolkit for the future" class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img1.png" /&gt;
&lt;p&gt;I&amp;rsquo;ve been working on &lt;a href="https://github.com/hexops/mach"&gt;Mach Engine&lt;/a&gt; for about 4 months now, although it as a project is many years in the making, and I believe in the next 4-6 months we&amp;rsquo;ll have completion of the first key milestone: truly cross platform graphics and seamless cross compilation.&lt;/p&gt;
&lt;h2 id="vision"&gt;Vision&lt;/h2&gt;
&lt;p&gt;Today, I share only the first milestone: Mach engine core. I&amp;rsquo;ve been working on this for around 1 year now, and we&amp;rsquo;re close (maybe 4-6 months away) from completion:&lt;/p&gt;
&lt;p&gt;&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img2.png"&gt;&lt;img alt="Zero fuss installation, out of the box cross compilation, and a truly cross-platform graphics API" class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img2.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="zero-fuss-installation--cross-compilation"&gt;Zero fuss installation &amp;amp; cross compilation&lt;/h2&gt;
&lt;p&gt;Only &lt;code&gt;zig&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; are needed to build from any OS and produce binaries for every OS. You do &lt;strong&gt;not&lt;/strong&gt; need any system dependencies, C libraries, SDKs (Xcode, etc.), C compilers or anything else.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re able to achieve this thanks to two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Zig has fantastic cross-compilation support, including its own custom linker &lt;code&gt;zld&lt;/code&gt; written by &lt;a href="http://www.jakubkonka.com/"&gt;Jakub Konka&lt;/a&gt; which is capable of supporting MacOS cross compilation.&lt;/li&gt;
&lt;li&gt;Mach doing the heavy lifting of packaging the required system SDK libraries and C sources for e.g. GLFW so our Zig build scripts can simply &lt;code&gt;git clone&lt;/code&gt; them for you as needed for the target OS you&amp;rsquo;re building for, completely automagically.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="truly-cross-platform-graphics-api"&gt;Truly cross-platform graphics API&lt;/h2&gt;
&lt;h3 id="directx-12-metal-vulkan--opengl"&gt;DirectX 12, Metal, Vulkan &amp;amp; OpenGL&lt;/h3&gt;
&lt;p&gt;Imagine a low-level, little to no overhead graphics API that unifies DirectX, Metal, Vulkan, and OpenGL (if no others are available):&lt;/p&gt;
&lt;p&gt;&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img3.png"&gt;&lt;img alt="Simple, low-level unified graphics API mapping to DirectX 12, Metal, Vulkan, and OpenGL" class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img3.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This isn&amp;rsquo;t anything new:&lt;/em&gt; all modern engines provide this, Godot has been working towards this for &lt;em&gt;years&lt;/em&gt; (and still is), and there exist abstraction layers for Vulkan over most of these APIs as well.&lt;/p&gt;
&lt;h3 id="vendor-support"&gt;Vendor support&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;An API is only as good as the momentum behind it.&lt;/strong&gt; What modern API can target the largest array of platforms with the most vendor backing?&lt;/p&gt;
&lt;p&gt;&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img4.png"&gt;&lt;img alt="Google to Vulkan, Microsoft to DirectX, Apple to Metal, AMD and NVidia to everything." class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img4.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Microsoft sees DirectX as the future, not Vulkan.&lt;/strong&gt; (DirectX 13 is coming by the end of 2022.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apple sees Metal as the future, not Vulkan.&lt;/strong&gt; OpenGL and OpenCL are deprecated, and private legal arguments with Khoronos make it unlikely we&amp;rsquo;ll ever see OpenGL or Vulkan on Apple hardware ever again.&lt;/li&gt;
&lt;li&gt;Google, with their Fuschia OS &lt;a href="https://fuchsia.dev/fuchsia-src/concepts/graphics/magma"&gt;appears to be primarily into Vulkan&lt;/a&gt; from a system-level POV.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NVIDIA, AMD, and Intel generally support as many graphics APIs as possible&lt;/strong&gt;, they want to sell hardware.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="one-api-that-apple-microsoft-and-google-can-all-agree-on"&gt;One API that Apple, Microsoft, and Google can all agree on&lt;/h3&gt;
&lt;p&gt;&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img5.png"&gt;&lt;img alt="Mozilla, Google, Microsoft, Apple, and Intel all to WebGPU" class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img5.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Outside the bounds of traditional graphics APIs there exists an attempt to provide a unified API across all platforms, &lt;a href="https://en.wikipedia.org/wiki/WebGPU"&gt;WebGPU&lt;/a&gt; (not to be confused with the much older &lt;em&gt;WebGL&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Mozilla, Google, Apple, and Microsoft all got together to build an abstraction layer over the modern graphics APIs - finding the common ground between Direct3D 12, Metal, and Vulkan - plus a safe way to expose that functionality in browsers.&lt;/p&gt;
&lt;p&gt;The name &lt;em&gt;WebGPU&lt;/em&gt; might lead you to believe that this is only for browsers, and that it may not be low-level or fast - but this really couldn&amp;rsquo;t be further from the truth.&lt;/p&gt;
&lt;h3 id="apple--googles-role-is-what-makes-webgpu-unique-and-why-we-chose-it"&gt;Apple &amp;amp; Google&amp;rsquo;s role is what makes WebGPU unique, and why we chose it&lt;/h3&gt;
&lt;p&gt;&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img6.png"&gt;&lt;img alt="Khronos group out of the piture in the future" class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img6.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What is new about WebGPU in my view is the vendors playing key roles in its development, and the fact that it grew outside the Khronos Group.&lt;/p&gt;
&lt;p&gt;Although abstraction layers over modern graphics APIs are nothing new - as Apple, Google, and Microsoft continue to get more into manufacturing their own hardware (it&amp;rsquo;s clear this is a strategic move for them) we should ask ourselves how this will change the landscape, and WebGPU is the first cross-vendor API to be produced by this new ecosystem.&lt;/p&gt;
&lt;h3 id="webgpu-extended-thoughts"&gt;WebGPU extended thoughts&lt;/h3&gt;
&lt;details&gt;
Is WebGPU "native enough"? Yes
&lt;p&gt;For browsers, WebGPU will require sandboxing and validation layers. But in native uses, this can all be turned off, and the WebGPU developers are clearly thinking about this use case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google's implementation of WebGPU, &lt;a href="https://dawn.googlesource.com/dawn"&gt;Dawn&lt;/a&gt;, can be configured to effectively turn off all browser sandboxing / validation that could harm performance due to its client/server architecture.&lt;/li&gt;
&lt;li&gt;Mozilla / gfx-rs Rust engineers have published articles such as &lt;a href="http://kvark.github.io/web/gpu/native/2020/05/03/point-of-webgpu-native.html"&gt;"The point of WebGPU on native"&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As for the quality of implementations, we could compare the amount of resources going into e.g. Google's WebGPU implementation vs. the amount of resources going into Unity/Unreal/MoltenVK/other graphics abstraction layers - but I suspect they're &lt;em&gt;about equal&lt;/em&gt;.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
Will WebGPU be implemented on GPUs natively? Maybe someday
&lt;p&gt;Not anytime soon. We get some insight into this &lt;a href="https://github.com/gpuweb/gpuweb/issues/847#issuecomment-642883924"&gt;via @kvark&lt;/a&gt;, a WebGPU developer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...] We are not in Khronos, and therefore we have limited participation from IHVs (only Intel and Apple are active). WebGPU was never designed to be implemented by the drivers. I mean, it would totally be rad, in the context of how usable WebGPU &lt;a href="http://kvark.github.io/web/gpu/native/2020/05/03/point-of-webgpu-native.html"&gt;can be on native&lt;/a&gt;, but it couldn't be the requirement from the start.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But as WebGPU usage grows or even becomes prodominate due to it being the most powerful API in browsers, and as Microsoft, Google, and Apple continue to develop their own hardware - I think it's not unreasonable to think that it's possible some day WebGPU will be an even more direct 1:1 mapping between a cross-platform API and low-level APIs, more direct than Vulkan abstraction layers such as MoltenVK (which is required to get Vulkan working on top of MacOS's Metal API) - with the potential that some vendor starts asking "what would a GPU native WebGPU implementation look like?"&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
Momentum of WebGPU vs. Vulkan
&lt;p&gt;To &lt;a href="https://news.ycombinator.com/item?id=23090432"&gt;quote&lt;/a&gt; &lt;a href="http://kvark.github.io/about/"&gt;Dzmitry Malyshau / kvark&lt;/a&gt;, a Mozilla engineer working on gfx-rs and WebGPU:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;At some point, it comes down to the amount of momentum behind the API. In case of WebGPU, we have strong support from Intel and Apple, which are hardware vendors, as well as Google, who can influence mobile hardware vendors. We are making the specification and have resources to appropriately test it and develop the necessary workarounds. It's the quantity to quality transition that sometimes just needs to cross a certain threshold in order to succeed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;According to some, Nvidia and AMD tend to develop new features with Microsoft as part of DirectX. Only then are they "ported" back to Vulkan and OpenGL. I think that says a lot.&lt;/p&gt;
&lt;/details&gt;
&lt;h2 id="what-progress-has-been-made-so-far-on-mach-engine"&gt;What progress has been made so far on Mach Engine?&lt;/h2&gt;
&lt;p&gt;Today, we have cross-compilation of GLFW on all desktop OSs working out of the box with nothing more than &lt;code&gt;zig&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img7.png"&gt;&lt;img alt="Cross compilation from Mac, Linux, and Windows to eachother on all major architectures." class="color-auto" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img7.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hexops/sdk-macos-11.3"&gt;Packaging MacOS SDKs&lt;/a&gt; and &lt;a href="https://github.com/hexops/sdk-linux-x86_64"&gt;Linux system X11/Wayland libraries&lt;/a&gt; into SDKs, and creating Zig build scripts that could merely &lt;code&gt;git clone&lt;/code&gt; them and utilize them for cross-compilation.&lt;/li&gt;
&lt;li&gt;Purchasing Apple M1 hardware to test on, and for GitHub Actions as it doesn&amp;rsquo;t support it.&lt;/li&gt;
&lt;li&gt;Normalizing symlinks in Mac/Linux SDKs everywhere so that Windows users don&amp;rsquo;t have a hard time with Git symlink management.&lt;/li&gt;
&lt;li&gt;Contributing &lt;a href="https://github.com/ziglang/zig/pull/9734"&gt;a small fix to the Zig linker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All this to say, we&amp;rsquo;re really taking a holistic approach to achieve this.&lt;/p&gt;
&lt;h2 id="whats-next-webgpu"&gt;What&amp;rsquo;s next? WebGPU&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m happy to report that a fair amount of progress on this front has been made.&lt;/p&gt;
&lt;p&gt;Here is Google&amp;rsquo;s WebGPU implementation, Dawn, compiled using &lt;code&gt;zig&lt;/code&gt;:
&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img8.png"&gt;&lt;img alt="A red triangle in a black window titled 'Dawn Window', the" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img8.png" /&gt;&lt;/a&gt;
&lt;a class="imglink" href="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img9.png"&gt;&lt;img alt="A Zig code file, hello_triangle.zig showing Dawn and WebGPU API usage in Zig" src="https://devlog.hexops.org/img/2021/mach-engine-the-future-of-graphics-with-zig/img9.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A ~500 line port of the &lt;code&gt;hello_triangle&lt;/code&gt; example from Dawn to Zig&lt;/li&gt;
&lt;li&gt;A ~1200 line &lt;code&gt;build.zig&lt;/code&gt; file which compiles all the Dawn sources using Zig, without using Google&amp;rsquo;s ninja/etc development tools.&lt;/li&gt;
&lt;li&gt;A hack to workaround a bug in Zig where ObjC++ &lt;code&gt;.mm&lt;/code&gt; files are not yet recognized.&lt;/li&gt;
&lt;li&gt;C shims for the &lt;code&gt;dawn_native&lt;/code&gt; C++ API and utility APIs, which are required in order to bind Dawn to an actual GLFW window.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a few weeks of work to do before this can be merged and will be usable by others, please stay tuned for that.&lt;/p&gt;
&lt;p&gt;After that will be development of idiomatic Zig bindings to the &lt;a href="https://github.com/webgpu-native/webgpu-headers"&gt;WebGPU C API&lt;/a&gt; which is shared between implementations such as Dawn and the Rust&amp;rsquo;s &lt;a href="https://github.com/gfx-rs/wgpu-native"&gt;gfx-rs/wgpu-native&lt;/a&gt; implementation (we could theoretically switch between them at startup in the future, but we&amp;rsquo;ll probably stick with Dawn as it does not require a separate Rust toolchain and it would prevent out-of-the-box cross compilation.)&lt;/p&gt;
&lt;h2 id="when-will-there-be-games-examples-etc"&gt;When will there be games, examples, etc.?&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;ll be a while because I am focusing purely on the groundwork first. It&amp;rsquo;s unlikely you&amp;rsquo;ll see anything with &lt;em&gt;real demo value&lt;/em&gt; before later next year.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure that will be disheartening to hear - and may make you to think there&amp;rsquo;s nothing of substance here. I totally understand that view, but I hope you&amp;rsquo;ll stay tuned because I&amp;rsquo;m in this for the long haul and it&amp;rsquo;s not my first rodeo (I previously spent 4 years writing &lt;a href="https://azul3d.org"&gt;a game engine in Go&lt;/a&gt;, and have worked &lt;a href="https://sourcegraph.com"&gt;at a devtools startup for 7 years&lt;/a&gt;, with my biggest lesson from of those experiences being the importance of demos and examples.&lt;/p&gt;
&lt;h2 id="follow-along"&gt;Follow along&lt;/h2&gt;
&lt;p&gt;Major developments will be posted here.&lt;/p&gt;
&lt;p&gt;You can also follow the project at &lt;a href="https://github.com/hexops/mach"&gt;github.com/hexops/mach&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you like what I&amp;rsquo;m doing, you can &lt;a href="https://github.com/sponsors/emidoots"&gt;sponsor me on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;</description><author>Hexops' devlog</author><pubDate>Sun, 17 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://devlog.hexops.org/2021/mach-engine-the-future-of-graphics-with-zig/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #41</title><link>https://justingarrison.com/123dev/2021-10-17-123dev-41/</link><description>&lt;img alt="Multiple abandoned buildings are destroyed" src="https://justingarrison.com/img/123dev/41-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="bgp"&gt;BGP&lt;/h2&gt;
&lt;p&gt;I heard the GIF came from what happened when Facebook ran the following command.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ip link set fb0 down&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Thankfully they were able to fix it with&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo reboot&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="thinking"&gt;Thinking&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&amp;rsquo;re overthinking you should write. If you&amp;rsquo;re underthinking you should read.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Written word is much slower than spoken word. It helps to slow down and be deliberate when you have lots of ideas in your head.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 17 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-17-123dev-41/</guid></item><item><title>Blender rendering on NVIDIA Jetson Nano</title><link>https://kinoshita.eti.br/2021/10/17/blender-rendering-on-nvidia-jetson-nano.html</link><description>&lt;figure class="feature"&gt;

 &lt;img alt="NVIDIA Jetson Nano computer" height="" src="https://kinoshita.eti.br/assets/posts/2021-10-17-blender-rendering-on-nvidia-jetson-nano/jetson.jpg" title="NVIDIA Jetson Nano computer" width="" /&gt;
 &lt;figcaption&gt;NVIDIA Jetson Nano computer&lt;/figcaption&gt;

&lt;/figure&gt;


&lt;p&gt;I had used Blender during my graduation at the Mackenzie University and started learning
Blender 2.8+ again a few weeks ago. Unfortunately rendering the basic tutorials like Andrew
Price&amp;rsquo;s donut takes several minutes on my old (but excellent for programming) Thinkpad
T550 i7 16 GB with a simple Samsung SSD. The reason is that my GPU, a
&lt;a href="https://www.techpowerup.com/gpu-specs/nvs-5400m.c1742"&gt;NVIDIA NVS 5400M&lt;/a&gt;
with 2 GB memory and 96 cores cannot be used with Blender as it only supports CUDA 2.1.
Blender 2.8+ GPU rendering requires CUDA 3.0 and higher, which means Blender Cycles
render is using my CPU, which is slower than using a decent GPU.&lt;/p&gt;</description><author>About on kinow</author><pubDate>Sun, 17 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://kinoshita.eti.br/2021/10/17/blender-rendering-on-nvidia-jetson-nano.html</guid></item><item><title>Venezuela, the unlikely leader of digital payments</title><link>https://stop.zona-m.net/2021/10/venezuela-the-unlikely-leader-of-digital-payments/</link><description>&lt;p&gt;Not so unlikely, when you think about it.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 17 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/venezuela-the-unlikely-leader-of-digital-payments/</guid></item><item><title>100 Bytes of CSS to look great everywhere</title><link>https://www.swyx.io/css-100-bytes</link><description>Simple grab and go upgrade to the base user agent stylesheet to make for readable sites</description><author>swyx's site RSS Feed</author><pubDate>Sun, 17 Oct 2021 00:05:53 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/css-100-bytes</guid></item><item><title>The second book is harder</title><link>https://liza.io/the-second-book-is-harder/</link><description>&lt;p&gt;After a year of short stories, I released my first novella recently and it&amp;rsquo;s been doing pretty well. I&amp;rsquo;m surprised that it is &lt;em&gt;still&lt;/em&gt; doing well, since usually books tend to drop off a cliff. But it&amp;rsquo;s still early days, and the cliff can come any time now.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Sat, 16 Oct 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/the-second-book-is-harder/</guid></item><item><title>Cognition Enhancing Technologies</title><link>https://one.mikro2nd.net/pages/cognition-enhancing-technologies/</link><description>Against all expectation, I got a job. Sort of. I&amp;rsquo;m working with a non-profit org, We Think Code, that teaches software development skills (not just programming!) to people new-to-programming who likely might not otherwise have access to such opportunity. My role is in curriculum development, where I work (async, remote) as a small part of a stellar team, helping to write lessons that &amp;ldquo;Help students become the developers we want to see &amp;amp; work with in the world.</description><author>one mikro2nd</author><pubDate>Sat, 16 Oct 2021 15:16:13 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/cognition-enhancing-technologies/</guid></item><item><title>Things my dog taught me about software development</title><link>https://philbooth.me/blog/things-my-dog-taught-me-about-software-development/</link><description>&lt;p&gt;I&amp;rsquo;ve always wanted a dog,
but could never get one
because I lived in rented accomodation.
Happily that changed last year
so as soon as I could,
I bought a puppy.
This is what I learned
about dogs, people and code.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of my dog, Milo, on the day I brought him home" src="https://assets.philbooth.me/images/milo-ball-672.jpg" /&gt;&lt;/p&gt;
&lt;h3 id="1-dont-be-surprised-when-puppies-do-puppy-stuff-"&gt;1. Don&amp;rsquo;t be surprised when puppies do puppy stuff 🐶&lt;/h3&gt;
&lt;p&gt;As an engineer of somewhat advanced years,
something I&amp;rsquo;ve struggled with at times
is being patient with less experienced devs.
I try to be patient
and I know impatience is bad,
but there are times when it feels like
I&amp;rsquo;m suppressing an urge to be exasperated.&lt;/p&gt;
&lt;p&gt;Puppies are tiny, cute bundles of joy.
They are impossible not to love.
They try very hard to be good little boys and girls,
and look to you for guidance.
But also they want to chew everything,
dig holes in your lawn,
jump up on your furniture
and go for a pee or a poo whenever they feel like it.
Obviously it would be stupid to get upset about any of that,
so patiently you guide them to make better choices
and gradually they learn how to behave.&lt;/p&gt;
&lt;p&gt;And so it goes for junior engineers.
They&amp;rsquo;re on a journey and trying to improve.
There are things they haven&amp;rsquo;t learned yet
but it&amp;rsquo;s a consequence of their experience,
not lack of ability.
If they&amp;rsquo;re confidently doing the wrong thing,
it&amp;rsquo;s an effect rather than a cause.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of Milo with a stolen shoe in his possession" src="https://assets.philbooth.me/images/milo-shoe-672.jpg" /&gt;&lt;/p&gt;
&lt;h3 id="2-reward-them-when-they-do-good-things-"&gt;2. Reward them when they do good things 🍪&lt;/h3&gt;
&lt;p&gt;Guidance comes in two forms:
calmly saying no to bad behaviours
and enthusiastically rewarding good ones.
You should do lots more of the second one
than the first.&lt;/p&gt;
&lt;p&gt;When saying no,
it doesn&amp;rsquo;t help to get angry about anything
because that won&amp;rsquo;t produce the desired outcome.
If you get angry,
they&amp;rsquo;ll get worked up too
and you enter an arms race
of who can be the most outraged.
It doesn&amp;rsquo;t lead anywhere.&lt;/p&gt;
&lt;p&gt;For puppies,
give them a clear and calm &amp;ldquo;no&amp;rdquo;
then ignore any further attempts at bad behaviour,
removing them from the situation if you have to.
For junior engineers,
explain the downsides of what they&amp;rsquo;re trying to do
and why alternative solutions don&amp;rsquo;t suffer from them.
Point out better options using broad enough strokes
that they still have some latitude for discovery and creativity
in reaching the right answer.
Don&amp;rsquo;t suck the joy out of programming.&lt;/p&gt;
&lt;p&gt;When rewarding,
you want your rewards to be fun
and memorable.&lt;/p&gt;
&lt;p&gt;For puppies
that means changing your tone of voice,
giving them tasty treats,
belly rubs,
all the things they enjoy.
And your reward must come right after whatever it&amp;rsquo;s for,
so they know why you&amp;rsquo;re rewarding them.&lt;/p&gt;
&lt;p&gt;Most of the above applies to engineers too,
although belly rubs are not advised.
Make sure the reward is something they actually value.
Some people like praise in public,
but some people hate the limelight that brings.
Make the reward commensurate to the size of the task
and make it clear why you&amp;rsquo;re rewarding them.
For code reviews,
call out specific parts of the diff that are good
instead of leaving a blanket 👍 on the PR.
If someone is consistently excellent,
speak to your boss and
help them get a promotion or a pay rise.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of Milo eating a treat" src="https://assets.philbooth.me/images/milo-treat-672.jpg" /&gt;&lt;/p&gt;
&lt;h3 id="3-dont-hold-grudges-"&gt;3. Don&amp;rsquo;t hold grudges 😠&lt;/h3&gt;
&lt;p&gt;After you&amp;rsquo;ve said no
to your puppy/engineer,
it&amp;rsquo;s important to let go of the subject
and move on.
Not doing that will cause problems on two fronts.&lt;/p&gt;
&lt;p&gt;First,
it will make them think you&amp;rsquo;re an unreasonable old meany.
They&amp;rsquo;ll get scared
and gradually your relationship will become one of fear.
They will not produce their best work
under those conditions.&lt;/p&gt;
&lt;p&gt;Second,
it will set the tone inside your own head
for how you interact with them in future.
Negativity begets more negativity,
it&amp;rsquo;s habitual.
Eventually, if you don&amp;rsquo;t keep a lid on it,
the &lt;em&gt;Ghost of Christmas Past&lt;/em&gt; will visit
and show you what you used to be like.
Don&amp;rsquo;t turn into Ebenezer Scrooge.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of Milo sitting in some long grass" src="https://assets.philbooth.me/images/milo-grass-672.jpg" /&gt;&lt;/p&gt;
&lt;h3 id="4-clear-up-any-mess-thats-left-behind-"&gt;4. Clear up any mess that&amp;rsquo;s left behind 💩&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s beholden on us all
to create the world we want to live in.
When you walk your puppy,
you take little plastic bags with you
and scoop up their mess.
When they chew through a soft toy
so that all the stuffing spills out
onto the living room floor,
you clean it away
before they can swallow any of it.
Accidents happen,
you clear up afterwards.&lt;/p&gt;
&lt;p&gt;The same applies to engineering too,
except we call it refactoring.
Code mess is a natural byproduct of feature development
that can paralyse teams if left unchecked.
The metaphor of technical debt is well understood,
but it can still be hard to get buy-in
from more product-minded parts of the team
when it comes to prioritisation.
The best solution to that
is to be constantly refactoring,
in small steps so that it&amp;rsquo;s hardly noticed.
Bigger refactors will still be needed from time to time of course,
but you can significantly reduce their impact
if you clean up any mess as you go.
Mess that&amp;rsquo;s left behind eventually develops a life of its own
and is that much harder to deal with as a result.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of Milo with grass seed all over his face" src="https://assets.philbooth.me/images/milo-seed-672.jpg" /&gt;&lt;/p&gt;
&lt;h3 id="5-make-time-for-play-"&gt;5. Make time for play 🧸&lt;/h3&gt;
&lt;p&gt;For puppies,
play is an important part of their development
and a way to increase the bond between you.
It&amp;rsquo;s also an opportunity to provide more guidance.
If you&amp;rsquo;re playing tug,
have some treats on hand
and use them to gradually teach the puppy
a &amp;ldquo;drop&amp;rdquo; or &amp;ldquo;give&amp;rdquo; command.
You can utilise your puppy&amp;rsquo;s instinct for chasing
to start teaching them &amp;ldquo;come&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Engineering is rarely a solo pursuit
and teams that only communicate around work tasks
are missing an opportunity for bonding too.
It can be difficult when working remotely
but small things like taking five minutes at the start of meetings just to chat,
can develop empathy among the team
and encourage healthier interaction between team-mates.
Deep technical conversations go much better
when you like the people you&amp;rsquo;re having them with.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Picture of Milo with his favourite toy bear" src="https://assets.philbooth.me/images/milo-bear-672.jpg" /&gt;&lt;/p&gt;</description><author>Phil Booth's Blog</author><pubDate>Sat, 16 Oct 2021 13:54:54 GMT</pubDate><guid isPermaLink="true">https://philbooth.me/blog/things-my-dog-taught-me-about-software-development/</guid></item><item><title>Human TCP</title><link>https://june.kim/human-tcp/</link><author>june.kim</author><pubDate>Sat, 16 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/human-tcp/</guid></item><item><title>The Javascript "ecosystem" is a hot mess and so is software development in general</title><link>https://nadh.in/blog/javascript-ecosystem-software-development-are-a-hot-mess/</link><description>&lt;p&gt;I have a small Vue 2 project (an &lt;a href="https://github.com/knadh/dictmaker/issues/14"&gt;admin UI&lt;/a&gt; for dictmaker) that I created with &lt;code&gt;vue cli&lt;/code&gt; six months ago. Today, I picked it up again to finish it, and started out by doing a &lt;code&gt;yarn upgrade&lt;/code&gt;. Of course, blindly upgrading all dependencies is never a good idea, but this is a tiny WIP project with just one dependency that I added, and there is a constant stream of GitHub dependabot alerts every month forcing me to upgrade some dependency or another, so what is the worst that could happen? At least that is what I thought.&lt;/p&gt;</description><author>Blog on Kailash Nadh / Personal homepage</author><pubDate>Sat, 16 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nadh.in/blog/javascript-ecosystem-software-development-are-a-hot-mess/</guid></item><item><title>Git Basics</title><link>https://trigonaminima.github.io/2021/10/git-training/</link><description>I created this introduction to Git for the Data Science team of Swiggy. I believe, the better we know Git, the more efficient our engineering processes will be. Data Scientists come from a variety of educational backgrounds. Many of them will not know anything about git. Many others will just have a working knowledge; they will use git commands, but they won’t really know what really happens. Observing this gap in the team, I decided to work on bridging it.</description><author>Playground</author><pubDate>Sat, 16 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://trigonaminima.github.io/2021/10/git-training/</guid></item><item><title>Why didn't the web disrupt mobile, you ask?</title><link>https://stop.zona-m.net/2021/10/why-didnt-the-web-disrupt-mobile-you-ask/</link><description>&lt;p&gt;Here is my answer.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 16 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/why-didnt-the-web-disrupt-mobile-you-ask/</guid></item><item><title>How to remove Traefik from K3S</title><link>https://yasha.solutions/posts/remove-traefik-from-k3s/</link><description>When you install k3s it automatically come with Traefik installed.
Traefik is great but if you need to work with an other ingress, you might want to remove it.
Here is how:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  # Remove traefik helm chart resource sudo kubectl -n kube-system delete helmcharts.helm.cattle.io traefik # Stop the k3s service sudo service k3s stop # add option to add to ExecStart "--no-deploy traefik \" sudo echo '--no-deploy traefik \' &amp;gt;&amp;gt; /etc/systemd/system/k3s.</description><author>Yasha Solutions</author><pubDate>Sat, 16 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/remove-traefik-from-k3s/</guid></item><item><title>Goodbye old friend, you will be missed</title><link>https://www.bajkowski.com/goodbye-old-friend-see-you-will-be-missed/</link><description>Reminiscing about our house cat Miko and the joy she brought over the last two decades</description><author>Bajkowski.com</author><pubDate>Fri, 15 Oct 2021 23:27:31 GMT</pubDate><guid isPermaLink="true">https://www.bajkowski.com/goodbye-old-friend-see-you-will-be-missed/</guid></item><item><title>Ansible + Terraform, the PBJ of DevOps</title><link>https://www.brightball.com/articles/ansible-and-terraform-the-pb-and-j-of-devops</link><description>This week I had the opportunity to speak to Upstate Carolina Linux User Group (UCLUG) to share my thoughts on how well Ansible and Terraform compliment each other, as well as the many ways to share information between the tools to use them together.</description><author>Brightball Articles</author><pubDate>Fri, 15 Oct 2021 17:42:03 GMT</pubDate><guid isPermaLink="true">https://www.brightball.com/articles/ansible-and-terraform-the-pb-and-j-of-devops</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #40</title><link>https://justingarrison.com/123dev/2021-10-15-123dev-40/</link><description>&lt;img alt="someone puts a board to cover a gap on a porch and then steps on the board which hits them in the head" src="https://justingarrison.com/img/123dev/40-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="laying-a-trap-for-yourself"&gt;Laying a trap for yourself&lt;/h2&gt;
&lt;p&gt;I have lost track of how many times I’ve patched a bug only to have it come back and hit me in the head. Sometimes tests will help you avoid a self-made trap, but more often then not I’ll just push harder until it breaks.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 15 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-15-123dev-40/</guid></item><item><title>Armed robodogs are just like smart rifles</title><link>https://stop.zona-m.net/2021/10/armed-robodogs-are-just-like-smart-rifles/</link><description>&lt;p&gt;Just as dumb, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 15 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/armed-robodogs-are-just-like-smart-rifles/</guid></item><item><title>Kubernetes Building Blocks</title><link>https://yasha.solutions/posts/kubernetes-building-blocks/</link><description>Kubernetes has multiple moving parts.
Here is an short article aiming at mapping all this to create some clarity.
Some Basic Vocabulary Nothing fancy but let&amp;rsquo;s get this out of the way.
  Nodes
A node can be a physical machine or a virtual machine.
  Cluster
A cluster is a group of nodes
  Container runtime
Docker (or runC)
  Pod
A pod is a group of containers - that together form a logical application.</description><author>Yasha Solutions</author><pubDate>Fri, 15 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/kubernetes-building-blocks/</guid></item><item><title>2021–10–15: Pinephone Pro – A Quick Review</title><link>https://xnux.eu/log/#047</link><author>megi's PinePhone Development Log</author><pubDate>Fri, 15 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#047</guid></item><item><title>My book-writing routine</title><link>https://lucasfcosta.com/blog/my-writing-routine/</link><description>I spent 2020 writing a 500-pages book about JavaScript Testing.

In this blog post, I'll explain my writing routine — or system, whatever you want to call it — and other seemingly unrelated habits th...</description><author>Lucas F. Costa - Blog</author><pubDate>Thu, 14 Oct 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://lucasfcosta.com/blog/my-writing-routine/</guid></item><item><title>When I read the title, I was thinking the article would discuss how everyone uses and interacts…</title><link>https://olshansky.info/posts/2021-10-14-when-i-read-the-title-i-was-thinking-the-article-would-discuss-how-everyone-uses-and-interacts/</link><description>I agree with Nate's comment that ethereum contracts are indeed public and immutable. Whether an individual chooses to sync a node, inspect…</description><author>🦉 olshansky 🦁</author><pubDate>Thu, 14 Oct 2021 12:59:56 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/posts/2021-10-14-when-i-read-the-title-i-was-thinking-the-article-would-discuss-how-everyone-uses-and-interacts/</guid></item><item><title>Pastel migraine auras</title><link>https://qubyte.codes/blog/pastel-migraine-auras</link><description>&lt;p&gt;A generative patchwork of pastel colours. The colours begin with a randomly
picked colour in &lt;a href="https://www.w3.org/TR/css-color-4/#lab-colors"&gt;LCH&lt;/a&gt;. Other
colours are hue rotations in LCH space so that they're perceptually nice
together. Feature detection is used to render using LCH colours when the browser
supports them (Safari only at the time of writing), or to pick a close colour in
RGB space when LCH is not supported (the code for this is based on code in
&lt;a href="https://github.com/d3/d3-color"&gt;d3-color&lt;/a&gt;). Refreshing generates a new
patchwork.&lt;/p&gt;</description><author>Qubyte Codes</author><pubDate>Thu, 14 Oct 2021 11:10:00 GMT</pubDate><guid isPermaLink="true">https://qubyte.codes/blog/pastel-migraine-auras</guid></item><item><title>Italia I - Umbria</title><link>https://faingezicht.com/photos/2021/10/14/umbria/</link><description>Many months ago, in the depths of COVID-times, our friend Maizie asked if we'd be willing to travel to Italy to celebrate her birthday if she planned everything for our friend group, assuming we could all get vaccinated by then. That was a pretty easy question to say yes to.

I had only been to Italy once before, on a short vist to Venice during a backpacking trip before college. Disconnecting from our daily routines is always relaxing, but getting back together with our friend group, many of whom had left San Francisco, and wandering through the life-sized museum that is Italy was a ton of fun.

_This is the first in a series shared throughout Fall 2021. When you're done here, make sure to also check out parts &lt;a href="/photos/2021/10/19/florence/"&gt;two&lt;/a&gt; and &lt;a href="/photos/2021/10/31/roma/"&gt;three&lt;/a&gt;, &lt;a href="/photos/2021/11/12/positano/"&gt;four&lt;/a&gt;, and &lt;a href="/photos/2021/12/02/italia-revisited/"&gt;five&lt;/a&gt;._</description><author>Avy Faingezicht</author><pubDate>Thu, 14 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/10/14/umbria/</guid></item><item><title>Bridge WIFI connection via LAN</title><link>https://xnacly.me/posts/2021/wifi_bridge/</link><description>How to bridge a WIFI connection to LAN</description><author>xnacly - blog</author><pubDate>Thu, 14 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xnacly.me/posts/2021/wifi_bridge/</guid></item><item><title>The Five Lies Analysis</title><link>https://serce.me/posts/2021-10-14-the-five-lies-analysis</link><description>The Five Whys analysis is a simple root cause investigation technique but can have trade-offs if blindly applied. This article explores the trade-offs of the approach.</description><author>SerCe's blog</author><pubDate>Thu, 14 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://serce.me/posts/2021-10-14-the-five-lies-analysis</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #39</title><link>https://justingarrison.com/123dev/2021-10-14-123dev-39/</link><description>&lt;img alt="A golf ball hits a steel plate in slow motion and deforms like rubber" src="https://justingarrison.com/img/123dev/39-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;p&gt;I twice had lunch with Ed Catmull at Disney and I asked him if there was anything that surprised him about managing people. He said he was surprised how well people can game rules and metrics for their own benefit.&lt;/p&gt;
&lt;p&gt;The above gif may or may not be real, but also speaks to people manipulating perception for—maybe—their benefits.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 14 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-14-123dev-39/</guid></item><item><title>The real advantage of broadband via laser links</title><link>https://stop.zona-m.net/2021/10/the-real-advantage-of-broadband-via-laser-links/</link><description>&lt;p&gt;It&amp;rsquo;s not the speed, or the reliability. It&amp;rsquo;s the people.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 14 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/the-real-advantage-of-broadband-via-laser-links/</guid></item><item><title>7 Step Guide to Installing rsync on Windows</title><link>https://ayewo.com/how-to-install-rsync-on-windows/</link><description>&lt;p&gt;I recently needed to move several gigabytes of data from an old laptop running Windows 8.1 to another laptop running macOS and my options were to use an external disk or a network transfer. I wanted the whole process to be unattended so I naturally gravitated towards doing the&lt;/p&gt;</description><author>Misleading Metaphors</author><pubDate>Wed, 13 Oct 2021 16:04:17 GMT</pubDate><guid isPermaLink="true">https://ayewo.com/how-to-install-rsync-on-windows/</guid></item><item><title>Social media break</title><link>https://liza.io/social-media-break/</link><description>&lt;p&gt;A few years ago I deleted my Facebook account, and now I feel like it&amp;rsquo;s time for the next step: Twitter; but nothing as drastic as deletion just yet.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Wed, 13 Oct 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/social-media-break/</guid></item><item><title>DIY cloud gaming: NVIDIA and Moonlight</title><link>https://ounapuu.ee/posts/2021/10/13/diy-cloud-gaming-nvidia-moonlight/</link><description>Comparing the experience that AMD and NVIDIA can provide in a DIY cloud gaming setup.</description><author>./techtipsy</author><pubDate>Wed, 13 Oct 2021 05:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/10/13/diy-cloud-gaming-nvidia-moonlight/</guid></item><item><title>Unintuitive Optimization</title><link>https://www.marginalia.nu/log/30-unintuitive-optimization/</link><description>&lt;p&gt;Optimization is arguably a lot about intuition. You have a hunch, and see if it sticks. Sure you can use profilers and instrumentation, but they are more like hunch generators than anything else.&lt;/p&gt;
&lt;p&gt;This one wasn&amp;rsquo;t as intuitive, at least not to me, but it makes sense when you think about it.&lt;/p&gt;
&lt;p&gt;I have an 8 Gb file of dense binary data. This data consists of 4 Kb chunks and is an unsorted list containing first an URL identifier with metadata and then a list of word identifiers. This is a sort of journal that the indexer produces during crawling. Its main benefit is that this can be done quickly with very high fault tolerance. Since it&amp;rsquo;s only ever added to, if anything does go wrong you can just truncate the bad part at the end and keep going.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 13 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/30-unintuitive-optimization/</guid></item><item><title>Skills, stories, and software every dev should know - 123dev #38</title><link>https://justingarrison.com/123dev/2021-10-13-123dev-38/</link><description>&lt;img alt="An animation explaining that k8s is a numeronym of Kubernetes" src="https://justingarrison.com/img/123dev/38-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="edutainment"&gt;Edutainment&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I would rather entertain and hope that people learned something than educate people and hope they were entertained.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Walt Disney&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;I had a lightning talk for Devops Days Portland called “TikTalk” about innovation. Please go watch it because it was a lot of fun to make.&lt;/p&gt;
&lt;p&gt;Innovation comes in different forms; there are sustaining innovations and disruptive innovations. Most companies need people who can help sustain their innovations. They need to be better, faster, and stronger than they were.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 13 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-13-123dev-38/</guid></item><item><title>Notes on running Electron</title><link>http://notes.eatonphil.com/notes-on-running-electron.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-10-13-notes-on-running-electron.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Wed, 13 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/notes-on-running-electron.html</guid></item><item><title>Algorithm bias, again. This time, in mortgages</title><link>https://stop.zona-m.net/2021/10/algorithm-bias-again.-this-time-in-mortgages/</link><description>&lt;p&gt;But the problem is not algorithms, of course.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 13 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/algorithm-bias-again.-this-time-in-mortgages/</guid></item><item><title>2021–10–13: How to adapt multi-boot image to your needs</title><link>https://xnux.eu/log/#046</link><author>megi's PinePhone Development Log</author><pubDate>Wed, 13 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#046</guid></item><item><title>Scrape, Scrape</title><link>https://liza.io/scrape-scrape/</link><description>&lt;p&gt;My book has been getting more and more reviews and ratings on Amazon and Goodreads. Instead of obsessively refreshing the page I figured it might be cool to collect reviews from different sources and have them sent as a summary email each day.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Wed, 13 Oct 2021 00:20:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/scrape-scrape/</guid></item><item><title>Publishing a Package to Pip</title><link>https://blog.randombits.host/publishing-a-package-to-pip/</link><description>&lt;figure class="kg-card kg-image-card kg-card-hascaption"&gt;&lt;img alt="alt" class="kg-image" height="186" src="https://blog.randombits.host/content/images/2021/10/image.png" width="248" /&gt;&lt;figcaption&gt;PyPi - The Python Package Index&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;I love Python. It&amp;apos;s easy to read, even easier to write, and best of all, the easiest language (I&amp;apos;ve used) to interact with other people&amp;apos;s code. I spent too long being only on one end of that equation though,&lt;/p&gt;</description><author>Random Bits</author><pubDate>Wed, 13 Oct 2021 00:14:33 GMT</pubDate><guid isPermaLink="true">https://blog.randombits.host/publishing-a-package-to-pip/</guid></item><item><title>TCP By Disk</title><link>https://nevesnunes.github.io/blog/2021/10/12/TCP-By-Disk.html</link><description/><author>onKeyPress</author><pubDate>Tue, 12 Oct 2021 20:10:46 GMT</pubDate><guid isPermaLink="true">https://nevesnunes.github.io/blog/2021/10/12/TCP-By-Disk.html</guid></item><item><title>How we may read the Scriptures with the most spiritual profit</title><link>https://honza.pokorny.ca/2021/10/how-we-may-read-the-scriptures-with-the-most-spiritual-profit/</link><description>&lt;figure&gt;&lt;img src="https://honza.pokorny.ca/images/kjv-isaiah.jpg" /&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;By Rev. Thomas Watson&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;And it shall be with him, and he shall read it all the days of his
life, that he may learn to fear the Lord his God, to keep all the words
of this Law and to do these Statutes.&amp;rdquo; &amp;mdash; Deut. 17.19.&lt;/p&gt;
&lt;p&gt;What Cicero said of Aristotle&amp;rsquo;s politics, may not unfittingly be said of
this book of Deuteronomy, it is full of golden eloquence. In this
chapter, God instructs the Jews about setting a king over them, and
there are two things specified, as to his election, and his religion.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;His election, verse 15. &amp;ldquo;You shall in any way set him king over you,
whom the Lord your God shall choose.&amp;rdquo; There is good reason that God
should have the choice of their king, since &amp;ldquo;by him kings reign.&amp;rdquo;
Prov. 8.15.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;His religion, verse 18. &amp;ldquo;When he sits on the throne of his kingdom,
he shall write him a copy of this law in a book, out of that which is
before the priest.&amp;rdquo; Here was a good beginning of a king&amp;rsquo;s reign; the
first thing he did after he sat upon the throne, was to copy out the
word of God in a book. And in the text, &amp;ldquo;It shall be with him, and he
shall read it, all the days of his life, that he may learn to fear
the Lord his God, to keep all the words of this Law and to do these
Statutes.&amp;rdquo; &amp;ldquo;It shall be with him.&amp;rdquo; The Book of the Law shall be his
&lt;em&gt;Vade Mecum&lt;/em&gt;, or daily companion. Charles the Great used to set his
crown upon the Bible. Indeed the Bible is the best support of
government. &amp;ldquo;And he shall read it.&amp;rdquo; It is not below the majesty of a
prince to peruse the oracles of Heaven; in them are comprised sacred
apothegms, Prov. 8.6: &amp;ldquo;I will speak of excellent things.&amp;rdquo; In the
Septuagint it is &amp;ldquo;grave things,&amp;rdquo; in the Hebrew, &amp;ldquo;princely things,&amp;rdquo;
such as are fit for a God to speak and a king to read. Nor must the
king only read the Book of the Law at his first installment into his
kingdom, but he &amp;ldquo;shall read it all the days of his life.&amp;rdquo; He must not
leave off reading, till he has left off reigning. And the reasons why
he must be conversant in the law of God, are in the subsequent
words: 1. &amp;ldquo;That he may learn to fear the Lord his God.&amp;rdquo; Reading the
word is the best means to usher in the fear of the Lord, 2. &amp;ldquo;That he
may keep all the words of this Law, to do them.&amp;rdquo; 3. &amp;ldquo;That he may
prolong his days in his kingdom.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I shall now confine myself to these words, &amp;ldquo;He shall read it all the
days of his life;&amp;rdquo; i. e. the Book of the Law.&lt;/p&gt;
&lt;p&gt;The Holy Scripture is, as Austin says, a golden epistle sent to us from
God. This is to be read diligently; ignorance of Scripture is the mother
of error, not of devotion. Matt. 22.29: &amp;ldquo;You err, not knowing the
Scriptures.&amp;rdquo; We are commanded to &amp;ldquo;search the Scriptures.&amp;rdquo; John 5.39. The
Greek word signifies to search as for a vein of silver. How diligently
does a child read over his father&amp;rsquo;s will and testament! and a citizen
peruse his charter! With like diligence we should read God&amp;rsquo;s word, which
is our Magna Charta for Heaven. It is a mercy that the Bible is not
prohibited. Trajan the emperor forbade the Jews to read in the Book of
the Law. Let us inquire at this sacred oracle. Apollos was &amp;ldquo;mighty in
the Scriptures.&amp;rdquo; Acts 18.24. Melancthon, when he was young, sucked the
sincere milk of the word. Alphonsus, King of Arragon, read over the
Bible fourteen times. That Roman Lady, Cecilia, had by much reading of
the word, made her heart the Library of Christ, as Jerome relates. Were
the Scriptures confined to the original tongues, many would plead an
excuse for not reading; but when the sword of the Spirit is unsheathed,
and the word is made plain to us by being translated, what should hinder
us from a diligent search into these holy mysteries?&lt;/p&gt;
&lt;p&gt;Adam was forbidden upon pain of death to taste of the tree of knowledge.
Gen. 2.17: &amp;ldquo;In the day you eat of it, you shall surely die.&amp;rdquo; But there
is no danger of touching this tree of Holy Scripture; if we do &lt;em&gt;not&lt;/em&gt; eat
of &lt;em&gt;this&lt;/em&gt; tree of knowledge we shall surely die. What will become of
those who are strangers to the Scriptures? Hosea 8.12: &amp;ldquo;I have written
to him the great things of my law, but they were considered as a strange
thing.&amp;rdquo; Many lay aside the Scriptures like rusty armor. Jer. 8.9. &amp;ldquo;They
are better-read in romances than in Paul; they spend many hours between
the comb and the glass, but their eyes begin to be sore when they look
at a Bible.&amp;rdquo; Even the Turks will rise up in judgment against these
Christians. The Turks reverence the Books of Moses; and if they find
just a leaf on which anything of the Pentateuch is written, they take it
up and kiss it. Those who slight the written word, slight God himself,
whose stamp it bears. To slight the king&amp;rsquo;s edict is an affront to the
person of the king. Those who reject and vilify the Scriptures are in a
state of condemnation. Prov. 13.13: &amp;ldquo;Whoever despises the word shall be
destroyed.&amp;rdquo; Nor is it enough to read the word of God, but it should be
our care to get some spiritual reward and profit by it, so that our
souls may be nourished in the words of faith. 1Tim. 4.6. Why else was
the Scripture written but that it might profit us? God did not give us
his word only as a landscape to look at, but he delivered it as a father
delivers a stock of money to his son to improve on it. &amp;lsquo;Tis sad not to
profit by the word, to be like the body in consumption, that does not
thrive. Men would be loath to trade and get no profit. The grand
question I am to speak to is this. How we may read the Scriptures with
the most spiritual profit. In answering this question, I shall lay down
several rules or directions about the reading of Scripture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; If you would profit by reading, &lt;em&gt;remove those things that will
hinder your profiting&lt;/em&gt;. That the body may thrive, obstructions must be
removed. There are three obstructions that must be removed if you would
profit by Scripture.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Remove the love of every sin. Let a physician prescribe ever so good
recipes, if the patient takes poison, it will hinder the virtue and
operation of the medicine. The Scripture prescribes excellent
recipes, but sin that is lived in, poisons all. The body cannot
thrive in a fever, nor can the soul thrive under the feverish heat of
lust. Plato calls the love of sin &lt;em&gt;magnus daemon&lt;/em&gt;, a great devil. As
the rose is destroyed by the canker which breeds in it, so are the
souls of men destroyed by those sins in which they indulge.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Take heed of those thorns which will choke the word that is read.
These thorns our Saviour expounds to be the cares of this world.
Matt. 13.22. By &lt;em&gt;cares&lt;/em&gt; is meant &lt;em&gt;covetousness&lt;/em&gt;. A covetous man has
such diversity of secular employment that he can scarcely find time
to read, or if he does, what errors does he commit in reading? While
his eye is upon the Bible, his heart is upon the world; it is not the
writings of the Apostles that he is so much taken up with, as the
writing in his account books. Is this man likely to profit? You may
as soon extract oil and syrup out of a flint, as for him to have any
real benefit out of Scripture.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Take heed against jesting with Scripture; this is playing with fire.
Some cannot be merry unless they make bold with God; when they are
sad, they bring forth the Scripture as their harp to drive away the
evil spirit, like a drunkard who having emptied his cups, calls to
his fellows, &amp;ldquo;Give us your oil, for our lamps have gone out.&amp;rdquo; In the
fear of God, beware of this. King Edward IV would not allow his crown
to be jested with, but executed anyone who said &lt;em&gt;he would make his
son heir to the Crown&lt;/em&gt;, meaning the sign of the Crown on his tavern.
Much less will God allow us to jest with his word. Eusebius relates
about someone who took a piece of Scripture to jest with, that God
struck him with panic. The Lord may justly give over such persons to
a reprobate mind. Rom. 1.23.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; If you would profit, &lt;em&gt;prepare your hearts for reading the word&lt;/em&gt;;
the heart is an instrument that needs to be tuned. 1Sam 7.3: &amp;ldquo;Prepare
your hearts to the Lord.&amp;rdquo; The heathen, as Plutarch notes, thought it was
indecent to be too hasty or rash in the service of their supposed
deities. This preparation for reading consists in two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In summoning our thoughts together to attend to that solemn work we
are about to do. Our thoughts are stragglers; therefore rally them
together.&lt;/li&gt;
&lt;li&gt;In purging those unclean affections which indispose us to reading.
Before we come to the water of life, let us cast away the poison of
impure affections. Many come rashly to the reading of the word; it is
no wonder that if they come without &lt;em&gt;preparation&lt;/em&gt;, they go away
without &lt;em&gt;profit&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; &lt;em&gt;Read the Scriptures with reverence&lt;/em&gt;; think about every line you
read; God is speaking to you. The ark in which the law was put, was
overlaid with pure gold, and was carried on bars, so that the Levites
might not touch it. Ex. 25, Why was this if not to breed in the people a
reverence for the law? When Ehud told Eglon he had a message for him
from God, Eglon arose from his throne. Judges 3.20, The written word is
a message from Jehovah; we should receive it with that veneration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; &lt;em&gt;Read the books of Scripture in order.&lt;/em&gt; Though circumstances may
sometimes divert our method, yet, for a &lt;em&gt;constant&lt;/em&gt; course, it is best to
observe an order in reading. Order is a help to memory. We do not begin
to read a friend&amp;rsquo;s letter in the middle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; &lt;em&gt;Get a right understanding of Scripture&lt;/em&gt;. Psalm 119.73: &amp;ldquo;Give me
understanding, that I may learn your commandments.&amp;rdquo; Though there are
some knots in Scripture which are not easily untied, yet the Holy Ghost
has plainly pointed out to us things essential to salvation. The
knowledge of the sense of the Scriptures is the first step to profit. In
the Law, Aaron was first to light the lamps, and then to burn the
incense; the lamp of the understanding must first be lighted before the
affections can be inflamed. Get what knowledge you can by comparing
scriptures, by conferring with others, and by using the best annotators.
Without knowledge, the Scripture is a sealed book; every line is too
high for us; and if the word shoots above our head, it can never hit our
heart.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; &lt;em&gt;Read the word with seriousness&lt;/em&gt;. If one goes over the Scripture
cursorily, says Erasmus, there is little good to be got by it; but if he
is serious in reading it, it is the savor of life; and we may well be
serious if we consider the importance of those truths which are bound up
in this sacred volume. Deut. 32.47: &amp;ldquo;It is not a vain thing for you; it
is your life.&amp;rdquo; If a letter were to be opened and read, in which a man&amp;rsquo;s
whole estate were concerned, how serious would he be in reading it? In
the Scripture, our salvation is concerned; it addresses the love of
Christ, a serious subject. Christ has loved mankind more than the angels
that fell. Heb. 2.7. The loadstone, indifferent to gold and pearl, draws
the iron to it; thus Christ passed by the angels, who were of more noble
extraction, and drew mankind to himself. Christ loved us more than his
own life; indeed, though we had a hand in his death, yet he would not
leave us out of his will. This is a love that &lt;em&gt;passes knowledge&lt;/em&gt;; who
can read this without seriousness? The Scripture speaks of the mystery
of faith, the eternal recompenses, and the paucity of those who shall be
saved. Matt. 20.16: &amp;ldquo;Few chosen.&amp;rdquo; Someone said the names of all the good
emperors of Rome might be engraved in a little ring; and there are but
(comparatively) few names in the Book of Life. The Scripture speaks of
striving for heaven as in an agony. Luke 13.24. It cautions us not to
fall short of the promised rest. Heb. 4.1. It describes the horrors of
the infernal torments, the worm, and the fire. Mark 9.44. Who can read
this and &lt;em&gt;not&lt;/em&gt; be serious? Some have light, feathery, spirits; they run
over the most weighty truths in haste (like Israel who ate the Passover
in haste); and so they are not benefited by the word. Read with a
solemn, composed spirit. Seriousness is the Christian&amp;rsquo;s ballast, which
keeps him from being overturned with vanity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; &lt;em&gt;Labor to remember what you read&lt;/em&gt;. Satan would steal the word out
of our mind; not that he intends to make use of it himself, but lest
&lt;em&gt;we&lt;/em&gt; should make use of it. The memory should be like the chest in the
ark, where the ark was put. Psa 119.52: &amp;ldquo;I remembered your judgments of
old.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Jerome speaks of that religious lady, Paula, that she knew most of the
Scriptures by heart; we are bid to have &amp;ldquo;the word dwell in us.&amp;rdquo; Col.
3.16, The word is a jewel; it adorns the hidden man, and shall we not
remember it? If the word does not stay in the memory, it cannot profit.
Some can better remember a piece of news than a line of Scripture; their
memories are like those ponds where the frogs live, but the fish die.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8.&lt;/strong&gt; &lt;em&gt;Meditate upon what you read&lt;/em&gt;. Psalm 119.15: &amp;ldquo;I will meditate on
your precepts.&amp;rdquo; The Hebrew word to meditate, signifies to be &lt;em&gt;intense&lt;/em&gt;
in the mind. In meditation there must be a fixing of the thoughts upon
the object. Luke 2.19: &amp;ldquo;Mary pondered those things.&amp;rdquo; Meditation is the
concoction of Scripture; reading brings a truth into our head,
meditation brings it into our heart; reading and meditation, like Castor
and Pollux, must appear together. Meditation without reading is
erroneous; reading without meditation is barren. The bee sucks the
flower, and then works it into the hive, and so turns it into honey. By
reading, we suck the flower of the word; and by meditation, we work it
into the hive of our mind, and so it turns to our profit. Meditation is
the bellows of the affection. Psalm 39.3: &amp;ldquo;While I was musing the fire
burned.&amp;rdquo; The reason we come away so cold from reading the word, is
because we do not warm ourselves at the fire of meditation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9.&lt;/strong&gt; &lt;em&gt;Come to the reading of Scripture with humble hearts&lt;/em&gt;; acknowledge
how unworthy you are that God would reveal himself in his word to you.
God&amp;rsquo;s secrets are with the humble. Pride is an enemy to profiting. It
has been said that the ground on which the peacock sits is barren; that
heart where pride sits is &lt;em&gt;really barren&lt;/em&gt;. An arrogant person disdains
the counsels of the word, and hates the reproofs: is he likely to
profit? James 4.6: &amp;ldquo;God gives grace to the humble.&amp;rdquo; The most eminent
saints have been of low stature in their own eyes; like the sun at the
zenith, they showed least when they were at the highest. David had &amp;ldquo;more
understanding than all his teachers.&amp;rdquo; Psalm 119.99: but how humble he
was. Psalm 22.6: &amp;ldquo;I am a worm and no man.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10.&lt;/strong&gt; &lt;em&gt;Give credence to the written word; believe it is of God&lt;/em&gt;; see the
name of God in every line. The Romans, so that they might gain credit
for their laws, reported that they were inspired by the gods at Rome.
Believe the Scriptures to be divinely inspired. 2Tim. 3.16: &amp;ldquo;All
Scripture is given by inspiration of God.&amp;rdquo; Who but God could reveal the
great doctrines of the Trinity, the atonement of Jesus Christ for
sinners, and the resurrection? From where would the Scriptures come, if
not from God?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Sinners&lt;/em&gt; could not be the authors of Scripture. Would they indite
such holy lines, or inveigh so fiercely against the sins which they
love?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Saints&lt;/em&gt; could not be the authors of Scripture; how could it stand
with their sanctity to counterfeit God&amp;rsquo;s name, and put &amp;ldquo;thus says the
Lord,&amp;rdquo; to a book of their own devising?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Angels&lt;/em&gt; could not be the authors of Scripture. What angel in heaven
dares impersonate God, and say, &amp;ldquo;I am the Lord?&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Believe that the pedigree of Scripture is sacred, and that it comes from
the Father of light. The antiquity of Scripture speaks its divinity. No
extant human history reaches further than Noah&amp;rsquo;s flood; but the
Scripture addresses things before time. Besides that, the majesty,
profundity, purity and harmony of Scripture, show it could be breathed
from none but God himself. Add to this the &lt;em&gt;efficacy&lt;/em&gt; that the written
word has upon men&amp;rsquo;s consciences. By reading Scripture, they have been
turned into other men, as may be instanced in Austin, Junius, and
others. If you were to set a seal upon a piece of marble, and it left a
print behind, you would say there was a strange virtue in that seed; so
that, when the written word leaves a heavenly print of grace upon the
heart, it argues that it is of divine authority. If you were to profit
by the word, you must believe it is of God.&lt;/p&gt;
&lt;p&gt;Some skeptics question the verity of Scripture. Though they have the
articles of religion in their creed, yet they do not have it in their
belief. Unbelief enervates the virtue of the word and makes it abortive.
Who will obey truths that he does not believe? Heb. 4.2: &amp;ldquo;The word did
not profit them, not being mixed with faith.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11.&lt;/strong&gt; &lt;em&gt;Highly prize the Scriptures.&lt;/em&gt; Psalm 119.72: &amp;ldquo;The law of your
mouth is better to me than thousands of gold and silver &lt;em&gt;pieces&lt;/em&gt;.&amp;rdquo; Can
someone be proficient in any art if he slights and depreciates it? Prize
this book of God above all other books. Gregory calls the Bible the
heart and soul of God. The Rabbies say that there is a mountain of sense
upon every point and tittle of Scripture. Psalm 19.7: &amp;ldquo;The law of the
Lord is perfect.&amp;rdquo; The Scripture is the library of the Holy Ghost; it is
a code of divine knowledge, an exact model and platform of religion. The
Scripture contains in it the &lt;em&gt;Credenda&lt;/em&gt;, the things which we are to
believe, and the &lt;em&gt;Agenda&lt;/em&gt;, the things which we are to practise; it is
able to make us wise unto salvation. 2Tim. 3.15. The Scripture is the
standard of truth, the judge of controversy; it is the pole star to
direct us to heaven. The Scripture is the compass by which the rudder of
our will is to be steered; it is the field in which Christ, the pearl of
price, is hidden; it is a rock of diamond; it is a sacred Collyrium, or
eye-salve; it mends the eyes of those who look upon it; it is a
spiritual optic glass in which the glory of God is resplendent; it is
the panacea, or universal medicine for the soul. The leaves of Scripture
are like &amp;ldquo;the leaves of the tree of life for the healing of the
nations.&amp;rdquo; Rev. 22.2. The Scripture is the breeder and feeder of grace.
How is the convert born, but by &amp;ldquo;the word of truth?&amp;rdquo; James 1.18. How
does he grow but by &amp;ldquo;the sincere milk of the word?&amp;rdquo; 1Peter 2.2. The
written word is the book out of which our evidences for heaven are
fetched; it is the sea-mark which shows us the rocks of sin; it is the
antidote against error and apostasy; the two-edged sword which wounds
the old serpent. It is our bulwark to withstand the force of lust, like
the Capitol at Rome, which was a place of strength and ammunition. The
Scripture is the tower of David on which the shields of our faith hang.
Take away the word, and you deprive us of the sun, said Luther. The
written word is above an angelical embassy, or a voice from heaven. 2
Peter 1.18: &amp;ldquo;We heard this voice which came from heaven; we also have a
more sure word.&amp;rdquo; If Caesar so valued his Commentaries, that in
preserving them he lost his purple robe, how should we estimate the
sacred oracles of God? Job 23.12: &amp;ldquo;I have esteemed the words of his
mouth more than my necessary food.&amp;rdquo; King Edward VI, on the day of his
coronation, had three swords presented to him, signifying that he was
monarch of three kingdoms. The King said there was one sword missing;
being asked what that was, he answered, &amp;ldquo;the Holy Bible, which is the
sword of the spirit, and it is to be preferred before all these ensigns
of royalty.&amp;rdquo; Robert, King of Sicily, so prized God&amp;rsquo;s word that, speaking
to his friend Petrarch, he said, &amp;ldquo;I protest that the Scriptures are
dearer to me than my kingdom, and if I must be deprived of one of them,
I would rather lose my diadem than the Scriptures.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12.&lt;/strong&gt; &lt;em&gt;Get an ardent love for the word&lt;/em&gt;. Prizing something relates to
the judgment; love relates to the affections. Psalm 119.159: &amp;ldquo;Consider
how I love your precepts.&amp;rdquo; He is likely to grow rich who delights in his
trade; a lover of learning will be a scholar. Austin tells us that
before his conversion he took no pleasure in the Scriptures, but
afterwards they were his delights. David thought the word was sweeter
than the honey which drips from the comb. Thomas `a Kempis used to say
he found no contentment except in a corner, with the book of God in his
hand. Did Alphonsus, King of Sicily, recover from a fit of sickness
because of the great pleasure he took in reading Quintus Curtius? Then
what infinite pleasure we should take in reading the book of life! There
is enough in the word to breed holy satisfaction and delight; it is a
specimen and demonstration of God&amp;rsquo;s holy love for us. The Spirit is
God&amp;rsquo;s love-token; the word is his love letter; and how one rejoices to
read over his friend&amp;rsquo;s letter! The written word is a divine treasury, or
store-house. Truth is scattered in it like pearls to adorn the inner man
of his heart. The written word is the true manna, which has all sorts of
sweet taste in it. It is a sovereign elixir; it gives wine to those with
a heavy heart. I have read of an ancient Rabbi, who, in a great
concourse of people, proclaimed a sovereign cordial which he had to
sell. Many resorted to him and asking him to show it, &lt;em&gt;he opened the
Bible&lt;/em&gt;, and directed them to several places of comfort in it. Holy David
drank of this cordial; Psalm 119.50: &amp;ldquo;This is my comfort in my
affliction; for your word has quickened me.&amp;rdquo; Chrysostom compares the
Scripture to a garden; every line in it is a fragrant flower which we
should not wear in our pocket but in our heart.&lt;/p&gt;
&lt;p&gt;Delight in the word causes profit: and we must not love only the
&lt;em&gt;comforts&lt;/em&gt; of the word, but the &lt;em&gt;reproofs&lt;/em&gt;. Myrrh is bitter to the
palate, but good for the stomach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13.&lt;/strong&gt; &lt;em&gt;Come to the reading of the word with honest hearts&lt;/em&gt;. Christ
speaks of the &amp;ldquo;honest heart.&amp;rdquo; Luke 8.15.&lt;/p&gt;
&lt;p&gt;What is it to read the word with an honest heart?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;To come with a heart willing to know the whole counsel of God. A good
heart would not have any truth concealed, but says as Job did: &amp;ldquo;What
I do not see, teach me.&amp;rdquo; When men pick and choose in religion, they
will do some things that the word enjoins them from, but not others.
These are unsound hearts, and they are not benefited by holy writ.
These are like a patient, who having a bitter pill prescribed, and a
mint julep, he will take the julep, but he refuses the pill.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To read the word with an honest heart is to read that we may be made
better by it. The word is the medium and method of sanctification,
and we come to it not only to &lt;em&gt;illuminate&lt;/em&gt; but to &lt;em&gt;consecrate&lt;/em&gt;
ourselves. John 17.17: &amp;ldquo;Sanctify them through your truth.&amp;rdquo; Some go to
the Bible as one goes to the garden to pick flowers, i.e. for fine
notions. Austin confesses that before his conversion he went to hear
Ambrose, more for the elegant speech and quaint notions, than for the
spirituality of the matter. This is like a woman that paints her
face, but neglects her health. But this is to have an honest heart:
when we come to the Scriptures as Naaman came to the waters of the
Jordan, to be healed of our leprosy. &amp;ldquo;O,&amp;rdquo; says the soul, &amp;ldquo;that the
sword of the spirit may pierce the rock of my heart; that this
blessed word may have such a virtue in it as the water of jealousy,
to kill and make fruitful, that it may kill my sin, and make me
fruitful in grace.&amp;rdquo; Num. 5.27.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;14.&lt;/strong&gt; &lt;em&gt;Learn to apply Scripture&lt;/em&gt;; take every word as spoken to
yourselves. When the word thunders against sin, think this way: God
means &lt;em&gt;my&lt;/em&gt; sins; when it presses any duty, God intends &lt;em&gt;me&lt;/em&gt; in this
word. Many put off Scripture from themselves, as if it only concerned
those who lived in the time when it was written; but if you intend to
profit by the word, then bring it home to yourselves. A medicine will do
no good unless it is applied. The saints of old took the word as if it
had been spoken to them by name. When king Josiah heard the threatening
which was written in the book of God, he applied it to himself; he &amp;ldquo;rent
his clothes and humbled his soul before the Lord.&amp;rdquo; 2 Kings 22.11.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15.&lt;/strong&gt; &lt;em&gt;Observe the preceptive part of the word, as well as the
promissory&lt;/em&gt;. The precepts carry duty in them, like the veins which carry
the blood; the promises carry comfort in them, like the arteries which
carry the spirit. Make use of the &lt;em&gt;precepts&lt;/em&gt; to direct you, and the
&lt;em&gt;promises&lt;/em&gt; to comfort you. Those who keep their eye on the promise,
while neglecting the command, are not edified by Scripture; they look
more for comfort than duty. They mistake their comforts, just as Apollo
embraced the laurel tree instead of Daphne. The body may be swelled with
wind as well as flesh; a man may be filled with false comfort, as well
as that which is genuine and real.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16.&lt;/strong&gt; &lt;em&gt;Let your thoughts dwell on the passages of Scripture that
matter&lt;/em&gt;. The bee fastens on those flowers where she may suck the most
sweetness. Though the whole context of Scripture is excellent, yet some
parts of it may have a greater emphasis, and be more quick and pungent.
Reading the names of the tribes, or the genealogies of the patriarchs,
is not of the same importance as faith and the new creature. Mind the
&amp;ldquo;great things of the law.&amp;rdquo; Hosea 8.12. Those who read only to satisfy
their curiosity, busy themselves, rather than profit themselves.
Searching too far into Christ&amp;rsquo;s &lt;em&gt;temporal&lt;/em&gt; reign has weakened his
&lt;em&gt;spiritual&lt;/em&gt; reign in some men&amp;rsquo;s hearts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17.&lt;/strong&gt; &lt;em&gt;Compare yourself with the word&lt;/em&gt;. See how the Scripture and your
hearts agree; how your dial goes with this sun. Are your hearts, as it
were, a transcript and a counterpart of Scripture? Is the word copied
out into your hearts? The word calls for humility; are you not only
&lt;em&gt;humbled&lt;/em&gt;, but also &lt;em&gt;humble&lt;/em&gt;? The word calls for regeneration: John 3.7;
do you have a change of heart? Is there not only a moral and partial
change, but a spiritual change? Is there such a change wrought in you,
that it is as if another soul lived in the same body? 1 Cor. 6.11: &amp;ldquo;Such
were some of you, but you are washed, you are sanctified.&amp;rdquo; The word
calls for love toward the saints; 1Peter 1.22. Do you love grace where
you see it? Do you love grace in a poor man as well as in a rich man? A
son loves to see his father&amp;rsquo;s picture, though hung in a poor frame.&lt;/p&gt;
&lt;p&gt;Do you love grace even though it is mixed with some failings, just as we
love gold even while it is in the ore? Bringing the rule of the word and
our hearts together, to see how they agree, would prove very
advantageous to us. By this we come to know the true complexion and
state of our souls, and we see what evidences and certificates we have
for heaven.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18.&lt;/strong&gt; &lt;em&gt;Take special notice of those Scriptures which speak to your
particular case&lt;/em&gt; If a person with consumption were to read Galen or
Hippocrates, he would chiefly observe what they said about consumption.
Great regard is to be had to those paragraphs of Scripture which are
most appropriate to one&amp;rsquo;s present case.&lt;/p&gt;
&lt;p&gt;I will note three cases: 1. Affliction. 2. Desertion. 3. Sin.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;1. Affliction&lt;/em&gt;. Has God made your chain heavy? Consult these
Scriptures. Heb. 12.7: &amp;ldquo;If you endure chastening, God deals with you as
sons.&amp;rdquo; Isaiah 27.9: &amp;ldquo;By this the iniquity of Jacob shall be purged.&amp;rdquo;
John 16.20: &amp;ldquo;Your sorrow shall be turned into joy.&amp;rdquo; 2Cor. 4.17: &amp;ldquo;Our
light affliction, which is but for a moment, works for us a far more
exceeding and eternal weight of glory.&amp;rdquo; The limner lays his gold on dark
colors; God first lays the dark color of affliction, and then the golden
color of glory.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;2. Desertion&lt;/em&gt;. Are your spiritual comforts eclipsed? See Isaiah 54.8:
&amp;ldquo;In a little wrath I hid my face from you, for a moment; but with
everlasting kindness I will have mercy on you.&amp;rdquo; The sun may hide itself
in a cloud, but it is not out of the firmament; God may hide his face,
but he is not out of the covenant. Isaiah 57.16: &amp;ldquo;I will not be always
angry, for the spirits would fail before me, and the souls which I have
made.&amp;rdquo; God is like the musician; he will not stretch the strings of his
lute too hard, lest they break. Psalm 97.11: &amp;ldquo;Light is sown for the
righteous.&amp;rdquo; A saint&amp;rsquo;s comfort may be hidden as seed is hidden under the
clods of dirt; but at last it will spring up into a harvest of joy.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;3. Sin&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Are you drawn away with lust? Read Gal. 5.24; James 1.15; 1Peter
2.11: &amp;ldquo;Abstain from fleshly lusts, which war against the soul.&amp;rdquo; Lust
kills with embracing. Prov. 7.10, 22, 23; Prov. 22.14: &amp;ldquo;Go to the
waters of the sanctuary to quench the fire of lust.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Are you under the power of unbelief? Read Isaiah 26.3: &amp;ldquo;You will keep
him in perfect peace whose mind is stayed on you, because he trusts
in you.&amp;rdquo; Mr. Boltor speaks of a distressed soul who found much
comfort from this Scripture on his sick bed. 2 Samuel 22.31: &amp;ldquo;The
word of the Lord is tested; he is a buckler to all that trust in
him.&amp;rdquo; John 3.15: &amp;ldquo;That whoever believes in him should not perish.&amp;rdquo;
Unbelief is a God-affronting sin. 1John 5.10: &amp;ldquo;He that does not
believe God has made him a liar.&amp;rdquo; It is a soul-murdering sin. John
3.36: &amp;ldquo;He that does not believe the Son shall not see life, but the
wrath of God abides on him.&amp;rdquo; Thus, in reading, observe those
Scriptures which touch upon your particular case. Although all of the
Bible must be read, yet be sure to put a special star upon those
texts which point most directly to your condition,.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;19.&lt;/strong&gt; &lt;em&gt;Take special notice of the examples in Scripture;&lt;/em&gt; make the
examples of others living sermons to you.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Observe the examples of God&amp;rsquo;s judgments upon sinners. They have been
hanged up in chains, as a terror. How severely God has punished proud
men! Nebuchadnezzar was turned to eat grass; Herod was eaten up with vermin.
How God has plagued idolaters! Numbers 25.3, 4, 9; 1Kings 14.9, 10. What
a swift witness he has been against liars! Acts 5.5, 10. These examples
are set up as sea-marks to avoid. 1Cor. 10.11; Jude 7.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Observe the examples of God&amp;rsquo;s mercy to saints. Jeremiah was preserved
in the dungeon; the three children in the furnace; Daniel in the
lion&amp;rsquo;s den. These examples are props to our faith, and spurs to
holiness.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;20.&lt;/strong&gt; &lt;em&gt;Do not stop reading in the Bible, till you find your hearts
warmed&lt;/em&gt;. Psalm 119.93: &amp;ldquo;I will never forget your precepts, for you have
made me alive with them.&amp;rdquo; Read the word not only as a history, but
strive to be affected with it. Let it not only &lt;em&gt;inform&lt;/em&gt; you, but
&lt;em&gt;inflame&lt;/em&gt; you. Jer. 23.29: &amp;ldquo;Is not my word like a fire?&amp;rdquo; says the Lord.
Do not go from the word till you can say as those disciples said, Luke
24.32: &amp;ldquo;Did our hearts not burn within us?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21.&lt;/strong&gt; &lt;em&gt;Begin the practice of what you read.&lt;/em&gt; Psalm 119.66: &amp;ldquo;I have done
your commandments.&amp;rdquo; A student in medicine does not satisfy himself to
read over a system or body of medicine, but he begins its practice. The
life-blood of religion lies in the practical part. So it is in this
text: &amp;ldquo;He shall read in the book of the law all the days of his life, so
that he may learn to keep all the words of this law and these statutes,
to do them.&amp;rdquo; &lt;em&gt;Christians should be walking Bibles&lt;/em&gt;. Xenophon said many
read the laws of Lycurgus, but few observed them. The written word is
not only a rule of knowledge, but a rule of obedience; it is not only to
mend our sight but to mend our pace. David calls God&amp;rsquo;s word a &amp;ldquo;lamp to
his feet.&amp;rdquo; Psalm 119.105. It was not only a light to his eyes to &lt;em&gt;see&lt;/em&gt;
by, but to his feet to &lt;em&gt;walk&lt;/em&gt; by; by practice we trade the talent of
knowledge, and turn it to profit. This is a blessed reading of the
Scriptures, when we fly from the sins which the word forbids, and
espouse the duties which the word commands. &lt;em&gt;Reading without practice
will be but a torch to light men to hell&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22.&lt;/strong&gt; &lt;em&gt;Make use of Christ&amp;rsquo;s prophetical office&lt;/em&gt;. He is the lion of the
tribe of Judah, to whom it is given to open the book of God, and loose
its seals. Rev. 5.5.&lt;/p&gt;
&lt;p&gt;Christ so teaches, as he enlivens. John 8.12. The philosopher says,
light and heat increase together. It is true here: where Christ comes
into the soul with his light, there is the heat of spiritual life going
along with it. Christ gives us a taste of his word: &amp;ldquo;You have taught me
how sweet your words are to my taste.&amp;rdquo; Psalm 119.102, 103. It is one
thing to read a promise, and another to taste it. Those who would be
proficient in Scripture, let them get Christ to be their teacher. Luke
24.45: &amp;ldquo;Then he opened their understanding, that they might understand
the Scriptures.&amp;rdquo; Christ not only opened the Scriptures, but he opened
their understanding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23.&lt;/strong&gt; &lt;em&gt;Tread often upon the threshold of the sanctuary.&lt;/em&gt; Wait
diligently upon a rightly constituted ministry. Prov 8.34: &amp;ldquo;Blessed is
the man who hears me, watching daily at my gates.&amp;rdquo; Ministers are God&amp;rsquo;s
interpreters; it is their work to open and expound dark places in
Scripture. We read of &lt;em&gt;pitchers&lt;/em&gt; and &lt;em&gt;lamps&lt;/em&gt; within those pitchers.
Judges 7.16. Ministers are earthen pitchers. 2Cor. 4.7. But these
pitchers have lamps within them to light souls in the dark.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24.&lt;/strong&gt; &lt;em&gt;Pray that God will make you profit&lt;/em&gt;. Isaiah 48.17: &amp;ldquo;I am the Lord
your God, who teaches you to profit.&amp;rdquo; Make David&amp;rsquo;s prayer: &amp;ldquo;Open my
eyes, that I may behold wondrous things out of your law.&amp;rdquo; Psalm 119.18.
Pray to God to take the veil off the Scriptures, so that you may
understand it; and the veil on your heart, so that you may believe it.
Pray that God will not only give you his word as a &lt;em&gt;rule&lt;/em&gt; of holiness,
but his grace as a &lt;em&gt;principle&lt;/em&gt; of holiness. Implore the guidance of
God&amp;rsquo;s Spirit. Neh. 9.20: &amp;ldquo;You gave them your good Spirit to instruct
them.&amp;rdquo; Though the ship has a compass to sail by, and a store of tackle,
yet without a gale of wind it cannot sail. Though we have the written
word as our compass to sail by, and we make use of our endeavors as the
tackle, yet unless the Spirit of God blows on us, we cannot sail with
profit. When the Almighty is like &lt;em&gt;dew&lt;/em&gt; to us, then &amp;ldquo;we grow as the
lily, and our beauty is like the olive tree.&amp;rdquo; Hosea 14.5, 6: by the
anointing of the Holy Ghost. One may see the figures on a sun-dial, but
he cannot tell how the day goes unless the sun shines. We may read many
truths in the Bible, but we cannot know them savingly till God&amp;rsquo;s Spirit
shines into our souls. 2Cor. 4.6. The Spirit is a Spirit of wisdom and
revelation. Eph. 1.17. When Philip joined himself to the eunuch&amp;rsquo;s
chariot, then the eunuch understood the Scriptures. Acts 8.35. When
God&amp;rsquo;s Spirit joins himself to the word, then it will be effectual to
salvation. These rules being observed, the written word would, through
God&amp;rsquo;s blessing, become an &amp;ldquo;engrafted word.&amp;rdquo; James 1.21. A good shoot
grafted into a bad stock, changes the nature of the stock, and makes it
bear sweet and generous fruit. So when the word is grafted savingly into
men&amp;rsquo;s hearts, it sanctifies them, and makes them bring forth the sweet
&amp;ldquo;fruits of righteousness.&amp;rdquo; Phil. 1.11.&lt;/p&gt;
&lt;p&gt;Thus I have answered this question, how we may read the Scriptures with
the most spiritual profit. In conclusion,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Do not content yourselves with the bare reading of the Scriptures,
but labor to find some spiritual improvement and profit from it. Get
the word transcribed into your hearts. Psalm 37.31: &amp;ldquo;The law of his
God is in his heart.&amp;rdquo; Those who profit by reading the book of God are the best Christians
alive; they answer God&amp;rsquo;s cost, they credit religion, and they save their
souls.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Those of you who have profited by reading the Holy Scriptures, adore
God&amp;rsquo;s distinguishing grace. Bless God that he has not only brought
the light to you, but opened your eyes to see it; that he has
unlocked his &lt;em&gt;hidden&lt;/em&gt; &lt;em&gt;treasure&lt;/em&gt;, and enriched you with saving
knowledge. Some perish by not &lt;em&gt;having&lt;/em&gt; Scripture, and others by not
&lt;em&gt;improving&lt;/em&gt; it. Bless God that he should pass by millions, and the
lot of his electing love should fall upon you; that the Scripture,
like the &lt;em&gt;pillar of cloud&lt;/em&gt;, should have a &lt;em&gt;dark&lt;/em&gt; &lt;em&gt;side&lt;/em&gt; to others,
but a &lt;em&gt;light&lt;/em&gt; &lt;em&gt;side&lt;/em&gt; to you; that to others it is a &lt;em&gt;dead letter&lt;/em&gt;,
but to you it is the &lt;em&gt;savor of life&lt;/em&gt;; that Christ should not only be
revealed &lt;em&gt;to&lt;/em&gt; you, but &lt;em&gt;in&lt;/em&gt; you; Gal. 1.16. &amp;mdash;You should be in an
holy ecstacy of wonder, and wish that you had the hearts of seraphims
burning in love toward God, and the voices of angels to make heaven
ring with God&amp;rsquo;s praises!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But some of the godly may say they fear that they do not profit by the
word they read. As in the body, when the vital spirits become faint,
cordials are employed. So let me apply a few divine cordials to those
who are ready to faint under the fear of their non-proficiency [in the
Scriptures].&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You may profit by reading the word, even though you come short of
others. The ground which brought forth thirty-fold was good ground.
Matt. 13.8. Do not say you obtained no profit because you are not
equal with other eminent saints; the others among David&amp;rsquo;s worthies
were considered his strong men, even though they did not attain the
honor of the first three. 2Sam. 23.19.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You may profit by reading the word, even though you are not quick to
apprehend. Some impeach themselves because they are slow of
understanding. When our blessed Saviour told of his sufferings to
come, the apostles themselves did not understand, and it was &lt;em&gt;hidden
from them&lt;/em&gt;. Luke 9.45. The author to the Hebrews speaks of some who
were dull of hearing. Heb. 5.11. Those who have weaker judgments may
have stronger affections. A Christian with a little knowledge may be
kept from sin, like a man with weak sight may be kept by a little
knowledge from falling into the water.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You may profit by reading Scripture, although you do not have
&lt;em&gt;excellent memories&lt;/em&gt;. Many complain that their memories leak.
Christian, are you grieved that you can no longer remember? Then for
your comfort,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You may have a good &lt;em&gt;heart&lt;/em&gt;, even though you do not have so good a &lt;em&gt;memory&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Though you cannot remember all you read, yet you remember what
matters most, and what you most need; at a feast we do not eat every
dish, but we take only so much as nourishes. It is with a good
Christian&amp;rsquo;s memory as it is with a lamp: though the lamp is not full
of oil, yet it has enough oil to make the lamp burn; though your
memory is not full of Scripture, yet you retain enough to make your
love towards God burn.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So then, be of good comfort. You profit by what you read, and you take
note of that encouraging Scripture, John 14.26: &amp;ldquo;The Comforter, which is
the Holy Ghost, he shall bring all things to your remembrance.&amp;rdquo; Amen.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.google.ca/books/edition/The_Bible_and_the_Closet/kk0ZAAAAYAAJ"&gt;Source&lt;/a&gt;&lt;/p&gt;</description><author>Honza Pokorný</author><pubDate>Tue, 12 Oct 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://honza.pokorny.ca/2021/10/how-we-may-read-the-scriptures-with-the-most-spiritual-profit/</guid></item><item><title>Mouse Gestures (the useful ones)</title><link>https://river.me/blog/mouse-gestures/</link><description>My Gesturefy bindings in Firefox and how I use them when working with MediaWiki</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 12 Oct 2021 08:06:54 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/mouse-gestures/</guid></item><item><title>Book review - Free to All</title><link>https://river.me/blog/book-review-free-to-all/</link><description>A review and chapter summaries of &amp;ldquo;Free to All: Carnegie Libraries &amp;amp; American Culture, 1890-1920&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 12 Oct 2021 07:26:12 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-free-to-all/</guid></item><item><title>Can Underground Agriculture Solve the World Food Problem?</title><link>https://www.tderflinger.com/en/underground-agriculture-world-food</link><description>Underground agriculture is an emerging field of agriculture that has the potential to help solve a big part of the current ecological crisis. How this can be achieved I want to explain in this article.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Tue, 12 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/underground-agriculture-world-food</guid></item><item><title>I’m a Rails developer—what the heck is Storybook, and should I care?</title><link>https://phacks.dev/articles/im-a-rails-developer-what-the-heck-is-storybook-and-should-i-care</link><description>A lightly edited version of our conversation that tries to answer the following questions: what is Storybook? How useful can it be in a Rails project? And maybe most importantly: as a Rails developer, should I care?</description><author>Nicolas Goutay</author><pubDate>Tue, 12 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://phacks.dev/articles/im-a-rails-developer-what-the-heck-is-storybook-and-should-i-care</guid></item><item><title>LIT Home</title><link>https://taylor.town/lit-home</link><description>&lt;p&gt;&lt;img alt="LIT Home" src="https://taylor.town/LIT%20Home%20%5BExterior%5D.jpg" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;My friends and I converted a shed into a tiny-home!&lt;/p&gt;
&lt;/blockquote&gt;</description><author>taylor.town</author><pubDate>Tue, 12 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://taylor.town/lit-home</guid></item><item><title>Exponential growth and shipping - 123dev #37</title><link>https://justingarrison.com/123dev/2021-10-12-123dev-37/</link><description>&lt;img alt="A timelaps illustration of the Titanic ship sinking which took over two hours" src="https://justingarrison.com/img/123dev/37-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="shipping-products"&gt;Shipping products&lt;/h2&gt;
&lt;p&gt;This past week I was part of the team that launched EKS Anywhere. Launching an open source project was something I really wanted to do to see how it was different from products and services I’ve helped launch in the past.&lt;/p&gt;
&lt;p&gt;The biggest difference for me was it’s really refreshing to point people to GitHub for issues and to let people use the software for free. The users I got to talk to were fellow infrastructure engineers which also made it really easy to talk about the project because I am personally familiar to the problems it solves.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 12 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-12-123dev-37/</guid></item><item><title>Surveillance cameras are just more social media</title><link>https://stop.zona-m.net/2021/10/surveillance-cameras-are-just-more-social-media/</link><description>&lt;p&gt;They have the same defects, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 12 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/surveillance-cameras-are-just-more-social-media/</guid></item><item><title>[The World] Requiem for Strawberry</title><link>https://www.devever.net/~hl/strawberry_farewell</link><description>&lt;p&gt;Strawberry passed away today, on the 11th of October 2021, at about 7:25am, UK
time. She died of heart failure; she died in one's arms. It was quick and unfraught,
and she was with her family until the end, as she had always wanted.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Mon, 11 Oct 2021 20:17:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/strawberry_farewell</guid></item><item><title>Thoughts on Redux</title><link>https://everttimberg.io/blog/thoughts-on-redux/</link><description>Redux is a state management library that is commonly used in React apps. Redux was created in 2015 and almost immediately became a common sight in React apps. This may have been due to the fact that many of the React docs assumed that Redux was installed along with the popularity of boilerplates that included Redux by default. As a result of this, many front-end developers including myself, incorrectly assumed that React had to be used with Redux.</description><author>Posts on Evert Timberg</author><pubDate>Mon, 11 Oct 2021 17:42:47 GMT</pubDate><guid isPermaLink="true">https://everttimberg.io/blog/thoughts-on-redux/</guid></item><item><title>Next.js Cheatsheet - handy snippets and tips</title><link>https://gourav.io/blog/nextjs-cheatsheet</link><description>Setup debugging, custom domains, Tailwind CSS, SEO, Typescript, ESLint, analytics, and much more to Next.js project</description><author>Gourav Goyal</author><pubDate>Mon, 11 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://gourav.io/blog/nextjs-cheatsheet</guid></item><item><title>Please don't argue with the weather</title><link>http://jeremymikkola.com/posts/2021_10_11_weather_and_estimates.html</link><description>&lt;div class="info"&gt;
    Posted on October 11, 2021
    
&lt;/div&gt;

&lt;p&gt;Say you have a vacation planned next week. You are going south to a fun coastal town renown for its
beaches. You’re hoping to get outside, get some sun, and stick your toes in the water while drinking
something with too much sugar and alcohol&lt;a class="footnote-ref" href="#fn1" id="fnref1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hoping for good weather, you turn on the news. The meteorologist says it will be 50 F and
cloudy. This isn’t what you were hoping for! Sitting on the beach won’t feel right in drab weather
like that.&lt;/p&gt;
&lt;p&gt;Think about how you respond to this situation: do you try to argue with the meteorologist? Do you
say, “how about we actually do 72 F and sunny?” No, that would be silly. A forecast isn’t a decision
the meteorologist is making. It’s merely a prediction about what will happen. Predicting a different
temperature will do nothing to how the air will feel.&lt;/p&gt;
&lt;p&gt;No, if you want different weather next week, your only option is to go to a different place. You
obviously can’t argue with the weather.&lt;/p&gt;
&lt;p&gt;Yet in software companies, we often try to do exactly that. When an engineer says that a project
will take 7 weeks, someone is sure to argue and ask for it to be done in 5. But changing the
estimate written down on paper doesn’t change how hard the project is to accomplish. It doesn’t
remove time waiting for other teams or mitigate project risks. It doesn’t make the code cleaner, the
tests run faster, or magically document undocumented libraries. It is mere arguing with the weather.&lt;/p&gt;
&lt;p&gt;If you want a different estimate for the end of a project, the only option is to change where that
project is going. Choosing to build something smaller or different can (sometimes!) give you more
favorable estimates. It helps to know the whole weather map, not just the weather in that one
town.&lt;/p&gt;
&lt;p&gt;But sometimes it is just plain cold everywhere. There’s no law of physics that says there must exist
a spot on the map with a temperature you like. If the system is too complex and too full of tech
debt, there might not be any meaningful options that can be accomplished in 5 weeks. Pushing back on
the person who tells you this won’t change the facts.&lt;/p&gt;
&lt;p&gt;If you are especially wise, you may notice a pattern where the temperature is frequently not what
you want. You realize that you need to make a systematic change in how you are planning your
vacations. Perhaps your yearly beach vacation needs to move from January to August. In software,
this might look like giving a team time to pay down tech debt so that they can move faster later,
staffing a team they get blocked by, or involving the team sooner in the new product.&lt;/p&gt;
&lt;p&gt;Please don’t argue with the weather. If you don’t like the temperature, change where you are going.&lt;/p&gt;
&lt;section class="footnotes footnotes-end-of-document" id="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn1"&gt;&lt;p&gt;I can’t recall speaking with anyone whose vacation plans actually look like this&lt;a class="footnote-back" href="#fnref1"&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>jeremymikkola.com</author><pubDate>Mon, 11 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://jeremymikkola.com/posts/2021_10_11_weather_and_estimates.html</guid></item><item><title>Tools and workflows - 123dev #36</title><link>https://justingarrison.com/123dev/2021-10-11-123dev-36/</link><description>&lt;img alt="Someone created a real time ray tracing program in excel. This shows balls" src="https://justingarrison.com/img/123dev/36-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="tools-youre-familiar-with"&gt;Tools you’re familiar with&lt;/h2&gt;
&lt;p&gt;The right tool for the job isn’t always the best tool. Some tools are made for a specific reason, but there is a learning curve and cognitive tax to know how to use new tools. Sometimes the right tool is the one that is good enough that you already know.&lt;/p&gt;
&lt;p&gt;The hardest part is knowing when to switch tools. If you work on a team this means updating documentation and training and maybe convincing the rest of your team the switch will be worth it.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 11 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-11-123dev-36/</guid></item><item><title>Golang pretty print</title><link>https://yasha.solutions/posts/golang-pretty-print/</link><description>A quick snippet to get some nicer debug of json and other mapped values.
1 2 3 4 5 6 7 8  //PrettyPrint print any interface func PrettyPrint(data interface{}) (err error) { indentedData, err := json.MarshalIndent(data, "", " ") if err == nil { fmt.Println(string(indentedData)) } return }</description><author>Yasha Solutions</author><pubDate>Mon, 11 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/golang-pretty-print/</guid></item><item><title>OBS Studio Virtual Camera on FreeBSD</title><link>https://www.davidschlachter.com/misc/freebsd-obs-virtualcam</link><description>Use the OBS Virtual Camera on FreeBSD using webcamd.</description><author>David Schlachter</author><pubDate>Mon, 11 Oct 2021 00:15:00 GMT</pubDate><guid isPermaLink="true">https://www.davidschlachter.com/misc/freebsd-obs-virtualcam</guid></item><item><title>Radio silence</title><link>https://rob.sh/post/where-to-find-us/</link><description>&lt;p&gt;For more regular updates, you can generally find &lt;a href="https://instagram.com/robjs"&gt;me on
instagram&lt;/a&gt;, or &lt;a href="https://instagram.com/heloisewoof"&gt;Héloïse on
instagram&lt;/a&gt;.&lt;/p&gt;
&lt;center&gt;
&lt;img src="https://cdn.rob.sh/img/heloise-mt-tam.jpg" style="width: 40vw;" /&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;Héloïse on Mt. Tam at Sunrise - October 2021&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;/center&gt;</description><author>rob.sh</author><pubDate>Sun, 10 Oct 2021 22:45:00 GMT</pubDate><guid isPermaLink="true">https://rob.sh/post/where-to-find-us/</guid></item><item><title>Sending emails to myself</title><link>https://voussoir.net/writing/emailing_myself</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;Sending emails to myself&lt;/h1&gt;
&lt;p&gt;In April, I wrote &lt;a href="https://git.voussoir.net/voussoir/voussoirkit/src/branch/master/voussoirkit/operatornotify.py"&gt;operatornotify.py&lt;/a&gt;, a module which allows my programs to notify me of important information or errors. I was immediately very proud of it. It felt awesome to get so much impact out of a sub-100 line module as I went around hooking it up to my existing programs and getting the tracebacks from my horribly broken code delivered right to me.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/emailing_myself/ycdl_errors.png" title="there's more" /&gt;&lt;/p&gt;
&lt;p&gt;Since then, I have figured out which parts of the interface are most important, and boiled them down into a single decorator, so that adding &lt;code&gt;@operatornotify.main_decorator&lt;/code&gt; is all it takes to get emails from a program. It's probably the best bang-for-buck code I've written in a while.&lt;/p&gt;
&lt;div class="highlight python"&gt;&lt;pre&gt;&lt;span class="nd"&gt;@operatornotify&lt;/span&gt;&lt;span class="nd"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;main_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="narg"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myprogram.py&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="narg"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span&gt;...&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__main__&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The most important part of operatornotify's design is that it takes advantage of your existing logging infrastructure. Although you can call &lt;code&gt;operatornotify.notify&lt;/code&gt; whenever you want, it's much easier to add a log handler that captures WARNING level messages automatically. This has encouraged me to revisit some older programs that just used print statements and replace them with proper logging, which makes them easier to use and debug in general.&lt;/p&gt;
&lt;p&gt;There are a few pain points that arise from using operatornotify as a log handler. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When using &lt;code&gt;@main_decorator&lt;/code&gt;, you have to pick a subject line ahead of time and can't change it later. But to improve the skimmability of emails in my inbox, I like to prefix the subject lines with &lt;code&gt;⭕︎&lt;/code&gt; if everything went okay &lt;a class="footnote_link" href="#footnote_text_1" id="footnote_link_1" title="[1] I wanted to use a checkmark, but the fonts on Android make all of the unicode check marks look stupendously ugly. And Android doesn't let me change the system font outside of Daddy Google's approved list because I'm a big stupid dummy baby who can't be trusted with a ttf because I might get a boo-boo."&gt;[1]&lt;/a&gt; and &lt;code&gt;✕&lt;/code&gt; if there was a warning, which obviously is not known until the end of runtime. I can imagine a few solutions but haven't decided which is most clean and appropriate for inclusion in the module &lt;a class="footnote_link" href="#footnote_text_2" id="footnote_link_2" title="[2] I am using a stopgap solution in my_operatornotify that detects the phrase &amp;quot;Program finished, returned 0&amp;quot; to add the ⭕︎."&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not all messages cleanly fit into the integer levels of logging. For the programs I run as scheduled tasks, it's nice to receive some kind of message to know that they ran successfully and didn't just fail to launch, but I don't necessarily want to see all of their output since I trust the returned status of 0. So neither &lt;code&gt;--operatornotify-level WARNING&lt;/code&gt; or &lt;code&gt;INFO&lt;/code&gt; is really ideal.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But these inconveniences are vastly outweighed by the ability to add a single decorator function that immediately supercharges all of the existing log lines and encourages me to add better logging where I skimped on it earlier.&lt;/p&gt;
&lt;p&gt;Then I took the next step towards this summit of email enlightenment: running the mail server myself. I figure there are three reasons I should self-host the mail for operatornotify:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The notifications usually contain private information or filepaths that I don't like to have sitting on a third-party server. I don't think these worries are justified since my email provider does at-rest encryption and uses SSL, but I just don't like it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My code might wind up sending hundreds of emails in a row, and I don't want to do that to a third-party host.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's a good chance for me to play with stuff I haven't tried before.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I'm using the &lt;a href="https://github.com/foxcpp/maddy"&gt;Maddy&lt;/a&gt; email server, which is an all-in-one solution for both SMTP and IMAP. I also considered &lt;a href="https://mailinabox.email/"&gt;Mail-in-a-Box&lt;/a&gt;, but their documents specifically say that it's designed to have the whole machine to itself, which I don't want to do. The most widely known software is &lt;a href="http://www.postfix.org/"&gt;Postfix&lt;/a&gt; for SMTP and &lt;a href="https://www.dovecot.org/"&gt;Dovecot&lt;/a&gt; for IMAP, but I started drowning in the configuration of dovecot before even touching postfix, and gave up. I'm just a single user emailing myself. I like that maddy is just a single binary. So now I'm free to send myself as many thousands of emails as I want, containing whatever I want, using my own code and connecting to my own server.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://f-droid.org/en/packages/eu.faircode.email/"&gt;FairEmail&lt;/a&gt; on Android supports IMAP IDLE, so I can get tracebacks anytime, anywhere. It's great.&lt;/p&gt;
&lt;p&gt;Here are some ways I'm using operatornotify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;My computer emails me after it boots. I configured the BIOS to automatically turn the computer back on after a power outage occurs, so I have an email letting me know roughly when that happened and what to expect next time I'm back.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My OVH server emails itself after it boots. Then, it launches a few programs — &lt;a href="https://etiquette.voussoir.net"&gt;etiquette.voussoir.net&lt;/a&gt;, &lt;a href="https://old.reddit.com/r/GoldTesting/comments/26xset/newsletter_bot_commands/"&gt;/u/Newsletterly&lt;/a&gt;, etc. — and emails again me when all of those have finished starting and the processes appear in &lt;code&gt;pidof&lt;/code&gt;. If I accidentally break something in my Python environment that prevents those programs from working, I'll know about it before too long.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Most of my task scheduler jobs use &lt;code&gt;--operatornotify-level WARNING&lt;/code&gt;. A lot of repetetive tasks that I used to do manually are now fully automated, but if they experience any errors (usually just network timeouts or 503s), I can re-run them at my convenience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I'm working on a daily "system heartbeat" email that gives me a rundown of all the daemon processes that should always be running, and all of the task scheduler jobs that ran throughout the day. Although adding operatornotify to a program can help me get its traceback, it doesn't help me when the program is totally broken and didn't even launch. For example, when I upgrade Python and forget to &lt;code&gt;pip install&lt;/code&gt; a module, so the &lt;code&gt;ImportError&lt;/code&gt; occurs before main ever runs. I'd rather not add an intermediate launcher that tries to launch the task and notifies me if that fails.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the record, the code that actually sends the emails is in my &lt;code&gt;my_operatornotify.py&lt;/code&gt;. The basic code you need is:&lt;/p&gt;
&lt;div class="highlight python"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;email&lt;/span&gt;&lt;span class="nn"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;message&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;smtplib&lt;/span&gt;

&lt;span class="kd"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="narg"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="narg"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xxx&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;recipient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xxx&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;

    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;From&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Subject&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;To&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="narg"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTP_SSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xxx&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="narg"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="narg"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I have some additional stuff in there to deal with retrying and eventually writing the message to my desktop if the mail server is unreachable.&lt;/p&gt;
&lt;p&gt;To write the system heartbeat, I'm parsing the output of &lt;code&gt;schtasks&lt;/code&gt; with the subprocess module:&lt;/p&gt;
&lt;div class="highlight python"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;voussoirkit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;winwhich&lt;/span&gt;

&lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;winwhich&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;schtasks&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/query&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/tn&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;voussoir&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/fo&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;csv&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="narg"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STDOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="narg"&gt;creationflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_NO_WINDOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a class="footnote_text" href="#footnote_link_1" id="footnote_text_1"&gt;[1]&lt;/a&gt; I wanted to use a checkmark, but the fonts on Android make all of the unicode check marks look &lt;a href="/writing/emailing_myself/checkmarks.png"&gt;stupendously ugly&lt;/a&gt;. And Android doesn't let me change the system font outside of Daddy Google's approved list because I'm a big stupid dummy baby who can't be trusted with a ttf because I might get a boo-boo.&lt;/p&gt;
&lt;p&gt;&lt;a class="footnote_text" href="#footnote_link_2" id="footnote_text_2"&gt;[2]&lt;/a&gt; I am using a stopgap solution in &lt;code&gt;my_operatornotify&lt;/code&gt; that detects the phrase "Program finished, returned 0" to add the ⭕︎.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/emailing_myself/emailing_myself.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/3475cb0abf63172e6392487ffaf41755f71a18e3"&gt;&lt;time datetime="2023-09-18T21:01:09-07:00"&gt;2023-09-18&lt;/time&gt; Replace github links with git.voussoir.net.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/b9f257e3e625074ead95f09b8f1f0b064e6c5ca6"&gt;&lt;time datetime="2021-10-10T12:20:41-07:00"&gt;2021-10-10&lt;/time&gt; Add emailing_myself.md.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Sun, 10 Oct 2021 22:20:41 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/emailing_myself</guid></item><item><title>In memory of Paolo Brunello</title><link>https://stop.zona-m.net/2021/10/in-memory-of-paolo-brunello/</link><description>&lt;p&gt;Good deeds should not be forgotten.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 10 Oct 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/in-memory-of-paolo-brunello/</guid></item><item><title>Printable card games</title><link>http://localhost/games/cards/</link><description>&lt;h2&gt;Shephy&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://boardgamegeek.com/boardgame/150016/shephy"&gt;Shephy&lt;/a&gt; (シェフィ) was designed in 2013 by Pawn. It's a solitaire game that takes around 15 minutes to play.&lt;/p&gt;
&lt;p&gt;This is a base game reskin.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" height="198" src="http://localhost/games/data/shephy.png" width="720" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the solitaire game Shephy, you start with one sheep in your field, and you must manage the event cards presented to you in order to have one thousand sheep in the field before you go through the deck of event cards three times.&lt;/p&gt;
&lt;p&gt;The sheep deck consists of values 1, 3, 10, 30, 100, 300 and 1,000; at the start of the game, you place a 1 sheep card in your field, then shuffle the 22 event cards and lay out five cards to form your hand. On a turn, you choose one card, carry out its effect, then refill your hand to five cards. Event cards might duplicate existing sheep cards, add new ones, swap one value of sheep for another, remove sheep, etc. Play through the deck until you have no cards in hand, then shuffle the event cards.&lt;/p&gt;
&lt;p&gt;If you go through the deck three times without having at least one 1,000 sheep card in the field or if at any point in the game you have no sheep in the field, then you lose.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=3UZNdNApnqE"&gt;How to play video&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Print and play my reskin: &lt;a href="http://localhost/games/cards/data/shephy.pdf"&gt;shephy.pdf&lt;/a&gt; 13 double-sided pages. 73 cards.&lt;/p&gt;
&lt;h2&gt;Murderer's Row&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://boardgamegeek.com/boardgame/185813/murderers-row"&gt;Murderer's Row&lt;/a&gt; was designed in 2015 by William Retert. It's a solitaire game that takes 10 minutes to play.&lt;/p&gt;
&lt;p&gt;This is a base game reskin with a Calvin and Hobbes theme.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" height="208" src="http://localhost/games/data/murderersrow.png" width="720" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Deal 10 cards in a row. Kill 9 of them.&lt;/p&gt;
&lt;p&gt;Each turn consists of activating a card and resolving its effect. Play proceeds until all cards except one have been eliminated or until no further moves are possible.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://localhost/games/cards/data/murderers_row_rules.pdf"&gt;rules&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Print and play my reskin: &lt;a href="http://localhost/games/cards/data/murderersrow.pdf"&gt;murderersrow.pdf&lt;/a&gt;. 3 double-sided pages. 22 cards.&lt;/p&gt;</description><author>fserb.com</author><pubDate>Sun, 10 Oct 2021 14:31:00 GMT</pubDate><guid isPermaLink="true">http://localhost/games/cards/</guid></item><item><title>Democracy vs Autocracy</title><link>https://camhashemi.com/essays/democracy-autocracy/</link><description>&lt;p&gt;A group makes countless decisions over its lifetime.&lt;br /&gt;Each decision falls on a spectrum from democratic to autocratic.&lt;/p&gt;&lt;p&gt;Some decisions are supremely autocratic, and others are purely democratic.&lt;br /&gt;Between these two extremes, there is a spectrum of consensus.&lt;/p&gt;&lt;p&gt;As much as a dictator can be popular, a democracy can be tyrannical.&lt;/p&gt;&lt;p&gt;Democracy is not inherently good as autocracy is not inherently bad.&lt;br /&gt;The success of each style depends on their execution within a context.&lt;/p&gt;&lt;h2 id="the-context"&gt;The Context&lt;/h2&gt;&lt;h3 id="autocracy"&gt;Autocracy&lt;/h3&gt;&lt;p&gt;Autocracy has the advantage of &lt;strong&gt;efficiency&lt;/strong&gt;.&lt;br /&gt;A capable dictator with the right goals can achieve them quickly.&lt;br /&gt;A plan of action from a single mind has consistency.&lt;br /&gt;Consistent, planned actions are more likely to succeed.&lt;/p&gt;&lt;p&gt;But autocracy also has the &lt;em&gt;&lt;strong&gt;disadvantage&lt;/strong&gt;&lt;/em&gt; of efficiency.&lt;br /&gt;An incompetent dictator lacks the input needed to choose the right goals,and they lack the feedback needed to correct themselves.&lt;br /&gt;So an incompetent dictator achieves the wrong goals quicker than they should and takes longer to see their mistakes.&lt;/p&gt;&lt;h3 id="democracy"&gt;Democracy&lt;/h3&gt;&lt;p&gt;Democracy has the advantage of &lt;strong&gt;information&lt;/strong&gt;.&lt;br /&gt;A healthy democracy gets input from every member of the group.&lt;br /&gt;People are encouraged to voice their perspective.&lt;br /&gt;Leaders who fail to resonate are ignored.&lt;/p&gt;&lt;p&gt;But democracy also has the &lt;em&gt;&lt;strong&gt;disadvantage&lt;/strong&gt;&lt;/em&gt; of information.&lt;br /&gt;Network complexity scales exponentially.&lt;br /&gt;Noise drowns the truth.&lt;br /&gt;Too many perspectives create a paradox of choice.&lt;/p&gt;&lt;h3 id="the-paradox"&gt;The Paradox&lt;/h3&gt;&lt;p&gt;Ironically, the success of a democracy depends on its leaders&amp;rsquo; ability to influence the majority.&lt;br /&gt;Without leaders, a democracy is chaos.&lt;br /&gt;A democracy without strong leaders is confused and uninspired.&lt;/p&gt;&lt;p&gt;And ironically, the success of an autocracy depends on its populace&amp;rsquo;s ability to influence their leader.&lt;br /&gt;Without a populace, an autocracy is nothing.&lt;br /&gt;An autocracy without a strong populace is clueless and unengaged.&lt;/p&gt;&lt;h2 id="the-balance"&gt;The Balance&lt;/h2&gt;&lt;p&gt;Democracy creates input, feedback, energy.&lt;br /&gt;Autocracy creates output, feed-forward, solidity.&lt;/p&gt;&lt;p&gt;To start, democracy collects voices and engages the group.&lt;br /&gt;Once heard, autocracy commits to an achievable plan for the group.&lt;br /&gt;Given that plan, distributing decisions maximizes the power of the group.&lt;br /&gt;To close, autocracy organizes new information and imagines the next chapter.&lt;/p&gt;&lt;p&gt;With democracy on one side and autocracy on the other,&lt;br /&gt;we should balance the scales gracefully across contexts.&lt;/p&gt;</description><author>Cam Hashemi</author><pubDate>Sun, 10 Oct 2021 03:43:33 GMT</pubDate><guid isPermaLink="true">https://camhashemi.com/essays/democracy-autocracy/</guid></item><item><title>The Mystery of the Ceaseless Botnet DDoS</title><link>https://www.marginalia.nu/log/29-botnet-ddos/</link><description>&lt;p&gt;I&amp;rsquo;ve been dealing with a botnet for the last few days, that&amp;rsquo;s been sending junk search queries at an increasingly aggressive rate. They were reasonably easy to flag and block but just kept increasing the rate until that stopped working.&lt;/p&gt;
&lt;p&gt;Long story short, my patience ran out and put my website behind cloudflare. I didn&amp;rsquo;t want to have to do this, because it does introduce a literal man in the middle and that kinda undermines the whole point of HTTPS, but I just don&amp;rsquo;t see any way around it. I just can&amp;rsquo;t spend every waking hour playing whac-a-mole with thousands of compromised servers flooding me with 50,000 search requests an hour. That&amp;rsquo;s five-six times more than when I was on the front page of HackerNews, and the attempts only increased.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sun, 10 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/29-botnet-ddos/</guid></item><item><title>Python 3.10 tips</title><link>https://blog.cetinich.net/content/2021/2021-python-tips/</link><description>Handy tips and notes about Python 30.8 10, 11 and beyond.</description><author>Brent's Blog</author><pubDate>Sun, 10 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.cetinich.net/content/2021/2021-python-tips/</guid></item><item><title>Mental health is health - 123dev #35</title><link>https://justingarrison.com/123dev/2021-10-10-123dev-35/</link><description>&lt;img alt="Gru shows off a slides of new ideas and then is surprised by domain renewals" src="https://justingarrison.com/img/123dev/35-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="stress"&gt;Stress&lt;/h2&gt;
&lt;p&gt;As a junior developer you get assigned work and have to implement it and solve problems within a certain scope. As you get more senior you have more responsibility which often comes with more visibility and more pressure. Sometimes you’re a blocker for a new project or are part of an outage. These can be stressful situations.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 10 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-10-123dev-35/</guid></item><item><title>A Web Of Solutions - October 2021</title><link>https://yasha.solutions/posts/a-web-of-solutions-october-2021/</link><description>A refactored digest of my reading of the month.
A short introduction to give a bit of context :
I have been looking for a way to share on a regular basis some of my learnings. I tend to go through a pretty large amount of content as part of my work and in general. So here I will try to provide it as a structured digest.
The goal is to provide an interesting selection of content both practical and conceptual to help advancing how to use tech to solve real problems.</description><author>Yasha Solutions</author><pubDate>Sun, 10 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/a-web-of-solutions-october-2021/</guid></item><item><title>Comparing the Fujifilm XF 16‑80 with the XF 18‑135 Travel Zoom Lenses</title><link>https://bastibe.de/2021-10-09-fuji-travel-zooms.html</link><description>&lt;p&gt;When I bought into the Fuji system, &lt;a href="https://bastibe.de/2019-05-11-fuji-zoom-lenses.html"&gt;I selected the XF 18‑135 f/3.5‑5.6 R LM OIS WR&lt;/a&gt; as my main zoom lens. This is a lens with a very wide focal range, that is commonly called a “travel zoom” because you could travel the world with just this one lens. And indeed &lt;a href="https://bastibe.de/2019-10-27-travel-cameras.html"&gt;I happily did&lt;/a&gt;. In 2019, Fujifilm released a &lt;em&gt;second&lt;/em&gt; travel zoom lens, the XF 16‑80 f/4 R OIS WR. Ever since, I have wondered how this new 16‑80 compares to my 18‑135. But given that these lenses are somewhat similar, few people on the internet were ever able to compare them side by side. This blog post will change that.&lt;/p&gt;
&lt;p&gt;The 18‑135 has served me very well indeed. Of course it is not the world's brightest lens, nor sharpest, nor smallest. But so long as I can get the shot, these limitations don't bother me. Except for two things: The transition between in-focus and out-of-focus can be a bit rough, and my lens extends on its own when carried on a sling. My hope is that the 16‑80 has a nicer rendering, especially for people photos with out-of-focus backgrounds, and a stiffer zoom ring that doesn't creep.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Picture of Squirrel, with hazy bokeh" src="/static/2021-10/DSCF6485_4.thumb.jpg" /&gt;
&lt;figcaption&gt;Instead of smoothly blurring, the near-focus areas of the 18‑135 have this strange, hazy look&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Physical&lt;/h2&gt;
&lt;p&gt;Physically, the 16‑80 is a slight bit smaller (89 mm vs 98 mm) and ligher (440 g vs 490 g) than the 18‑135, but the difference is negligible on camera. If anything, the 16‑80 actually feels a bit &lt;em&gt;bigger&lt;/em&gt; to me due to its larger front element. Some people claim that the 16‑80 feels better built than the 18‑135. To that end, the rings on my 16‑80 turn more smoothly than my 18‑135's, but then that is comparing a new-ish lens to a well-used one. Autofocus speeds are also reputably different, but they feel similar to me.&lt;/p&gt;
&lt;p&gt;The 16‑80 has a numbered aperture ring that allows for adjustments while the camera is turned off. On the other hand, the 18‑135 can switch to and from auto-aperture without losing its preset aperture, which is useful as well. The ideal lens for me would have both the auto-aperture switch &lt;em&gt;and&lt;/em&gt; the numbered aperture ring. Oh well.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A picture of the two lenses from the side, both collapsed and extended" src="/static/2021-10/allthelenses.thumb.jpg" /&gt;
&lt;img alt="A picture of the two lenses from the front" src="/static/2021-10/lensfront.thumb.jpg" /&gt;
&lt;figcaption&gt;Size comparison of the two lenses. The 18‑135 is a longer, and the 16‑80 wider. Note the marked aperture ring on the 16‑80, and the Auto-Aperture/OIS switches on the 18‑135.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The 16‑80 has a 72 mm filter thread, while the 18‑135 uses 67 mm filters. This is somewhat annoying for me, as my filters are all 67 mm, which also fit on my 70‑300. I'd imagine users of the 72 mm 10‑24 see this differently. Anyway, using a thin polarizer with a 72‑to‑67 step-down ring works without issue on the 16‑80. My inch-thick macro filter however does vignette heavily until 23 mm.&lt;/p&gt;
&lt;h2&gt;Rendering&lt;/h2&gt;
&lt;p&gt;In terms of rendering, I find the 16‑80 to have a gentler transition from in-focus to out-of-focus, and indeed render out-of-focus backgrounds more smoothly than the 18‑135. At the long end, the 16-80 is actually a useful portrait lens, which is an unexpected but welcome feature for my photography.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A picture taken with each lens, with a blurry background" src="/static/2021-10/bokeh.thumb.jpg" /&gt;
&lt;figcaption&gt;Rendering of out-of-focus elements. Especially the points of light in the top right have an asymmetric crescent shape for the 18‑135, but blur smoothly for the 16‑80.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;At the wide end, the 16‑80 exhibits some significant distortion. While the camera or post processing programs can easily correct this, it leaves the image corners stretched, and renders the 16‑80's nice round bokeh balls as ugly ovals. Thus large-aperture shots at the wide end can be somewhat problematic.&lt;/p&gt;
&lt;h2&gt;Resolution&lt;/h2&gt;
&lt;p&gt;Next, let's compare the resolution of the two lenses. To do that, I printed a &lt;a href="https://www.graphics.cornell.edu/~westin/misc/res-chart.html"&gt;test chart&lt;/a&gt;, and took some test images. All images were taken in identical illumination, on a tripod, with the chart covering half the sensor height/width. The following graphic shows a resolution scale near the center of the frame and near the left edge of the frame. And just for fun, I've thrown in a similar analysis from two prime lenses as well. The resolution scales are in 200x line widths per picture height. On my 6000×4000-pixel sensor, the theoretical maximum resolution would therefore be a resolution number of ⁴⁰⁰⁰∕₂₀₀ = 20. All test images are straight crops from original images, reproduced at their original resolution.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A grid of crops of a test chart, with various lenses at various focal lengths and f‑numbers" src="/static/2021-10/comparison.svg" width="4724px" /&gt;
&lt;figcaption&gt;Comparison of lens resolution with a test chart. Each image is a direct crop at the X‑T2's native resolution. Resolution numbers in 200x line widths per image height.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Center images were focused in the center, and side images were focused on the side. A two-second timer was used to eliminate camera shake. Due to the geometry of my room and the size of my printer, all pictures were taken near the close-focusing distance of the lenses. A red line indicates the limit of resolution of these lines, as judged by my eyes. The corresponding resolution numbers are generally consistent with the ones published by &lt;a href="https://opticallimits.com/fuji_x/1103_fuji1680f4ois"&gt;opticallimits&lt;/a&gt; and &lt;a href="https://www.lenstip.com/571.4-Lens_review-Fujifilm_Fujinon_XF_16-80_mm_f_4_R_OIS_WR_Image_resolution.html"&gt;lenstip&lt;/a&gt;&lt;sup class="footnote-ref" id="fnref-1"&gt;&lt;a href="#fn-1"&gt;1&lt;/a&gt;&lt;/sup&gt;, albeit I judged them a bit more conservatively.&lt;/p&gt;
&lt;p&gt;The results of this test are surprising to me: the 16‑80 is actually my sharpest lens. It not only bests the 18‑135 at all settings, but also the 35 f/1.4. Only the 60 f/2.4 can reach similar resolution at the same f‑numbers. Generally, the 16‑80 is perfectly sharp right from f/4, while the 18‑135 has to be stopped down to f/8 to reach a similar resolution, especially on the image edges. Only at the long end does the 16‑80 benefit from stopping down.&lt;/p&gt;
&lt;p&gt;That said, take these resolution measurements with a grain of salt. For instance, a “great” result of 15 (3000 LW/PH) translates to a blur radius of 1.3 pixels, while a “mediocre” result of 10 (2000 LW/PH) is instead 2 pixels. This is scientifically significant, but not at all relevant to (my) photography.&lt;/p&gt;
&lt;p&gt;Additionally, the fact that the 60 f/2.4 macro lens scores so highly but the 35 f/1.4 does not is an indication that these measurements might be biased by being taken near the close focusing distance of the lenses. Thus the next set of images compares these lenses at more natural distances.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A grid of crops from a landscape shot, with the two lenses at various focal lenths and f‑numbers" src="/static/2021-10/resolution.thumb.jpg" /&gt;
&lt;figcaption&gt;Comparison of lens resolution with natural images. Each image is a 500x500 pixel crop from original photographs. The subject is ca. 100 m from the camera.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;At this farther distance, and with a more natural subject, the differences are no longer as easily visible. What differences there are this time favor the 18‑135 instead of the 16‑80. Interestingly, I didn't see any significant differences between these pictures when looking at them “merely” side-by-side in Capture One. Only when I actually assembled these here graphics and looked at them at 200% did the differences become apparant.&lt;/p&gt;
&lt;p&gt;Nevertheless, it remains curious that there would be such a difference between the two lenses. Then, someone mentioned that the 16‑80 might suffer from &lt;em&gt;shutter shock&lt;/em&gt;, where the camera's mechanical shutter jolts the camera enough to upset the image stabilization system and induce a slight bit of motion blur. An issue such as this might explain the 16‑80's slightly reduced resolution in my test shots. So I created another series of images, but this time both, with the mechanical shutter, and with electronic shutter. In electronic shutter mode, nothing moves in the camera and there can be no shutter shock.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A grid of crops from a landscape shot, with the two lenses at various focal lenths and f‑numbers and with electronic and mechanical shutter" src="/static/2021-10/shutter.thumb.jpg" /&gt;
&lt;figcaption&gt;Comparison of lens resolution and shutter shock. Each image is a 500x500 pixel crop from original images. The subject is ca. 100 m from the camera.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;From this comparison, I can see no evidence of shutter shock. It might have been an issue on earlier firmware versions of the 16‑80, but my camera (an X-T2) and lens (at firmware 1.05) does not does not exhibit shutter shock. Furthermore, this series of pictures shows the 16‑80 and 18‑135 essentially matched in image resolution.&lt;/p&gt;
&lt;p&gt;All of that said, I must add that all of these comparisons used extremely tight crops of high-contrast geometrical features. Most of the differences here are all but invisible in actual photographs. From these resolution experiments, I see no reason to prefer one lens over the other. Both of them are perfectly sharp in everyday use.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So, how to choose between the Fujifilm XF 16‑80 f/4 R OIS WR and the XF 18‑135 f/3.5‑5.6 R LM OIS WR? My 16‑80 has tighter aperture and zoom rings, does not creep, and has a smoother rendering of out-of-focus background. On the other hand, I do find the increased telephoto of the 18‑135 very useful, and it doesn't suffer from wide-angle distortion as much as the 16‑80.&lt;/p&gt;
&lt;p&gt;In terms of resolution, I did not find fault with either lens. Both are very sharp across their entire focal range and the entire frame. That said, the 18‑135 does benefit from stopping down for optimum resolution, while the 16‑80 is sharp right from f/4, and the 16‑80 might be sharper for closer subjects.&lt;/p&gt;
&lt;p&gt;My tentative conclusion from these experiments is therefore that I would slightly prefer the 16‑80 for people pictures, where the close-focus sharpness and nicer background rendering are advantageous, and the larger aperture at 80 mm might make a difference. And I would prefer the 18-135 for landscapes, where stopping down is usually easy and the longer focal length comes in handy.&lt;/p&gt;
&lt;p&gt;That said, the differences in rendering and resolution are really very minor, and the choice most importantly comes down to the focal range. Which is as it should be with modern lenses. And both lenses are of course very well-built, weather sealed, and have fantastic image stabilization. But you probably knew that already.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-1"&gt;&lt;p&gt;multiply lenstip numbers by 2×16.7 mm to convert from lpmm (lines per millimeter) to LW/PH (line width per picture height)&lt;a class="footnote" href="#fnref-1"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>bastibe.de</author><pubDate>Sat, 09 Oct 2021 18:16:00 GMT</pubDate><guid isPermaLink="true">https://bastibe.de/2021-10-09-fuji-travel-zooms.html</guid></item><item><title>Creating a custom Bulma build</title><link>https://stribny.name/posts/bulma-build/</link><author>Posts by Petr Stribny</author><pubDate>Sat, 09 Oct 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://stribny.name/posts/bulma-build/</guid></item><item><title>WebAssembly-ing the Pieces: Vectorized’s Data Policy Engine</title><link>https://whoisnnamdi.com/data-policy-engine/</link><description>Rather than ship data to code, which is expensive and latency-prone, why not ship code to the data?</description><author>Who is Nnamdi?</author><pubDate>Sat, 09 Oct 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://whoisnnamdi.com/data-policy-engine/</guid></item><item><title>Web Browsing</title><link>https://www.marginalia.nu/log/28-web-browsing/</link><description>&lt;p&gt;An idea I&amp;rsquo;ve had for a long time with regards to navigating the web is to find a way to browse it.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Browse&amp;rdquo; a difficult word to use, because it has a newer connotation of just using a web browser, I mean it in the old pre-Internet sense, browse like when you flip through a magazine, or peruse an antiques shop, not really looking for anything in particular just sort of seeing if anything catches your eye.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sat, 09 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/28-web-browsing/</guid></item><item><title>Chicago, Fall 2021</title><link>https://faingezicht.com/photos/2021/10/09/chicago-9-21/</link><description>I went to Chicago for work, and took a lot of abstract building shots. You can definitely see the &lt;a href="https://angiemcmonigal.com/fine-art/"&gt;Angie McMonigal&lt;/a&gt; inspiration in some of these.

There's also a couple of people in there.</description><author>Avy Faingezicht</author><pubDate>Sat, 09 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/10/09/chicago-9-21/</guid></item><item><title>The importance of docs - 123dev #34</title><link>https://justingarrison.com/123dev/2021-10-09-123dev-34/</link><description>&lt;img alt="A uniquely shaped puzzle piece slides into place in a finished puzzle" src="https://justingarrison.com/img/123dev/34-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="puzzling"&gt;Puzzling&lt;/h2&gt;
&lt;p&gt;Growing up my dad loved puzzles. He had the weirdest rule that I never understood.&lt;/p&gt;
&lt;p&gt;No matter how many pieces the puzzle had, he would look at the picture once when he opened it and never looked at it again. Thankfully, he never enforced that rule on us as kids, but I still don’t understand why he did it.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sat, 09 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-09-123dev-34/</guid></item><item><title>Automating My Air Conditioner</title><link>https://www.dannyguo.com/blog/automating-my-air-conditioner</link><author>Danny Guo</author><pubDate>Sat, 09 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.dannyguo.com/blog/automating-my-air-conditioner</guid></item><item><title>Facebook down gave people a glimp of a better world</title><link>https://stop.zona-m.net/2021/10/facebook-down-gave-people-a-glimp-of-a-better-world/</link><description>&lt;p&gt;&amp;ldquo;The news&amp;rdquo;. Think about that!&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 09 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/facebook-down-gave-people-a-glimp-of-a-better-world/</guid></item><item><title>Cross entropy, joint entropy, conditional entropy and relative entropy</title><link>https://bytepawn.com/cross-entropy-joint-entropy-conditional-entropy-and-relative-entropy.html</link><description>&lt;p&gt;What's the difference between cross entropy, joint entropy, conditional entropy and relative entropy? &lt;br /&gt;&lt;br /&gt;&lt;img alt="Information gain" src="/images/inf_gain.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 09 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/cross-entropy-joint-entropy-conditional-entropy-and-relative-entropy.html</guid></item><item><title>October</title><link>https://gyani.net/blog/october/</link><description>.. another month - 
		  Ok, September is here. 4 more months left. I know I am writing this on the 5th of the month. The last few days have been busy! Retrospective Running: I wanted to be at 1496 at the end of the month. I am at 1500. ✅ Reading: I wanted to...</description><author>Gyanendra Mishra</author><pubDate>Fri, 08 Oct 2021 21:30:00 GMT</pubDate><guid isPermaLink="true">https://gyani.net/blog/october/</guid></item><item><title>Little Intuition</title><link>https://ztoz.blog/posts/little-intuition/</link><description>&lt;p&gt;This is the 60th anniversary of the proof of Little&amp;rsquo;s Law, a widely useful rule for queuing systems that links throughput, response (cycle) time, and number of requests (work items). Unfortunately, although software engineers often operate queues or pipelines (modern computer systems are rife with queues, implicit and explicit), they aren&amp;rsquo;t exposed to the mathematical foundations and often fail to build intuition around how queues act operationally. In this post, I aim to describe the Law, applications within software systems, and build an intuition in its use.&lt;/p&gt;
&lt;h1 id="the-equation"&gt;The equation&lt;/h1&gt;
&lt;p&gt;Little&amp;rsquo;s Law has two equivalent formulations: \(L = \lambda W\) and \(WIP = TH \times CT\). The first formulation is that the average number of items within the system (\(L\)) is equal to  the average arrival rate of items (\(\lambda\)) times the average waiting time (\(W\)) of an item. The second formulation is the average number of work in progress (WIP) items is equal to the average throughput (TH) times the average cycle time (CT). The two formulations are equivalent, with the first focusing on &amp;ldquo;inputs&amp;rdquo; to the system with the second focusing on &amp;ldquo;outputs&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Importantly, the waiting time or cycle time includes the time spent processing in addition to  the time in queue. Similarly, \(L\) and WIP include items sitting in queues as well as items being actively worked. All three values are expressed as averages; the law by itself won&amp;rsquo;t provide insight into ranges of those values.&lt;/p&gt;
&lt;h1 id="some-intuition"&gt;Some intuition&lt;/h1&gt;
&lt;p&gt;First, logically, none of the three variables can be negative.&lt;/p&gt;
&lt;p&gt;Secondly, cycle time will have some minimal value, \(CT_0\), that represents the time to move an item through the pipeline with no schedule delay. However, cycle time can grow almost arbitrarily large if the amount of work exceeds the system&amp;rsquo;s capacity.&lt;/p&gt;
&lt;p&gt;Thirdly, throughput will have some maximal value, \(TH_\Omega\), that represents the maximum throughput manageable by the pipeline. (If you are modeling the system via the arrival rate, there might not be a maximum value although systems will need to reject requests at some point.)&lt;/p&gt;
&lt;p&gt;For the system to perform some work (for throughput to be positive), some work needs to be released into the system. If too much work is released, cycle time will grow and the system will bounce around its maximum throughput. However, it is not necessary (nor likely) for throughput to climb linearly and for cycle time to only increase once throughput is saturated.&lt;/p&gt;
&lt;p&gt;For instance, if a system has a minimal cycle time of 2 seconds and a maximum throughput of 4 units/second, then an ideal WIP, TH, and CT may act as:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;WIP&lt;/th&gt;
&lt;th&gt;TH&lt;/th&gt;
&lt;th&gt;CT&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;\(CT_0\)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1/2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2/3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;\(TH_\Omega\) reached; end of flat \(CT_0\)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;CT grows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;However, a system could also behave like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;WIP&lt;/th&gt;
&lt;th&gt;TH&lt;/th&gt;
&lt;th&gt;CT&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1/2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2/3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;CT grows, \(TH_\Omega\) not yet reached&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6/5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;4/5&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;4/3&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A divergence from an ideal progression can indicate losses due to queuing overhead or failures but can also be attributed to natural inefficiencies and cost of variability.&lt;/p&gt;
&lt;p&gt;Because these numbers are &lt;em&gt;averages&lt;/em&gt;, outliers can greatly influence the values. The law of small numbers and large numbers apply to observations. We plan to write more about this later, but the variability in a system (e.g. cycle times) greatly impacts the operator&amp;rsquo;s ability to control the system within some service level objective.&lt;/p&gt;
&lt;h1 id="applications"&gt;Applications&lt;/h1&gt;
&lt;p&gt;Some applications that requiring solving for or controlling each of the three variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L: If you need to estimate sizing requirements, like the size of the queue or how much disk will be required, you will be computing L via arrival rate and wait time.&lt;/li&gt;
&lt;li&gt;CT: If you want to control response time/cycle time, you will estimate this value based on a count of accepted work and system throughput.&lt;/li&gt;
&lt;li&gt;TH: If you want to control throughput, you will be using work release strategies to improve batching and trying to improve cycle time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="further-reading"&gt;Further Reading&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://people.cs.umass.edu/~emery/classes/cmpsci691st/readings/OS/Littles-Law-50-Years-Later.pdf"&gt;PDF Link&lt;/a&gt; John D. C. Little. 2011. OR FORUM&amp;mdash;Little&amp;rsquo;s Law as Viewed on Its 50th Anniversary. Oper. Res. 59, 3 (May 2011), 536–549. DOI:https://doi.org/10.1287/opre.1110.0940&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ndl.ethernet.edu.et/bitstream/123456789/25163/1/31.pdf"&gt;PDF Link&lt;/a&gt; Chhajed, Dilip &amp;amp; Lowe, Timothy. (2008). Building Intuition: Insights From Basic Operations Management Models and Principles. Chapter &amp;ldquo;Little&amp;rsquo;s Law&amp;rdquo; by Little, John &amp;amp; Graves, Stephen. DOI:https://doi.org/10.1007/978-0-387-73699-0_5&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Fri, 08 Oct 2021 21:23:01 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/little-intuition/</guid></item><item><title>Machine Learning with Wikidata</title><link>https://blog.vfiles.no/posts/machine-learning-with-wikidata/</link><description>&lt;p&gt;This tutorial will be dedicated to understanding how to use the linear
regression algorithm with Wikidata to make predictions. For a detailed
explanation of how this algorithm works please read the
&lt;a href="https://en.wikipedia.org/wiki/Wikipedia"&gt;Wikipedia&lt;/a&gt; article:
&lt;a href="https://en.wikipedia.org/wiki/Linear_regression"&gt;linear regression&lt;/a&gt;. In this
walkthrough
&lt;a href="https://en.wikipedia.org/wiki/Python_%28programming_language%29"&gt;Python&lt;/a&gt; is used.&lt;/p&gt;
&lt;h3 id="importing-modulespackages"&gt;Importing Modules/Packages&lt;/h3&gt;
&lt;p&gt;Before we start coding, import/install all of the following packages:
&lt;a href="https://pypi.org/project/numpy/"&gt;NumPy&lt;/a&gt;,
&lt;a href="https://pypi.org/project/pandas/"&gt;Pandas&lt;/a&gt;, and
&lt;a href="https://pypi.org/project/scikit-learn/"&gt;Sklearn&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-python"&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #75715e;"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;import&lt;/span&gt; numpy &lt;span style="color: #66d9ef;"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;import&lt;/span&gt; pandas &lt;span style="color: #66d9ef;"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;import&lt;/span&gt; sklearn
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;from&lt;/span&gt; collections &lt;span style="color: #f92672;"&gt;import&lt;/span&gt; defaultdict
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;from&lt;/span&gt; sklearn &lt;span style="color: #f92672;"&gt;import&lt;/span&gt; linear_model
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="loading-in-our-data"&gt;Loading in Our Data&lt;/h3&gt;
&lt;p&gt;Now it&amp;rsquo;s time for some data collection from Wikidata. For this example we are
using the yearly (average) population stacked by country in a query (linked
further down). This gives us a lot of interesting values and some with faults,
unfortunately. I have chosen to filter this query to only include values from
2005 and newer. How you choose to import the query into the script is your
decision. A passibility is to
&lt;a href="https://www.wikidata.org/wiki/Wikidata:Pywikibot_-_Python_3_Tutorial/Iterate_over_a_SPARQL_query"&gt;iterate over a SPARQL query&lt;/a&gt;
by downloading the &lt;code&gt;.rq&lt;/code&gt; file or just download a
&lt;a href="https://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt; file of the result from the
&lt;a href="https://query.wikidata.org"&gt;query.wikidata.org&lt;/a&gt; site. Once you&amp;rsquo;ve downloaded
the data set and placed it into your main directory (of the Python code) you
will first need to clean the data, and later load it in using the pandas module.&lt;/p&gt;</description><author>Posts on A blog</author><pubDate>Fri, 08 Oct 2021 17:48:41 GMT</pubDate><guid isPermaLink="true">https://blog.vfiles.no/posts/machine-learning-with-wikidata/</guid></item><item><title>Testing external API calls in Go</title><link>https://liza.io/testing-external-api-calls-in-go/</link><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="#intro"&gt;Intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#method-1-mockable-interface-around-the-external-call"&gt;Method 1 (Mocks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#method-2-httptest"&gt;Method 2 (httptest)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="intro"&gt;Intro&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Both testing methods described here can be found in &lt;a href="https://github.com/lazeratops/go-bucket"&gt;this repository&lt;/a&gt;. Be sure to run &lt;code&gt;go generate&lt;/code&gt; before &lt;code&gt;go test&lt;/code&gt; for the mocks to be generated. Otherwise, the mocking approach test described below will not work.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Fri, 08 Oct 2021 09:20:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/testing-external-api-calls-in-go/</guid></item><item><title>Node.js Package Management at UtahJS</title><link>https://thomashunter.name/posts/2021-10-08-nodejs-package-management-utahjs</link><author>Thomas Hunter II</author><pubDate>Fri, 08 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://thomashunter.name/posts/2021-10-08-nodejs-package-management-utahjs</guid></item><item><title>Climbing the corporate ladder - 123dev #33</title><link>https://justingarrison.com/123dev/2021-10-08-123dev-33/</link><description>&lt;img alt="A person standing on top of a ladder cuts a branch off a tree that falls and hits the ladder and the person falls out of sight" src="https://justingarrison.com/img/123dev/33-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="stealing-the-corner-office"&gt;Stealing The Corner Office&lt;/h2&gt;
&lt;p&gt;I listened to this book and here are the action items to help you climb the career ladder into senior leadership if that’s what you want. It’s not the best strategies for a dev/IC, but some of the tips are still helpful. If you can follow some of these tips your career climb hopefully won’t look like the gif.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 08 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-08-123dev-33/</guid></item><item><title>Human Denial of Service Attacks</title><link>https://www.danstroot.com/posts/2021-10-08-human-denial-of-service-attack</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/ddos-attack.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Can companies create human "denial of service" attacks against their own employees? They can and they do - all in the name of collaboration and 'making the business better'. If you work in a large organization you will recognize the pattern. Here's how it works...&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-10-08-human-denial-of-service-attack"&gt;Human Denial of Service Attacks&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Fri, 08 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-10-08-human-denial-of-service-attack</guid></item><item><title>Your "21st century banking" is sixty years old</title><link>https://stop.zona-m.net/2021/10/your-21st-century-banking-is-sixty-years-old/</link><description>&lt;p&gt;And not just banking. But not for software.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 08 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/your-21st-century-banking-is-sixty-years-old/</guid></item><item><title>My First Post</title><link>https://blog.vfiles.no/posts/my-first-post/</link><description>&lt;p&gt;Hello! Bonjour! Hola! Hei!&lt;/p&gt;
&lt;p&gt;This is my first time using this platform to write a blog post. I have not
decided on what I&amp;rsquo;m supposed to write about. The platform is still a work in
progress and was created since I&amp;rsquo;ve always wanted to start a technological blog.
The topics will most likely be relevant to what I&amp;rsquo;m interested in or what I&amp;rsquo;m
working on.&lt;/p&gt;
&lt;p&gt;So, thank you for taking the time to read this first blog post; hopefully, there
will be more to come in the future. Good-bye!&lt;/p&gt;</description><author>Posts on A blog</author><pubDate>Thu, 07 Oct 2021 23:22:22 GMT</pubDate><guid isPermaLink="true">https://blog.vfiles.no/posts/my-first-post/</guid></item><item><title>Our Goals and Our Questions</title><link>https://www.unsungnovelty.org/posts/10/2021/our-goals-and-our-questions/</link><description>&lt;blockquote&gt;
&lt;p&gt;License: &lt;a href="https://creativecommons.org/licenses/by-nc-nd/4.0/"&gt;Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
_International Public License (CC BY-NC-ND
_4.0)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We all have goals and we all ask questions. Like everybody, I have a ton of goals that needs to be finished. Things I want to achieve or finish. I use todo list items to describes a goal. But when you start working on a todo list item, the thing you will have in your head won&amp;rsquo;t be goal itself, but a bunch of &lt;em&gt;questions&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Goals are important though. But we often need a plan when we start working on things. Goals are essentially those plans. Goals makes our existence as humans justifiable. Because we need things. And in order for us to get things we need, we try to finish a set of tasks which will help us reach our goals. Having a plan instead of winging it all the time will help things easier for us. Todo lists are just one such &amp;ldquo;plan&amp;rdquo; or format which hosts most of our goals these days. Doing a bunch of task items from the todo lists helps us reach our goals faster. Or that is the general gist. We use a notebook or an application on our computer/smartphones to create these tasks. And when we start doing the tasks though, we end up asking &lt;em&gt;questions&lt;/em&gt;, to ourselves or others.&lt;/p&gt;
&lt;p&gt;Questions are in our head, at least initially. Questions most of the time are also not documented/written down anywhere like our todo list items (goals!). This means you know what you want to do but you don&amp;rsquo;t know how to do it. You still ask questions though. You often search online for answers to your questions. How do I make a website? How can I do this? How can I do that? Or you ask a friend or someone who knows better about the subject which you are meddling with. This is a problem if you think about it. Because you&amp;rsquo;d rarely document the question and the answer. Then when you ask the same question, you repeat the procedure of finding the same answer all over again. Questions are our notes, our journey. Snippets of information which constitutes a goal. We could document the answers for our questions we have in our head. This can lead to not having to ask the same question again. DRY principle anyone?&lt;/p&gt;
&lt;p&gt;I have recently started using questions for notes instead of a heading and the answer. I try to write the question I have in my head or use the question I asked the search engine instead of using a title/heading followed by the asnwer. This has been really helpful for me cos it is much more clear. Makes the note a lot better in my opinion. When you are using a software, product, project and you are going through the documentation, where do you go first? Mostly &lt;em&gt;Frequently Asked Questions (FAQs)&lt;/em&gt;. Have you noticed that FAQs are far more easier and much more accessible to you than the documentation itself? That is because you already have a question in your head and you are trying to find that question in the entire documentation in the hope that you can also find the answer to them. For example, my heading for notes on &lt;em&gt;git tags&lt;/em&gt; changed from &lt;em&gt;&lt;strong&gt;git tags&lt;/strong&gt;&lt;/em&gt; to &lt;strong&gt;&lt;em&gt;How do I create an annotated git tag?&lt;/em&gt;&lt;/strong&gt;. Because information about annonated tag is what I want. Even when I need to find something else specific to git tags, I will be searching online anyway. So why bother copy pasting the entire documentation as your note? By being specific and splitting a topic to multiple questions, I know exactly what to look for next time I encounter a question. Which is mostly a lot of times. You could even classify your questions under a specific title or heading to make it more organised. For example, all the questions about git tags can be asked under git tags heading. And this is a much better way of using headings or a title.&lt;/p&gt;
&lt;h2 id="how-do-i-ask-better-questions"&gt;How do I ask better questions?&lt;/h2&gt;
&lt;p&gt;Obviously the next big question is, how and when should I ask a question? Hear are some pointers for you…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask the question to yourself first. Sometimes all you want is a rubber duck. Someone to listen the words inside your head to get the answer. That is why sometimes when you ask a question/doubt to someone, midway through the question you answer it yourself.&lt;/li&gt;
&lt;li&gt;Ask the question to a search engine. You could ask the question to a search engine before asking a human. There is a high chance that what you are trying to find is already tried by someone else in the world. You might even get better ideas for the information you are trying to get.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DON&amp;rsquo;T BE A HELP VAMPIRE!&lt;/strong&gt; A help vampire is someone who just ask for help without putting any effort on their own to find solution for themselves. Before you ask a question, do your due diligence so that others are not wasting their time. It will also be faster most of the times to just do the research yourself instead of waiting for somebody else&amp;rsquo;s reply.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="references-links"&gt;References / Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@gordon_zhu/how-to-be-great-at-asking-questions-e37be04d0603"&gt;https://medium.com/@gordon_zhu/how-to-be-great-at-asking-questions-e37be04d0603&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://xyproblem.info/"&gt;https://xyproblem.info/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/"&gt;https://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/help/how-to-ask"&gt;https://stackoverflow.com/help/how-to-ask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://slash7.com/2006/12/22/vampires/"&gt;https://slash7.com/2006/12/22/vampires/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have thoughts or feedback regarding my post/writing? DM me on Twitter &lt;a href="https://twitter.com/unsungNovelty"&gt;@unsungNovelty&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description><author>unsungNovelty</author><pubDate>Thu, 07 Oct 2021 11:19:45 GMT</pubDate><guid isPermaLink="true">https://www.unsungnovelty.org/posts/10/2021/our-goals-and-our-questions/</guid></item><item><title>Drama going on at the .NET Foundation</title><link>https://nicolaiarocci.com/drama-going-on-at-the-.net-foundation/</link><description>&lt;p&gt;A few months after I released my first &lt;a href="https://fatturaelettronicaopensource.org/"&gt;.NET open source project&lt;/a&gt; (a niche
one targeting the Italian fintech world), I was contacted by a representative
of Team Digitale, the digital innovation branch of the Italian Public
Administration. He suggested joining the Developers Italia initiative and
moving my project to the their organization on GitHub &amp;ldquo;to enjoy
enhanced visibility and broaden the audience&amp;rdquo;. I politely refused. I did not
doubt my counterpart&amp;rsquo;s good faith. At the same time, I was concerned about the
possible long-term consequences of a seemingly easy move. Moving a GitHub
project away from your profile or an organization you control means ceding
control over it. I was assured I would keep control of the project. But what
happens if sometime in the future, when people in charge might even have
changed, they revoke my access rights? As long as I am involved with my
project, I should be in control. Also, I was not convinced that the move would
help promote the project. We live in the search-engine age; people search for
solutions to their problems. I was, and still am, confident that if I did my
due diligence and my project is any good, people will find it&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 07 Oct 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/drama-going-on-at-the-.net-foundation/</guid></item><item><title>Trouble Comes To Small Town</title><link>https://tiltingatwindmills.dev/trouble-comes-to-small-town/</link><description>There's a certain type of movie I find that I really like. It's not exactly a
genre. In fact it can exist in just about any genre. For lack…</description><author>Tilting at Windmills</author><pubDate>Thu, 07 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://tiltingatwindmills.dev/trouble-comes-to-small-town/</guid></item><item><title>Seed Stage Operating</title><link>/2021/10/07/Seed-Stage-Operating.html</link><description>The trap The differences between a first-time founder and a repeat founder are most noticeable during the seed stage. First-time founders want to take their newly acquired funding and put it to use right away. Hiring a team, buying new services, buying more ads, etc. Repeat-founders understand that cash is king, and preserving that funding to extend runway is how you avoid getting rinsed during your next round. Repeat-founders are also acutely aware of what product market fit looks like. This means they understand that dumping gas on a fire that isn’t yet ready for it is a great way to burn through several million dollars in a very short amount of time.</description><author>rrich.io</author><pubDate>Thu, 07 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/2021/10/07/Seed-Stage-Operating.html</guid></item><item><title>Please stop building custom CD tools - 123dev #32</title><link>https://justingarrison.com/123dev/2021-10-07-123dev-32/</link><description>&lt;img alt="A black and white video of the first olympics from 1908. Including various sports which by today's standards seems not impressive" src="https://justingarrison.com/img/123dev/32-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="your-build-tools-are-great-but-no-one-wants-to-learn-them"&gt;Your build tools are great, but no one wants to learn them&lt;/h2&gt;
&lt;p&gt;I’ve never worked somewhere that had a completely off-the-shelf CI/CD system. In some cases the systems are just minor tweaks or wrapper scripts over common tools (usually Jenkins). In extreme cases it’s completely from scratch tooling that has syntax and behaviors I’ve never encountered before.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 07 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-07-123dev-32/</guid></item><item><title>Clojure</title><link>https://mfashby.net/posts/2021-10-06-clojure/</link><description/><author>Home on mfashby.net</author><pubDate>Wed, 06 Oct 2021 23:35:37 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-10-06-clojure/</guid></item><item><title>The difference between Instagram and TikTok, and why it matters</title><link>https://stop.zona-m.net/2021/10/the-difference-between-instagram-and-tiktok-and-why-it-matters/</link><description>&lt;p&gt;It&amp;rsquo;s about equal opportunities. At least in India.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 06 Oct 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/the-difference-between-instagram-and-tiktok-and-why-it-matters/</guid></item><item><title>firefox: disable download dialog delay</title><link>https://evilcookie.de/firefox-disable-download-dialog-delay.html</link><description/><author>blog</author><pubDate>Wed, 06 Oct 2021 14:41:53 GMT</pubDate><guid isPermaLink="true">https://evilcookie.de/firefox-disable-download-dialog-delay.html</guid></item><item><title>blog update: rss feed</title><link>https://evilcookie.de/blog-update-rss-feed.html</link><description/><author>blog</author><pubDate>Wed, 06 Oct 2021 11:52:03 GMT</pubDate><guid isPermaLink="true">https://evilcookie.de/blog-update-rss-feed.html</guid></item><item><title>Getting with the times</title><link>https://www.marginalia.nu/log/27-getting-with-the-times/</link><description>&lt;p&gt;Since my search engine has expanded its scope to include blogs as well as primordial text documents, I&amp;rsquo;ve done some thinking about how to keep up with newer websites that actually grow and see updates.&lt;/p&gt;
&lt;p&gt;Otherwise, as the crawl goes on, it tends to find fewer and fewer interesting web pages, and as the interesting pages are inevitably crawled to exhaustion, accumulate an ever growing amount of junk.&lt;/p&gt;
&lt;p&gt;Re-visiting each page and looking for new links in previously visited pages is probably off the table, that&amp;rsquo;s something I can maybe do once a month.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 06 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/27-getting-with-the-times/</guid></item><item><title>Union vs sum types</title><link>https://viralinstruction.com/posts/uniontypes/index.html</link><description>A brief explanation of union types, sum types, and how they relate and compare.</description><author>viralinstruction</author><pubDate>Wed, 06 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://viralinstruction.com/posts/uniontypes/index.html</guid></item><item><title>No code mo problems - 123dev #31</title><link>https://justingarrison.com/123dev/2021-10-06-123dev-31/</link><description>&lt;img alt="A pigeon walks across wet cement leaving foot prints" src="https://justingarrison.com/img/123dev/31-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="cher-ami"&gt;Cher Ami&lt;/h2&gt;
&lt;p&gt;Cher Ami was a messenger pigeon used by the American army in France during WWI. A battalion of 550 troops was surrounded by the German army and the allies thought they were in a different location. The Major was trying to send out messages for help, but the messengers he sent (humans and pigeons) kept getting killed.&lt;/p&gt;
&lt;p&gt;The allies began to drop artillery fire onto their location not knowing it was them. The Major sent out another pigeon, Cher Ami, with the message&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 06 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-06-123dev-31/</guid></item><item><title>The unsurprising digital challenges of agritourism</title><link>https://stop.zona-m.net/2021/10/the-unsurprising-digital-challenges-of-agritourism/</link><description>&lt;p&gt;Nothing new in the [digitized] country.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 06 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/the-unsurprising-digital-challenges-of-agritourism/</guid></item><item><title>Using Apache Airflow ETL to fetch and analyze BTC data</title><link>https://blog.adnansiddiqi.me/using-apache-airflow-etl-to-fetch-and-analyze-btc-data/</link><description>&lt;p&gt;This post is part of the Data Engineering and ETL Series. I am taking a short break from Blockchain-related posts. In the past, I have covered Apache Airflow posts here. In this post, I am discussing how to use the CCXT library to grab BTC/USD data from exchanges and create an ETL for data analysis and visualization. I am using the dockerized version of Airflow. It is easy to set up and use proper different images to run different components instead of a one-machine setup. The docker-compose.yml file is available here but I have made a few changes to install custom libraries. Therefore, it is advised you use the file I have provided in the Github repo. Before I continue, the demo of the work is given here: The DAG The entire flow looks like the below: I am fetching BTC/USD data from three different exchanges: Binance, ByBit, and FTX. Once the data is available, I am sending all three sources into the next stage where I am only pulling close and timestamp data because these are fields that I need for the next stages. From here, I am branching out two tasks: one to create text files and store data for later use and the other is storing into the DB. The 3 different files are then sent to the server via FTP. The data from load_data section is then used to generate reports and then FTPing the file to the server. Notice that some borders are dark green and some are light green in color. Dark green means these tasks are executed successfully while light green means they are still running. The task, ftp_pdf_file is yet to run. In Airflow terms, the DAG flow has been set like below: In the first stage all 3 tasks running in parallel and output results to transform_data therefore I put all 3 tasks in a Python list.  The &amp;#62;&amp;#62; sign is telling that it is upstream. The transformed data is being to create_text_file and load_data tasks who themselves are upstream for ftp_file and generate_pdf_reports tasks. Notice I am using &amp;#60;&amp;#60; here to define upstream for ftp_pdf_file and generate_pdf_reports tasks. Again these tasks were first added in Python list and then branched out for the next stage. In order to pass data from one task to another task, Airflow provides xcom_pull and xcom_push methods. XCom means cross-communication. When you use xcom_pull, it means you are fetching data from the task whose IDs have been passed. For instance: Here I passed the task_id of three tasks and assigned in three different variables. Wondering where those task_ids are defined? they are defined as PythonOperator. Check the log of the task fetch_binance_ohlcv: I highlighted the returned value of the task. This and the other two tasks values are xcom_pulled in the transform_data task. I am also using two external components here: FTP and Database. You may use the traditional python code by importing the required libraries but Airflow also provides the options of hooks and connections. For instance, pgsql connection looks like the below: Once the connection is defined, you may use PG Hook to perform Postgres-based operations. &amp;#160; Cool, isn&amp;#8217;t it? No need to save credentials in the code. The same thing has been done with FTP. In the end, data is analyzed and generates a two-page PDF for executives. the graph is being generated in matplotlib and saved as an embedded image in the PDF file. Sweet, No? I was supposed to add data and charts of both FTX and ByBit too but I leave it for you ;). Also, this is not a heavy analysis of the data. I have provided you all data need. You may play with ta-lib or other libraries and find insights. All up to you 😉 I am using fpdf python library to generate PDF files. This is how files are being sent via FTP. Conclusion In this post, you learned how you can make complex flows for ETLs and use connections and hooks to connect 3rd party tools like FTP, DB, AWS, etc. Airflow could be a pretty powerful tool if used correctly. Like always, the code is available on GitHub. If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/using-apache-airflow-etl-to-fetch-and-analyze-btc-data/"&gt;Using Apache Airflow ETL to fetch and analyze BTC data&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Tue, 05 Oct 2021 17:51:38 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/using-apache-airflow-etl-to-fetch-and-analyze-btc-data/</guid></item><item><title>Write It Now</title><link>https://josh.works/write-it-now</link><description>&lt;h2 id="the-original-post"&gt;The original post&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;note from October 5, 2021: This was typed up/published in about 20 minutes, took 2x as long as I wish it had. I could make it 10x better with another hour of work, but I only have 20 minutes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m a fan of “conceptual frameworks”&lt;/p&gt;

&lt;p&gt;This concept has been important to me:&lt;/p&gt;

&lt;p&gt;&lt;img alt="write-it-now" src="/images/2020-07-21-write-it-now.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;I’ve referenced it, almost apologetically, in a few other things I’ve written recently.&lt;/p&gt;

&lt;p&gt;Usually I’ve just slapped it into an email or linked from another blog post, as a bit of an “i’m sorry” for how much work I might demand from the reader, or how unpleasant or confusing what I’m writing might be.&lt;/p&gt;

&lt;p&gt;I feel like I often self-censure in an attempt to make something “better” than it is, and in doing so, fall off the back of this curve. (or I let vague concerns about quality/execution/time delay me from writing anything, ever)&lt;/p&gt;

&lt;p&gt;I will reference this page regularly, in some ways to act as a defense against some very obvious criticisms. IMO, I’d rather throw 10 things as the wall quickly in a week, instead of 1 thing every other week.&lt;/p&gt;

&lt;p&gt;If I propose something that sparks “controversy”, however that is defined, I’ll gladly cross that bridge (or burn it, if need be) when I get there.&lt;/p&gt;

&lt;p&gt;If you’re a software developer, and use Github, you are welcome to leave a comment below (the link takes you to a comment on a dedicated Github Issue page: &lt;a href="https://github.com/josh-works/josh-works.github.io/issues/49"&gt;https://github.com/josh-works/josh-works.github.io/issues/49&lt;/a&gt;), or on any page around here with this link. I added this comments thing only recently, and still have not scripted an API call to auto-generate a new github issue when I write a new post. So… I just don’t bother, at least some of the time.&lt;/p&gt;

&lt;p&gt;Here’s other things I’ve written along this vein:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="/limitations-of-my-own-thinking"&gt;My own thinking is severely limited&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="/driven-by-compression-progress-novelty-humor-interestingness-curiosity-creativity"&gt;Driven by Compression Progress: A Simple Principle Explains Essential Aspects of Subjective Beauty, Novelty, Surprise, Interestingness, Attention, Curiosity, Creativity, Art, Science, Music, Jokes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read around the internet, viewed as permission/empathy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://blog.torh.net/2022/06/14/writing-is-hard/"&gt;Writing is Hard&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=31741262"&gt;hacker news thread&lt;/a&gt;&lt;sup id="fnref:noteworthy"&gt;&lt;a class="footnote" href="#fn:noteworthy" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://guzey.com/personal/why-have-a-blog/"&gt;guzey.com/personal/why-have-a-blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="updates-to-this-post"&gt;Updates to this post&lt;/h2&gt;

&lt;h3 id="2022-06-15"&gt;2022-06-15&lt;/h3&gt;

&lt;p&gt;I periodically re-visit and update things I’ve written in the past. This post is one of them. Sometimes I modify my writing substantially - it’s simply text files in a git repository, therefore all of what you see around here could be considered in some various form of draft.&lt;/p&gt;

&lt;p&gt;I’ve considered adding links to every page on my website that say “click here to see prior versions of this file”. I perceive a feeling of some obligation to the reader to note every time there’s been an edit, or at least &lt;em&gt;some&lt;/em&gt; of the time when I might edit things. But obviously fixing typos doesn’t need to be called out in detail.&lt;/p&gt;

&lt;p&gt;Additionally, it’s sometimes fun to bury hot takes in walls of text. For example, does the following text seem sort of dry, and academic to you? It’s intentional, if it does:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In &lt;a href="https://www.goodreads.com/book/show/2023854.The_Slaughter_of_Cities"&gt;The Slaughter of Cities&lt;/a&gt;, the author tells a compelling tale of how one ethnic group sucessfully perpetuated ethnic cleansing upon other ethnic groups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s passive voice, it’s just a link to a goodreads page for a book. It doesn’t sound as provocative as it otherwise could. So if it does/does not get the intended result, one could turn it up or down a bit. For example, how does this phrasing compare?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In &lt;a href="https://www.fidelitypress.org/book-products/the-slaughter-of-cities"&gt;The Slaughter of Cities: Urban Renewal as Ethnic Cleansing&lt;/a&gt; we read the many ways American urban renewal programs in the 1950s and 60’s were run to create ghettos and perpetuate acts of ethnic cleansing. Those people attacked essentially every non-white ethnic group they encountered, and the relevence of all this is very much in the room with us right now, in most ways that matter.’&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I find writing to be trivially easy, I find writing &lt;em&gt;well&lt;/em&gt;, however defined, to be hard. Does it spark joy? Does it record something in a way that allows someone to save time in the future? A friend recently sent me a whatsapp message that they were googling something and the top google result was an article I wrote, which indeed contained something useful to them &lt;a href="https://josh-works.medium.com/testing-rake-tasks-in-rails-6573f7185a0a"&gt;Testing Rake Tasks in Rails&lt;/a&gt;. Lots around here is technical, I lean hard on ‘software norms’ for all that I do.&lt;/p&gt;

&lt;h2 id="jan-2024"&gt;Jan 2024&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sashachapin.substack.com/p/write-faster-130"&gt;Write Faster (sashachapin.substack.com)&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Humans are naturally sense-making, story-telling creatures. We’re wired to explain ourselves and the universe, to ourselves and to each other. We’re naturally ingenious fabulists, shrewd reporters, idiosyncratic word-painters. Basically all of us have this facility.&lt;/p&gt;

  &lt;p&gt;But this ability disappears when it’s subjected to excessive scrutiny. When we start reflecting at length on what kind of story we should tell, or what kind of writer we should be, we become lost in the sea of data our senses provide us, and lose the brio of playfulness. It’s like how when you think about how you’re walking, you start walking stupid. You’ve just got to trust that you know what you’re doing: that, as a sense-making creature, you will largely make sense.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can also be our own ‘excessive scrutinizers’. Something like this undoubtedly influences those who describe themselves as perfectionistic.&lt;/p&gt;

&lt;p&gt;Something I love hanging out with kids is how easily they embody ‘uninhibited self-expression’. I find it healing and enthusiasm-engendering, because you can take delight in someone else taking delight and interest in their environment, experience, activity.&lt;/p&gt;

&lt;p&gt;I’ve noticed, at times, when I simply would imagine an act of spontaneous self-expression, I would pre-emptively shut it down by imagining either vocalizable reactionary criticism or a non-response, a shun, withdrawal. The topics would span from technical (feeling embarrassed about asking something, admitting that I don’t know/couldn’t figure out something) or casual/mundane. (a comment about something in the environment, etc)&lt;/p&gt;

&lt;p&gt;So, some of the ‘write it now’ energy I propose you/me capture is something that moves towards a healthy form of ‘uninhibited self-expression’.&lt;/p&gt;

&lt;p&gt;Ironically, I can ‘hear’ a criticsm in my mind:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;But doesn’t some of the worst parts of the internet seem to typify ‘uninhibited self-expression’?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think of a quote I’ve heard on the expression of anger. It can be paraphrased as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Anger is fully ‘valid’, to feel, full stop. Suppressing it, or expecting others to suppress it, is foolish. It’s as natural and healthy as sadness, or joy, or any of the other many things that we feel as people. ‘anger’ becomes a problem if it’s expressed &lt;em&gt;abusively&lt;/em&gt; or harmfully towards others, and even in this case the issue isn’t the anger, it’s the abusive expression of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Someone who hits when angry doesn’t have an anger problem, they have a hitting/violence problem.&lt;/p&gt;

&lt;p&gt;So, if one person is being cruel to someone else, via ‘uninhibited self-expression’, the problem is not the uninhibited self-expression, it’s the cruelty.&lt;/p&gt;

&lt;p&gt;Here’s the last thing - I think good uninhibited self-expression is best experienced when you know whoever you’re expressing to feels similarly about such things. Someone needs to point towards you something that feels like a steady-enough message of ‘you are fundamentally acceptable to me, as are the ways you move through the world’. This feels like a high bar, but I don’t really think it is that hard to achieve.&lt;/p&gt;

&lt;h2 id="links-and-additional-reading"&gt;Links and additional reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://herman.bearblog.dev/the-two-kinds-of-writing/"&gt;Two kinds of writing (herman.bearblog.dev)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://github.com/jbranchaud/til"&gt;https://github.com/jbranchaud/til&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://sashachapin.substack.com/p/write-faster-130"&gt;Write Faster (sashachapin.substack.com)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:noteworthy"&gt;
      &lt;p&gt;There were some very lovely comments in the hackernews thread. Here’s a portion of &lt;a href="https://news.ycombinator.com/item?id=31760857"&gt;this comment&lt;/a&gt;:&lt;/p&gt;

      &lt;blockquote&gt;
        &lt;p&gt;It’s hard because it probably won’t change the world, but it becomes much harder when shouting.&lt;/p&gt;

        &lt;p&gt;It’s entirely OK to sit and write without fanfare, we’re not wizards or witches any longer, just plain people with the same plain obligation towards the mind as towards the body: tend, feed, and regularly empty out.&lt;/p&gt;

        &lt;p&gt;If today you really gotta go write, go ahead, knock yourself out.&lt;/p&gt;

        &lt;p&gt;If tomorrow you just scribble in the margins of empty pages but nothing comes, have a bath or a nap or both, and sit again the day after.&lt;/p&gt;

        &lt;p&gt;I also find it very helpful to remember that I’m not addressing others when writing, I’m not in a possibly empty auditorium talking into the void, instead I’m reaching across time and continue a discussion I started with myself earlier, and may pick up again later.&lt;/p&gt;
      &lt;/blockquote&gt;
      &lt;p&gt;&lt;a class="reversefootnote" href="#fnref:noteworthy"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Tue, 05 Oct 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/write-it-now</guid></item><item><title>Auto-Updating Docker Containers</title><link>https://blog.randombits.host/auto-updating-docker-containers/</link><description>&lt;p&gt;Docker is the biggest advance to production software engineering in the past decade. If you&amp;apos;re like me though, you slept on it this entire time and are now feeling like you&amp;apos;re too far behind to catch up and finally start using them. I&amp;apos;m going&lt;/p&gt;</description><author>Random Bits</author><pubDate>Tue, 05 Oct 2021 13:07:34 GMT</pubDate><guid isPermaLink="true">https://blog.randombits.host/auto-updating-docker-containers/</guid></item><item><title>Back to the (Phone) Future!  Changing roles!</title><link>https://blog.herlein.com/post/webrtc-phone/</link><description>&lt;p&gt;The only constant is change!  And&amp;hellip; sometimes things come full circle.  I&amp;rsquo;m changing roles at AWS.  Back to the Future!&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Tue, 05 Oct 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/webrtc-phone/</guid></item><item><title>I stored a boolean instead of a timestamp</title><link>https://river.me/blog/boolean-instead-of-timestamp/</link><description>A Cargo field I was going to store as a timestamp but decided to store as a boolean instead</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 05 Oct 2021 04:16:32 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/boolean-instead-of-timestamp/</guid></item><item><title>Book review - Celebration, U.S.A.</title><link>https://river.me/blog/book-review-celebration-usa/</link><description>A review of &amp;ldquo;Celebration, U.S.A.: Living in Disney&amp;rsquo;s Brave New Town&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 05 Oct 2021 03:49:11 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-celebration-usa/</guid></item><item><title>What I've Been Up To Lately</title><link>https://ben.bolte.cc/posts/2021-10-05-october-update</link><description>Short post about what I've been up to lately.</description><author>Ben Bolte</author><pubDate>Tue, 05 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ben.bolte.cc/posts/2021-10-05-october-update</guid></item><item><title>Resiliency and new domains - 123dev #30</title><link>https://justingarrison.com/123dev/2021-10-05-123dev-30/</link><description>&lt;img alt="A paper clip and spring bend back into shape automatically when exposed to heat" src="https://justingarrison.com/img/123dev/30-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="resiliency"&gt;Resiliency&lt;/h2&gt;
&lt;p&gt;Code and systems break all the time. At a certain scale it’s important that your start building software that can heal itself. It’s sometimes confusing what that means because what state should the software return to?&lt;/p&gt;
&lt;p&gt;While it was bent out of shape the paper clip wasn’t broken, but it wasn’t useful as it was intended to be. Software is similar in that it may still be functioning but not usable for its intended purpose.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 05 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-05-123dev-30/</guid></item><item><title>Pandemics, kicking cars out of the stone age</title><link>https://stop.zona-m.net/2021/10/pandemics-kicking-cars-out-of-the-stone-age/</link><description>&lt;p&gt;The &lt;strong&gt;semiconductor&lt;/strong&gt; stone age, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 05 Oct 2021 02:40:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/pandemics-kicking-cars-out-of-the-stone-age/</guid></item><item><title>Eating the Cloud from Outside In</title><link>https://www.swyx.io/cloudflare-go</link><description>AWS is playing Chess. Cloudflare is playing Go.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 04 Oct 2021 05:23:15 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/cloudflare-go</guid></item><item><title>What it’s like to ship products - 123dev #29</title><link>https://justingarrison.com/123dev/2021-10-04-123dev-29/</link><description>&lt;img alt="A surfer gets tossed like a rag doll from a large wave" src="https://justingarrison.com/img/123dev/29-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="an-industry-secret"&gt;An industry secret&lt;/h2&gt;
&lt;p&gt;For all the automation the technology industry prides itself in there’s still a lot of it that’s manual. Some companies get up on stage and talk about how great their automatic canary deployments are, how they use feature flags, or a variety of different methods to get their products to market.&lt;/p&gt;
&lt;p&gt;I’ve been a part of some large product launches and plenty of incremental changes in various environments. As soon as the product launch requires coordinating with a non-development team (e.g. blog post or press release) they all had some form of a slack channel, email list, or phone bridge where someone was in charge of manually going down a spreadsheet and clicking buttons.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 04 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-04-123dev-29/</guid></item><item><title>Simple Lists: a tiny to-do list app written the old-school way (server-side Go, no JS)</title><link>https://benhoyt.com/writings/simple-lists/</link><description>Simple Lists is a tiny to-do list web application written in Go, with old school server-side rendering and no JavaScript.</description><author>Ben Hoyt's technical writing</author><pubDate>Sun, 03 Oct 2021 21:30:00 GMT</pubDate><guid isPermaLink="true">https://benhoyt.com/writings/simple-lists/</guid></item><item><title>CTF Writeup - TSG CTF 2021 - 2 Reversing Tasks</title><link>https://nevesnunes.github.io/blog/2021/10/03/CTF-Writeup-TSG-CTF-2021-2-Reversing-Tasks.html</link><description/><author>onKeyPress</author><pubDate>Sun, 03 Oct 2021 13:26:35 GMT</pubDate><guid isPermaLink="true">https://nevesnunes.github.io/blog/2021/10/03/CTF-Writeup-TSG-CTF-2021-2-Reversing-Tasks.html</guid></item><item><title>ThinkPad P14s AMD First Impressions</title><link>https://liza.io/thinkpad-p14s-amd-first-impressions/</link><description>&lt;p&gt;I was fully set on getting a ThinkPad T14 AMD, but seeing no hint of the Gen 2 AMD version in sight and learning that the P14s is basically &lt;em&gt;the same thing&lt;/em&gt;, I decided to go for that instead. This is a quick ramble with some first impressions of the ThinkPad P14s AMD Gen 2 (Ryzen 5)&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Sun, 03 Oct 2021 12:20:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/thinkpad-p14s-amd-first-impressions/</guid></item><item><title>VLC Error</title><link>https://blog.herlein.com/post/vlc/</link><description>&lt;p&gt;A bug in VLC?  Grrrr.  Reminds me of the old days.&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Sun, 03 Oct 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/vlc/</guid></item><item><title>Working faster not harder - 123dev #28</title><link>https://justingarrison.com/123dev/2021-10-03-123dev-28/</link><description>&lt;img alt="Two women put on table cloths and place plates very fast using large turn tables" src="https://justingarrison.com/img/123dev/28-0.gif" width="100%" /&gt;
&lt;hr /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="code-generators"&gt;Code generators&lt;/h2&gt;
&lt;p&gt;Lots of people recommend learning to program by starting with fundamentals. Learn the basic language patterns and elements and work your way up to frameworks. You’ll have a stronger understanding of how things work.&lt;/p&gt;
&lt;p&gt;I don’t think this is good advice anymore. By doing this you slow down your speed to market and what I’ve seen is more companies need speed than understanding. If I want to create a website I’m not going to start with HTML and CSS. I’m going to use a generator like React, Vue, or Hugo.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 03 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-03-123dev-28/</guid></item><item><title>An overview of cloud buzz words</title><link>https://jasoneckert.github.io/myblog/cloud-buzz-words/</link><description>&lt;p&gt;&lt;img alt="Dilbert comic" src="dilbert.jpg#center" title="Dilbert comic" /&gt;&lt;/p&gt;
&lt;p&gt;As this Dilbert comic eloquently illustrates, there&amp;rsquo;s no shortage of cloud buzz words thrown around today. But as strange and complex as they may sound (even to seasoned developers and system administrators), they are not difficult to understand. In this blog post, I&amp;rsquo;ll walk through the different components and features of the cloud, explaining the associated buzz words along the way.&lt;/p&gt;
&lt;h1 id="what-is-the-cloud"&gt;What is the cloud?&lt;/h1&gt;
&lt;p&gt;Back in the early 1990s, when the US government sold off parts of their ARPAnet and NSFnet to private companies (later called Internet Service Providers, or ISPs) to create the public Internet, there wasn&amp;rsquo;t anything exciting about the Internet at all. After all, the Internet was just a physical computer network that allowed your computer to talk to other computers, usually through a telephone network via a modem.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Sun, 03 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/cloud-buzz-words/</guid></item><item><title>Do Not Erase</title><link>https://ztoz.blog/posts/do-not-erase/</link><description>&lt;img alt="Cover of the Do Not Erase book" class="fit-image" src="https://ztoz.blog/posts/do-not-erase/do-not-erase-cover.jpg" /&gt;
  

&lt;p&gt;&lt;a href="https://press.princeton.edu/books/hardcover/9780691199221/do-not-erase"&gt;Do Not Erase: Mathematicians and their Chalkboards&lt;/a&gt;
by Jessica Wynne is a beautiful photography book. Each leaf contains a photograph of a mathematician&amp;rsquo;s chalkboard and a short essay by that mathematician talking about their work, what is displayed, or something personal. Wynne&amp;rsquo;s work only looks at blackboards or chalkboards, not whiteboards. The entries vary from the prosaic, such as diagrams left over from an office hours session on elementary calculus, to snapshots of current work, to results that were a life&amp;rsquo;s work.&lt;/p&gt;
&lt;p&gt;One of the recurring themes in the mathematician&amp;rsquo;s essays is the superiority of chalkboard over a digital medium like PowerPoint. Many of the essays leave their author&amp;rsquo;s rationale unexplored, but the reasons articulated include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The act of writing slows down the speaker, providing more time for the audience to understand the content (and, in a class, take notes)&lt;/li&gt;
&lt;li&gt;The speaker is able to use their full body to communicate (versus a voice and a static image)&lt;/li&gt;
&lt;li&gt;High-quality chalk provides better legibility than a marker and skilled users can create subtle artwork, but chalk isn&amp;rsquo;t so versatile as to encourage adding unnecessary and distracting details like digital works&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For why a chalkboard is superior to a whiteboard:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Whiteboard markers smell&lt;/li&gt;
&lt;li&gt;Whiteboard markers are always running out&lt;/li&gt;
&lt;li&gt;Markers aren&amp;rsquo;t as legible and flexible as chalk&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Some of the authors do note that markers have the advantage of leaving less dust about, as in the case where the mathematician moved a chalkboard into their bedroom in case there was a late night burst of insight.)&lt;/p&gt;
&lt;p&gt;As the production of the book extended over the pandemic, many of the mathematicians express sorrow over their inability to use their boards for their work. Although most of the mathematicians in the book are in fields sufficiently esoteric that book readers will not grasp what is being shown on the board, we can appreciate the medium and skill that permits the transmission of deep concepts in such a succinct manner and wish we could hear and see the mathematician explaining their work in concert with the pictures.&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Sun, 03 Oct 2021 01:36:03 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/do-not-erase/</guid></item><item><title>HTTPS and other hosting details</title><link>https://jay.jvf.cc/posts/now-with-ssl/</link><description>&lt;p&gt;Last week, I got my blog up quickly with a simple upload to Google Cloud storage, with public access. However, this has
a huge limitation &amp;ndash; no HTTPS. Google&amp;rsquo;s proscribed message to enable it? Pay them for TLS terminating load balancers.
I have no desire to setup a money-sucking machine in case one of these blogposts go viral one day (unlikely), so that was
out.&lt;/p&gt;
&lt;p&gt;Then I read a recent post about a new product released by &lt;a href="https://cloudflare.com"&gt;Cloudflare&lt;/a&gt;. I looked on their site and
discovered they have a free plan. It&amp;rsquo;s possible they&amp;rsquo;ve always had a free plan, and I just wasn&amp;rsquo;t aware of it, but it
looks like I get free DNS hosting and CDN proxying for up to 3 domains, including SSL termination and certificate
management. This seemed pretty great, so I went ahead and signed up and migrated over. I&amp;rsquo;m still using Google Cloud storage
as the place where the site is actually hosted, but it&amp;rsquo;s all Cloudflare fronted.&lt;/p&gt;</description><author/><pubDate>Sat, 02 Oct 2021 21:16:52 GMT</pubDate><guid isPermaLink="true">https://jay.jvf.cc/posts/now-with-ssl/</guid></item><item><title>Sublime Text as EDITOR</title><link>https://voussoir.net/writing/sublime_as_editor</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;Sublime Text as EDITOR&lt;/h1&gt;
&lt;p&gt;I use Sublime Text as my &lt;code&gt;EDITOR&lt;/code&gt; environment variable and as my git editor. I used ST2 for many years and recently jumped to ST4, skipping 3. But something has changed along the way, or maybe it was always a little imperfect.&lt;/p&gt;
&lt;p&gt;Between Sublime's workspaces and project sessions and hot-exit files that allow you to close with unsaved changes, there is quite a lot of state saved to disk. I found myself getting frustrated at the various incantations of &lt;code&gt;sublime_text.exe -n&lt;/code&gt;, &lt;code&gt;sublime_text.exe -n -w&lt;/code&gt;, &lt;code&gt;subl.exe -n -w&lt;/code&gt; because they recalled the previous state and opened a bunch of tabs or windows when I just wanted to do a single git rebase.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/sublime_as_editor/bad1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/sublime_as_editor/bad2.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://forum.sublimetext.com/t/disable-automatic-loading-of-last-session/4132/10" title="Disable automatic loading of last session"&gt;Here is a forum thread&lt;/a&gt; where other people have the same problem, and they mostly discuss workarounds through config files. I didn't want to make any of those changes because I like having all my tabs restored in 99% of cases, just not for temporary &lt;code&gt;EDITOR&lt;/code&gt; purposes.&lt;/p&gt;
&lt;p&gt;Then I realized something that I should have realized a long time ago because I'm the one who's always going on about it: Sublime is &lt;a href="/writing/master_of_my_domain"&gt;portable software&lt;/a&gt;. I can just make a copy of the folder. And I did. And it's great.&lt;/p&gt;
&lt;p&gt;Now, I have &lt;code&gt;C:\Software\Sublime Text\4133&lt;/code&gt; and &lt;code&gt;C:\Software\Sublime Text\4133_EDITOR&lt;/code&gt;, and I can configure them independently. For the EDITOR copy, I can remove all project folders, disable a lot of UI elements, turn off state-saving, and uninstall unnecessary packages.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/sublime_as_editor/good1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/sublime_as_editor/good2.png" /&gt;&lt;/p&gt;
&lt;p&gt;My &lt;code&gt;EDITOR&lt;/code&gt; environment variable is &lt;code&gt;C:/software/sublime text/4113_EDITOR/subl.exe -n -w&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My .gitconfig contains:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[core]
    editor = 'C:/software/sublime text/4113_EDITOR/subl.exe' -n -w&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My WinSCP editor is &lt;code&gt;C:/software/sublime text/4113_EDITOR/sublime_text.exe -n !.!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Use portable software, folks.&lt;/p&gt;
&lt;p&gt;P.S. I found that the &lt;code&gt;-w&lt;/code&gt; flag only waits if you also provide a filename to edit on the command line. &lt;code&gt;subl -w&lt;/code&gt; does not block, but &lt;code&gt;subl -w myfile.txt&lt;/code&gt; does block. This made me confused while I was testing everything.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/sublime_as_editor/sublime_as_editor.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/c9a3432f8674c1b4f85d3d7e6af1cbfc31235bf8"&gt;&lt;time datetime="2021-10-02T11:09:51-07:00"&gt;2021-10-02&lt;/time&gt; Add sublime_as_editor.md.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Sat, 02 Oct 2021 21:09:51 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/sublime_as_editor</guid></item><item><title>Collateralizing Mortgages and Loans With the Present Value of Rent Flow</title><link>https://josh.works/collateralizing-financial-products-with-present-value-of-rent-flow</link><description>&lt;p&gt;&lt;em&gt;this is a draft document, it pairs with &lt;a href="/pud"&gt;this Planned Unit Development application draft document&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Inspiration comes from many places, but most strongly it draws heavily from &lt;em&gt;Order Without Design&lt;/em&gt;. I’ve quoted in depth two pages below, but there is many other sections of the book germane to the topic of this page.&lt;/p&gt;

&lt;p&gt;Other things to read:&lt;/p&gt;

&lt;h2 id="summary-of-how-a-financial-institution-could-use-rent-generating-property-to-collateralize-a-loan"&gt;Summary of how a financial institution could use rent-generating property to collateralize a loan&lt;/h2&gt;

&lt;p&gt;Assess the value of a land parcel by evaluating the &lt;em&gt;present value of the flow of rents generated by what ever structure they can build on this parcel&lt;/em&gt;.&lt;sup id="fnref:collateralizable"&gt;&lt;a class="footnote" href="#fn:collateralizable" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;A property could plausibly generate $6,000/mo in rent? (say, with six small units at $1000/mo in rent?)&lt;/p&gt;

&lt;p&gt;That $6,000/mo or $72,000/yr could easily collateralize a $600,000 loan/mortgage/financial product &lt;em&gt;especially if there is reason to think even more rent could be generated from the structures.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this point, collateralization discussions would require a plot-by-plot evaluation of the likelihood a given structure on a given plot could generate such income streams.&lt;/p&gt;

&lt;h2 id="a-financial-product-crossing-the-distance-between-the-net-present-value-of-cash-and-guaranteed-future-income-flows"&gt;A financial product “crossing the distance” between the net present value of cash, and guaranteed future income flows&lt;/h2&gt;

&lt;p&gt;The FIRE community (&lt;a href="https://www.reddit.com/r/Fire/"&gt;r/fire&lt;/a&gt;) has the 4% rule, which can be expressed a few different ways, but broadly it’s the expression of a relationship between the value of a given lump of cash on hand, and future risk-adjusted income flows that said lump of cash can generate.&lt;/p&gt;

&lt;p&gt;Because of math, a guaranteed $1000/mo income is “as valuable as” a $12k/yr income, which is 4% of $300,000.&lt;/p&gt;

&lt;p&gt;So, if I wanted to borrow $100k to do something that would have a strong probability of being capable of generating $2000/mo in rent, that is a “safe” mortgage, because even if I’m way off on the amount of rent I generate, I still will have something that is worth far more than was spent creating it. It’s an &lt;em&gt;asset&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This notion all comes from the text expressed below, and other text in the same book, and other books. More on it soon.&lt;/p&gt;

&lt;h2 id="example-4-chinas-urban-villages-p-293"&gt;Example 4: China’s Urban Villages (p. 293)&lt;/h2&gt;

&lt;p&gt;The following comes from page 293 of Alain Bertraud’s &lt;a href="https://www.goodreads.com/book/show/39644188-order-without-design"&gt;Order Without Design: How Markets Shape Cities&lt;/a&gt; Lightly formatted for readability.&lt;sup id="fnref:internet-age"&gt;&lt;a class="footnote" href="#fn:internet-age" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id="legal-status-of-land-in-chinese-villages"&gt;Legal Status of Land in Chinese Villages&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;As in Indonesia, many cities in China are absorbing large numbers of villages as they expand. I will use the term “urban villages” to indicate the villages surrounded by an urban area administered by a municipality.&lt;/p&gt;

  &lt;p&gt;The land occupied by these villages has a special status by law. The use of village land is controlled by a village collective, not by the municipality, although according to the constitution of China, all land belongs “to the people” (i.e., the central government).&lt;/p&gt;

  &lt;p&gt;But there is a difference between full ownership, which involves the ability to sell property, and ownership of land use rights only, which is limited to the right to develop land and to rent floor space or land to a third party.&lt;/p&gt;

  &lt;p&gt;Villagers in China are free to set their own building standards and land use; they can rent land and whatever structure they build to a third party, but they cannot sell either the structure or the land.&lt;/p&gt;

  &lt;p&gt;Only the government can acquire land from farmers by expropriation with compensation.&lt;/p&gt;

  &lt;p&gt;For Chinese farmers living in urban villages, therefore, the value of their land parcel is represented by the &lt;em&gt;present value of the flow of rents generated by what ever structure they can build on this parcel&lt;/em&gt;.&lt;sup id="fnref:collateralizable:1"&gt;&lt;a class="footnote" href="#fn:collateralizable" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;

  &lt;p&gt;Contrary to farmers living in traditional market economies, they do not have any incentive to sell their land to a developer, as the compensation price they would receive from the government is likely to be less than the capitalized value of the flow of rents. This explains why villagers in China resist selling land to the government whenever they can, and many protest forcible acquisition.&lt;sup id="fnref:historic-preservation"&gt;&lt;a class="footnote" href="#fn:historic-preservation" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;

  &lt;p&gt;When the built-up area of an expanding city reaches a village, the municipal government expropriates the fields around it but usually abstains from expropriating the village itself, as the compensation paid to villagers is based on a “replacement value” of the floor space demolished, while the fields are compensated based on the value of crops.&lt;/p&gt;

  &lt;p&gt;Expropriating the village and providing alternative housing units is therefore much more expensive for government than expropriating open fields.&lt;/p&gt;

  &lt;p&gt;As a result, villages are often initially spared demolition and become an enclaved urban village with a special status over the control of land use.&lt;/p&gt;

  &lt;p&gt;As a city expands, the land value increases in the villages that were initially at the fringe of urbanization, to the point of becoming higher than the compensation to be paid to farmers for whatever structures they have built.&lt;/p&gt;

  &lt;p&gt;The municipal government then tends to evict farmers after compensation or relocation and sell the land to developers. However, the process is long and cumbersome, and many urban villages survive a long time before redevelopment occurs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="frequently-asked-questions"&gt;Frequently Asked Questions&lt;/h2&gt;

&lt;h3 id="are-you-proposing-a-new-financial-product"&gt;Are you proposing a new “financial product”?&lt;/h3&gt;

&lt;p&gt;More or less, yes. Eventually something like this could be construed as an alternative to a 30-year fixed-rate mortgage.&lt;/p&gt;

&lt;p&gt;The reasons could be stated by meandering through the current state of affairs, the historical contexts and structures that made the current state of affairs most likely, and a scintillating discussion of who’s visions for the future show the most merit.&lt;/p&gt;

&lt;p&gt;For example, I just read &lt;a href="https://www.netinterest.co/p/financing-the-american-home"&gt;https://www.netinterest.co/p/financing-the-american-home&lt;/a&gt;. The way things are right now is &lt;em&gt;strange&lt;/em&gt;, to say the least, and pressure is building for a good alternative that isn’t “just” a 1:1 replacement.&lt;sup id="fnref:private-capital"&gt;&lt;a class="footnote" href="#fn:private-capital" rel="footnote"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Something much bigger needs to give, but this is a big piece of it.&lt;/p&gt;

&lt;p&gt;I could speak at length of the intersection of &lt;a href="https://www.goodreads.com/book/show/54388.Money_Bank_Credit_and_Economic_Cycles"&gt;Money, Bank Credit, and Economic Cycles&lt;/a&gt; and lending institutions.&lt;/p&gt;

&lt;h3 id="how-would-you-propose-getting-the-most-out-of-the-land"&gt;How would you propose ‘getting the most out of the land’?&lt;/h3&gt;

&lt;p&gt;Great question! By applying established processes to your specific bit of land.&lt;/p&gt;

&lt;p&gt;I propose:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bertraud Model&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Notes for myself, that I bet others will find interesting.&lt;br /&gt;&lt;br /&gt;I have a paper, crafted a few years ago, by Alain and Marie-Agnes Bertaud.&lt;br /&gt;&lt;br /&gt;I'll be adding many more notes in this thread, but for starters, here's the title and abstract. &lt;a href="https://t.co/FKnTvYiHtW"&gt;pic.twitter.com/FKnTvYiHtW&lt;/a&gt;&lt;/p&gt;&amp;mdash; Josh Thompson (@josh_works) &lt;a href="https://twitter.com/josh_works/status/1426386086125875200?ref_src=twsrc%5Etfw"&gt;August 14, 2021&lt;/a&gt;&lt;/blockquote&gt;


&lt;p&gt;I need to write up an article about that, but until then, the twitter thread will have to do.&lt;/p&gt;

&lt;p&gt;Skip straight to this page:&lt;/p&gt;

&lt;p&gt;https://twitter.com/josh_works/status/1426952509088845830/photo/1&lt;/p&gt;

&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:collateralizable"&gt;
      &lt;p&gt;And this flow of rents represents an equivalent present cash value. At least one interpretation is the 4% rule in the FIRE community - “you can take, annually, 4% of your current nest egg for the rest of your life. If you’re able to live on that 4% for a year, you can quit your job tomorrow.” $2.6 million “generates” $100k/yr. A stable $1000/mo is “as valuable as” a $312,000 cash account. &lt;a class="reversefootnote" href="#fnref:collateralizable"&gt;&amp;#8617;&lt;/a&gt; &lt;a class="reversefootnote" href="#fnref:collateralizable:1"&gt;&amp;#8617;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:internet-age"&gt;
      &lt;p&gt;AKA I shotgunned line-breaks through the document because half of us are going to read this on our phones at least twice. &lt;a class="reversefootnote" href="#fnref:internet-age"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:historic-preservation"&gt;
      &lt;p&gt;‘having no incentive to sell to a developer’ is why everyone interested in historic preservation will be a &lt;em&gt;massive&lt;/em&gt; supporter of this model. This model &lt;em&gt;preserves&lt;/em&gt; the city. &lt;a class="reversefootnote" href="#fnref:historic-preservation"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:private-capital"&gt;
      &lt;p&gt;To quote from that article:&lt;/p&gt;

      &lt;p&gt;This reluctance of private capital to backstop the mortgage market would recur again. It stems partly from the risk involved (30 years is a long time) and partly because &lt;em&gt;having established a foothold in the market, the government’s presence makes it difficult for private capital to compete&lt;/em&gt;. The same issues give the the government such a dominant share of the market today. (emphasis mine) &lt;a class="reversefootnote" href="#fnref:private-capital"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Sat, 02 Oct 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/collateralizing-financial-products-with-present-value-of-rent-flow</guid></item><item><title>COVID-19, a lost opportunity for italian administrations</title><link>https://stop.zona-m.net/2021/10/covid-19-a-lost-opportunity-for-italian-administrations/</link><description>&lt;p&gt;Like tears in the rain, really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 02 Oct 2021 13:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/covid-19-a-lost-opportunity-for-italian-administrations/</guid></item><item><title>Documentation for Ada on the Raspberry Pi Pico</title><link>https://synack.me/blog/documentation-for-ada-pico.html</link><description>I’ve written some documentation for Ada on the Raspberry Pi Pico. This is still a work in progress, but provides quite a lot of context around the usage of the libraries and how to build a project on this platform.</description><author>Jeremy Grosser</author><pubDate>Sat, 02 Oct 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://synack.me/blog/documentation-for-ada-pico.html</guid></item><item><title>The Husband Stitch</title><link>https://arunmani.in/library/the-husband-stitch/</link><description>A scary, interesting short story.</description><author>Arun Mani</author><pubDate>Sat, 02 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/the-husband-stitch/</guid></item><item><title>The Little Prince</title><link>https://arunmani.in/library/the-little-prince/</link><description>A refreshing story that was fun to read.</description><author>Arun Mani</author><pubDate>Sat, 02 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/the-little-prince/</guid></item><item><title>Experimenting with Personalized PageRank</title><link>https://www.marginalia.nu/log/26-personalized-pagerank/</link><description>&lt;p&gt;The last few days I&amp;rsquo;ve felt like my first attempt at a ranking algorithm for the search engine was pretty good, like it was producing some pretty interesting results. It felt close to what I wanted to accomplish.&lt;/p&gt;
&lt;p&gt;The first ranking algorithm was a simple link-counting algorithm that did some weighting to promote pages that look in a certain fashion. It did seem to keep the page quality up, but also seemed to as a strange side-effect promote very &amp;ldquo;1996&amp;rdquo;-looking websites. This isn&amp;rsquo;t quite what I wanted to accomplish, I wanted to promote new sites as well as long as they were rich in content.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sat, 02 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/26-personalized-pagerank/</guid></item><item><title>Be kind and AWS Infinidash 🌩️ - 123dev #27</title><link>https://justingarrison.com/123dev/2021-10-02-123dev-27/</link><description>&lt;img alt="An animation showing how a rocket launches from earth and lands on the moon" src="https://justingarrison.com/img/123dev/27-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="silent-stories"&gt;Silent stories&lt;/h2&gt;
&lt;p&gt;The ability to educate with stories is something humans are very good at. Videos are just a new(ish) medium for telling stories. One of the reasons I love gifs is they’re videos with restrictions. The restrictions limit the types of stories we can tell but not the impact those stories can have to educate.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sat, 02 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-02-123dev-27/</guid></item><item><title>Why is mobile game development big in Turkey?</title><link>https://stop.zona-m.net/2021/10/why-is-mobile-game-development-big-in-turkey/</link><description>&lt;p&gt;All jobs are glamourous, until they are not.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 02 Oct 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/10/why-is-mobile-game-development-big-in-turkey/</guid></item><item><title>Blog</title><link>https://mfashby.net/posts/2021-10-01-blog/</link><description/><author>Home on mfashby.net</author><pubDate>Fri, 01 Oct 2021 21:34:15 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-10-01-blog/</guid></item><item><title>Planned Unit Design Document (work-in-progress)</title><link>https://josh.works/pud</link><description>&lt;p&gt;&lt;em&gt;This is a draft document, meant for circulation, will evolve with time and eventually be something we bring to the City of Golden for ratification, or whatever needs to happen to get this done in this zone. This document relates to &lt;a href="/collateralizing-financial-products-with-present-value-of-rent-flow"&gt;Collateralizing Mortgages and Loans With the Present Value of Rent Flow&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I, Josh Thompson, owner of 621 Iowa St, with my wife and the owners and rentors of the below properties, hereby request a Planned Unit Designation from the City of Golden, in accordence to the following terms.&lt;/p&gt;

&lt;h1 id="dimensionsboundaries-of-the-pud"&gt;Dimensions/Boundaries of the PUD&lt;/h1&gt;

&lt;p&gt;Addresses included in the PUD, representing a contiguous territory and boundary.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;648 Iowa St.&lt;/li&gt;
  &lt;li&gt;621 Iowa St.&lt;/li&gt;
  &lt;li&gt;623 Iowa St.&lt;/li&gt;
  &lt;li&gt;619 Iowa St.&lt;/li&gt;
  &lt;li&gt;617 Iowa St.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the specific properties, all owners and tenants of which desire to be included in the PUD:&lt;/p&gt;

&lt;p&gt;&lt;img alt="properties-in-pud" src="pud-addresses.jpg" /&gt;&lt;/p&gt;

&lt;h1 id="property-owners-of-the-lots-inside-of-and-around-the-pud"&gt;Property Owners of the lots inside of and around the PUD&lt;/h1&gt;

&lt;p&gt;For the rest of the document, the collection of properties will be “The PUD boundary zone” or some otherwise collected group.&lt;/p&gt;

&lt;p&gt;&lt;img alt="pud" src="pud-outline.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;The owners of these included lots desire for their land to be included in this PUD, and will sign contracts or testaments to this fact if it would be helpful for obtaining this PUD.&lt;/p&gt;

&lt;p&gt;All property owners and tenants are strongly invested in seeing this general plan be accomplished.&lt;/p&gt;

&lt;h2 id="zone-1-immediately-impacted-property-owners-adjacent"&gt;Zone 1: Immediately-Impacted Property Owners (Adjacent)&lt;/h2&gt;

&lt;p&gt;Nearly everyone adjacent to the designated area, and 100% of the people I’ve spoken to about this, approve of this PUD and would not take actions to prevent it from being designated as such.&lt;/p&gt;

&lt;p&gt;&lt;img alt="impacted-neighbors" src="immediately-impacted-property-owners.jpg" /&gt;&lt;/p&gt;

&lt;h2 id="zone-2-impacted-property-ownersresidents-nearby"&gt;Zone 2: Impacted Property Owners/Residents (nearby)&lt;/h2&gt;

&lt;p&gt;Obviously, PUDs affect far more than just the immediately-adjacent properties. If we were to take an expansionist view of affected parties, we’d need to fill in the corners of the “space”, so it would be inclusive of everyone who lived within a certain distance of &lt;em&gt;a&lt;/em&gt; boundary of the PUD. Here is that expansionist view:&lt;/p&gt;

&lt;p&gt;&lt;img alt="expansionist" src="large-area.jpg" /&gt;&lt;/p&gt;

&lt;h2 id="zone-3-affected-by-usage-patterns"&gt;Zone 3: Affected by usage patterns&lt;/h2&gt;

&lt;p&gt;Finally, since roads are mobility networks, and &lt;em&gt;neighborhoods&lt;/em&gt; are a common unit of analysis, not just individual lots, here’s the &lt;em&gt;neighborhood&lt;/em&gt; inclusivity of persons who would have some vested interest in this project:&lt;/p&gt;

&lt;p&gt;&lt;img alt="expansionist" src="neighborhood.jpg" /&gt;&lt;/p&gt;

&lt;h1 id="pud-code-provisions"&gt;PUD Code Provisions&lt;/h1&gt;

&lt;p&gt;This PUD “application” will contain several sections, modeled roughly after the other PUD applications I could find on the City of Golden’s website:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="GoldenOverlookPUDAmendment.pdf"&gt;Golden Overlook PUD Amendment (PDF)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="LookoutPUDSheet.pdf"&gt;Lookout Mountain PUD Sheet (PDF)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://library.municode.com/co/golden/codes/municipal_code?nodeId=TIT18PLZO_CH18.28USDERE_18.28.300PUPLUNDEDI"&gt;18.28.300 - (PUD) Planned unit development district. (Golden’s Municipal Code)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="the-role-of-the-city"&gt;The role of the city&lt;/h2&gt;

&lt;p&gt;The role of the City and of its Agents is limited to providing connections to the trunk city networks for transport, water, sewer, and storm drainage.&lt;/p&gt;

&lt;h2 id="city-easements"&gt;City easements&lt;/h2&gt;

&lt;p&gt;Existing city infrastructure within the boundaries of the PUD will be maintained, broadly, by a coalition between the PUD and the city. Easements currently owned by the city that extend beyond the physical boundaries of the road, sewer, and water network will be deemed unexercisable, and full possession will be remitted to the property owners.&lt;/p&gt;

&lt;h2 id="rights-of-way"&gt;Rights of way&lt;/h2&gt;

&lt;p&gt;The owners representing the PUD will maintain the existing roads to ensure safe and adequate mobility network through-put.&lt;/p&gt;

&lt;p&gt;Non-vehicular rights-of-way will be decided upon and maintained by the property owners.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id="summarized-language-initial-draft"&gt;Summarized language, initial draft:&lt;/h1&gt;

&lt;p&gt;All language copy-pasted from Alain Bertraud, &lt;a href="https://www.goodreads.com/book/show/39644188-order-without-design"&gt;Order Without Design&lt;/a&gt;, pasted below.&lt;/p&gt;

&lt;p&gt;In specific, I want to model this PUD on the Indonesian Kampung. I’ll copy-paste from the long quote, translating the described phenomina to a set of zoning rules legible to a local municipality.&lt;/p&gt;

&lt;p&gt;As I have discussed this with neighbors (in all zones), and explained the genesis of where this language comes from, they have all eagerly endorsed the plan. To emphasize, this general approach and language has broad community support.&lt;/p&gt;

&lt;p&gt;Below are sentences, as encountered in order, in their original form from the long quote shared below.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In these two countries, the government allowed low-income households to use the standards they could afford and to make their own trade-offs between location, road space, lot space, and floor space. The government role was limited to providing connections from these settlements to the trunk city networks for transport, water, sewer, and storm drainage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Lots were subdivided, but always on the initiative of the original owner&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;adjustments can be made to plot size, width of access to individual lots, and drainage of waste water&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;standards were adjusted to increasing land values&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;A network of small roads, footpaths, and passages was maintained, reflecting the former village structure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;while the government concentrates its efforts not on housing construction but on gradually improving residential infrastructure and services to all residential settlements&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id="long-quote-from-order-without-design-how-markets-shape-cities-p-287"&gt;Long quote from &lt;em&gt;Order Without Design: How Markets Shape Cities&lt;/em&gt;, p. 287&lt;/h1&gt;

&lt;p&gt;[…]&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Finally, housing supply could be greatly increased by improving the urban transport system. Areas with low demand because of poor accessibility to jobs have necessarily a low density. Improving accessibility to jobs by increasing trans port speed or introducing new transport technology should increase the de facto land supply of cities. Unless the regulatory and infrastructure housing supply constraints are removed, adding new subsidies to housing, whether supply side or demand side, would have very little effect on the welfare of the city’s lowest income households.&lt;/p&gt;

  &lt;p&gt;The above example of inclusionary zoning is not just an anecdotal illustration of a poorly designed housing policy. It represents a trend in many of the most economically successful cities in the world. The increasing regulatory frenzy that characterizes some cities like New York imposes large economic costs on the entire country. In a paper published in 2015, the economists Chang-Tai Hsieh and Enrico Moretti found that, between 1964 and 2009, the high cost of housing in some US cities relative to wages had lowered aggregate US GDP by 13.5 percent:&lt;/p&gt;

  &lt;p&gt;Most of the loss was likely caused by increased constraints to housing supply in high productivity cities like New York, San Francisco and San Jose. Lowering regulatory constraints in these cities to the level of the median city would expand their work force and increase U.S. GDP by 9.5%&lt;/p&gt;

  &lt;p&gt;Many of the regulations recently introduced in cities around the world that aim to improve the housing consumption of low-income households, not only do not deliver the number of housing units promised, but also contribute to lowering the economic opportunity of the very people they are supposed to help. It is time to audit these regulations and policies, taking into account the increasingly valuable economic literature on the subject.&lt;/p&gt;

  &lt;h2 id="cities-are-reluctant-to-accept-housing-standards-that-are-affordable-for-the-poor-the-exceptions"&gt;Cities Are Reluctant to Accept Housing Standards That Are Affordable for the Poor: The Exceptions&lt;/h2&gt;

  &lt;p&gt;Cities in low-income countries, where urban migration annually adds a large number of unskilled people, cannot afford to subsidize the housing of the large number of poor. However, cities in Indonesia, using an “integration” approach to low income settlements, have been successful in absorbing new migrants into the urban labor force while maintaining an acceptable level of environmental health in the poorest neighborhoods.&lt;/p&gt;

  &lt;p&gt;In many Asian countries, large numbers of people migrating to cities have been creating dense urban villages in an urban setting but without any urban infrastructure. These small, simple structures provided the shelter these people needed at a price they could afford. It also allowed them to participate in the urban economy. However, the lack of connection to the city infrastructure soon created unacceptable sanitary conditions when these urban villages aggregated in large contiguous neighborhoods of several hundred thousand people. In addition, the lack of schools and health facilities contributed to slowing down or even prevent ing the integration of younger generations into urban society.&lt;/p&gt;

  &lt;p&gt;The predictable first reaction of governments has usually been to set minimum urbanization standards to prevent the legal construction of these unsanitary urban villages. The regulations made the situation worse, as they prevented these informal settlements from obtaining normal urban services from the municipality. They also created a risk of future demolition, which discouraged housing improvement that the households would have naturally done themselves. Eventually, many governments slowly regularized the older informal settlements in a piecemeal fashion, as is the practice in India, for instance. But the regularization of informal settlements usually had been conducted with a provision that after a set date, no more informal settlements would be regularized.&lt;/p&gt;

  &lt;p&gt;The outcome of these successive policies – first ostracism, then benign neglect followed by reluctant integration – has been disastrous. A significant share of the urban labor force, otherwise gainfully employed, live in large “informal” settlements often with unsafe water supplies, deficient sanitation, and sporadic solid waste collection. In Mumbai, one of the economic powerhouses of Asia, 60 percent of the population lives in slums.&lt;/p&gt;

  &lt;p&gt;I discuss below two case studies in Indonesia and China. **These countries, for different reasons and in different ways, took a different approach to integrating affordable low housing standards into the city infrastructure. The outcome for the poor migrant households was significantly better than in the countries where a strict regulatory approach had been followed. In these two countries, the government allowed low-income households to use the standards they could afford and to make their own trade-offs between location, road space, lot space, and floor space. The government role was limited to providing connections from these settlements to the trunk city networks for transport, water, sewer, and storm drainage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="example-3-indonesias-urban-enclaves-with-no-minimum-standards"&gt;Example 3: Indonesia’s Urban Enclaves with No Minimum Standards&lt;/h2&gt;
&lt;blockquote&gt;

  &lt;p&gt;In the densely populated island of Java, expanding cities absorb large existing villages, called “kampungs,” whose population then integrates rapidly into the urban labor force. The former agricultural fields are soon built on by formal developers, while new informal constructions are added in the kampungs that have been absorbed into the city. So far, this is not too different from what happens in the rest of Asia, where countries are urbanizing rapidly. There is one important difference. Indonesian kampungs always had a formal robust administrative structure based on traditional laws, and that administrative structure has been able to survive their absorption into a larger urban municipality.&lt;/p&gt;

  &lt;p&gt;Confronted with urbanization, the kampung traditional leadership organized themselves to absorb newcomers. Lots were subdivided, but always on the initiative of the original owner, and within the kampung traditional regulatory constraints and social norms. To use modern terminology, we could compare kampungs to a form of condominium association: They have their own internal regulations and norms, forming a sort of local authority at the lower level than a municipality or a ward.&lt;/p&gt;

  &lt;p&gt;One difference from a condominium is that the kampung local microgovernment also has jurisdiction over land use. Therefore, adjustments can be made to plot size, width of access to individual lots, and drainage of waste water by using traditional norms developed over centuries of practice of good neighbor rules while overruling the development standards imposed by the municipality that surrounds the kampung. Formal developers building on adjacent greenfield land, however, had to follow the municipal land use standards rules concerning land development and subdivisions.&lt;/p&gt;

  &lt;p&gt;Because the income and preferences of new settlers were well known to the original kampung inhabitants, land use standards evolved to adjust to the new economic reality facing kampungs; standards were adjusted to increasing land values, so that plots would remain affordable to newcomers, who were usually poor migrants. Because the local traditional norms were respected, it did not result in an anarchic aggregation of houses that would be wasteful of land, as often happens in spontaneous squatter settlements where migrants aggregate without forming an organized community. A network of small roads, footpaths, and passages was maintained, reflecting the former village structure.&lt;/p&gt;

  &lt;p&gt;As Indonesian cities developed, the kampungs located closer to city centers densified more rapidly, as expected by their favorable locations closer to employment. The low income of their inhabitants, the lack of access to finance, the frag mentation of properties into small lots, and the lack of access to infrastructure prevented the construction of multistory buildings. However, the subdivision of existing village lots and the narrowness of internal access streets reflected the opportunity cost of land and soon resulted in much higher densities than that of the original villages, often resulting in densities of more than 500 people per hectare.&lt;/p&gt;

  &lt;p&gt;However, this increased density has a downside. The traditional source of water and means of sanitation – consisting of shallow wells and seepage pits – became grossly inadequate to serve the new higher densities. The low absorption capacity of the traditional sanitary system transformed the kampungs into dense unsanitary slums. The traditional storm drainage network, built around former irrigation canals, was insufficient to prevent flooding during the monsoon because of enhanced impermeability of the area caused by urbanization.&lt;/p&gt;

  &lt;h2 id="innovative-housing-policy-concentrating-subsidies-on-infrastructure-not-on-housing-structure"&gt;Innovative Housing Policy Concentrating Subsidies on Infrastructure, Not on Housing Structure&lt;/h2&gt;

  &lt;p&gt;So far the history of the Indonesian kampungs does not appear to be very different from that of informal settlements in many other developing countries. What made a difference was a decision taken in 1969 by the government of Indonesia to concentrate its resources on the improvement of the kampungs’ infrastructure without trying to remove or restructure the existing housing, however small or inadequate it was. The provision of urban infrastructure and services to kampungs was called the Kampung Improvement Program (KIP). Compared to the prevailing housing policies of developing countries in the 1960s, which consisted mostly of bulldozing informal settlements to relocate their inhabitants in public housing flats, this approach was revolutionary.&lt;/p&gt;

  &lt;p&gt;And, even more exceptional, since 1969 to this day, the Indonesian government’s support for KIP has been unwavering, in spite of political upheaval and constitutional changes. The government housing policy objective consists of allowing the poor to settle in and around existing villages at the standards of their choice, while the government concentrates its efforts not on housing construction but on gradually improving residential infrastructure and services to all residential settlements. The policy has proved largely successful. The living and sanitary standards in most kampungs of Indonesia are far above what is found in informal settlements in countries with similar GDPs.&lt;/p&gt;

  &lt;p&gt;No kampung was ever bulldozed. No large groups of households were promised free housing. No large central government institution has tried to replace the many small KIP contracts for civil work on infrastructure in kampungs with larger contracts involving the construction of a massive program of public housing.&lt;/p&gt;

  &lt;p&gt;Operationally, the KIP continues to provide financial and technical assistance to the existing kampungs’ traditional administrative structure to build connections to the municipal water supply network, to pave foot paths, to build drains using existing rights-of-way, to establish a system of solid waste collection that feeds into main collection bins that are part of the municipal solid waste collection system. The maintenance of the internal network and waste collection was and to this day is managed by the kampung community itself, with some financial assistance from the municipal government. The decentralization of decision making and the participation of the communities were embedded in the KIP from the beginning.&lt;/p&gt;

  &lt;p&gt;The KIP has been complemented with citywide investments in transport and most importantly for Southeast Asia, in storm drainage networks to prevent periodic flooding in residential areas&lt;/p&gt;

  &lt;p&gt;Indonesia, with a GDP per capita of US$10,500 in 2015, remains a lower-middle-income country, according to the World Bank. The overall standards of living reflect this income category. However, because the government concentrated its scarce resources on providing urban infrastructure to all urban residents, instead of increasing the housing consumption of a few poor households selected by lottery for public housing, every poor Indonesian received benefits. And these benefits increase over time. The government focus on infrastructure assistance allowed households to use their own resources to invest in their own housing, either as self-occupied dwellings or as investment in rental housing. The housing standards, defined as the size and quality of the structure, may therefore vary from very low to good in the same neighborhood, but access to safe water, sanitation, education, and health is ensured for all.&lt;/p&gt;

  &lt;p&gt;The demand-driven land use standards allowed by regulations in Indonesia are illustrated by the site plans of two neighborhoods in Surabaya, Indonesia, as surveyed in 2010 (figure 6.24). On the left in the figure, a kampung site plan shows the variety of plot and housing sizes that are possible in the same neighborhood. Some structures located in the back of lots facing the main passageways are very small and of poor quality, but they have access to safe water and sanitation. They have also access to the same schools and health facilities as their more affluent neighbors.&lt;/p&gt;

  &lt;p&gt;TODO: ADD PICTURES FROM BOOK&lt;/p&gt;

  &lt;p&gt;The road system allows only emergency and construction vehicles but is adequate for local motorcycle and pedestrian traffic. On the right in the figure, a site plan designed by a formal developer, which shows a different, more standard type of residential layout with much less variety in lot size and more house area, reflecting middle-class standards. homogeneity in house area, reflecting middle-class standards.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="kampung-integration-policy-has-successfully-provided-a-flow-of-low-income-housing"&gt;Kampung Integration Policy Has Successfully Provided a Flow of Low-Income Housing&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Because the kampung boundaries have always been protected since Indonesia started its rapid urbanization, kampungs are found in every neighborhood of Indonesian cities. Consequently, kampungs are always located side by side with commercial areas and higher-income areas, providing a socially desirable income mix at the neighborhood level. Because kampungs and middle-class areas share the same trunk infrastructure for water and sanitation, little discrimination is possible in public investment.&lt;/p&gt;

  &lt;p&gt;I have observed kampungs’ evolution and improvement on a regular basis, since my first work trip to Indonesia in 1977. Many housing units in kampung areas look more middle class than low income. This reflects the increase in house hold incomes in Indonesia over this period. The inability to use a car as a means of transport in kampungs (because of the narrowness of the streets) prevents any large displacement of the poor from the best-located kampungs. The low road standards are the best guarantee against massive gentrification.&lt;/p&gt;

  &lt;p&gt;Some households that have reached sufficiently high incomes are likely to leave the kampung and move into formal development neighborhoods. This movement creates vacancies in the kampungs that are immediately filled with holds who rent or buy a dwelling. Low-income households do not need to be on poorer a waiting list and do not have to submit a proof of their income to buy or rent in house a kampung. Expansion zoning plans are reserving land around existing kampungs located in the periphery to ensure a flow of new low-income housing in the future.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;The Indonesia kampungs and the KIP program demonstrate that it is possible for a government to provide large benefits to its large low-income population without creating long waiting lists. It shows that allowing standards to adjust and, consequently, population densities to float, not only provides affordable housing but also improves the city structure by allowing higher densities in areas accessible to jobs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id="alain-bertrauds-specific-feedback-on-this-document"&gt;Alain Bertraud’s specific feedback on this document&lt;/h1&gt;

&lt;p&gt;I (Josh) am friends with Alain and Marie-Agnes Bertraud, the authors of &lt;em&gt;Order Without Design: How Markets Shape Cities&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When I sent him a draft of this document, Alain said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Your proposed PUD is an imaginative adaptation to a Colorado subdivision of the Kampung and Chinese urban village concept described in my book.&lt;/p&gt;

  &lt;p&gt;I hope that it gets approved under Colorado law!&lt;/p&gt;

  &lt;p&gt;I hope that other people will also imitate the idea with some variations, ensuring constant adaptation to changing socio-economic conditions.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;This new way of changing land use initiated by current occupiers would be a wonderful escape from the arbitrary rigidity of existing zoning laws, which prevent demand-driven land use adaption.&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Your &lt;a href="/collateralizing-financial-products-with-present-value-of-rent-flow"&gt;preliminary financial calculation&lt;/a&gt; also demonstrates that it is possible to have an excellent financial return by unit of land while creating affordable housing units at standards that are acceptable to the end-user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;emphasis mine&lt;/em&gt;&lt;/p&gt;

&lt;h1 id="faq"&gt;FAQ&lt;/h1&gt;

&lt;p&gt;This document prompts many questions. If you have any, consider leaving a comment below, or shoot me an email about it, or text me, or whatever. I’ll update it accordingly.&lt;/p&gt;

&lt;h2 id="what-exactly-is-a-pud-again"&gt;What exactly is a PUD again?&lt;/h2&gt;

&lt;p&gt;TL;DR: A “Planned Unit Development District” is a legal document, enacted via a political process&lt;sup id="fnref:political-process"&gt;&lt;a class="footnote" href="#fn:political-process" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; by which the City approves of modifications to the existing rules and regulations, as applied to a specific piece of land.&lt;/p&gt;

&lt;p&gt;The Planning and Zoning Office of Golden &lt;a href="https://library.municode.com/co/golden/codes/municipal_code?nodeId=TIT18PLZO_CH18.28USDERE_18.28.300PUPLUNDEDI"&gt;defines&lt;/a&gt; a PUD as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(1) The PUD district permits the community and economic development of land which is suitable in location and character for the uses proposed as unified and integrated developments in accordance with detailed development plans. The PUD district also provides for specific land uses not permitted in other zone districts. The PUD district is intended to provide a means of accomplishing the following objectives:&lt;/p&gt;

  &lt;p&gt;(a) To provide for development concepts not otherwise permitted within standard zone districts.&lt;/p&gt;

  &lt;p&gt;(b) To provide flexibility, unity and diversity in land community and economic development, resulting in convenient and harmonious groupings of uses, structures and common facilities; varied type design and layout of housing and other buildings; and appropriate relationships of open spaces to intended uses and structures.&lt;/p&gt;

  &lt;p&gt;(c) To provide for the public health, safety, integrity and general welfare, and otherwise achieve the purposes as provided for within the Planned Unit Development Act of 1972, Title 24, Article 67, Colorado Revised Statutes 1973, as amended.&lt;/p&gt;

  &lt;p&gt;(d) To provide for phased development, the more efficient use of land and the public and private services needed therefor, and reflect changes in the technology of land development.&lt;/p&gt;

  &lt;p&gt;(e) To provide for land development flexibility in dwelling type, bulk, density, intensity and open space, as a policy which Golden wishes to encourage; and, as a corollary, regulate certain proposed land development which would distort the objective of Golden’s zoning.&lt;/p&gt;

  &lt;p&gt;(f) To encourage integrated planning in order to achieve the above purposes.&lt;/p&gt;

  &lt;p&gt;(2) Uses within a PUD development may be multiple in nature and may include uses not otherwise permitted within the same zone district. The location and relationship of these uses shall be as established in and conform to the policies and standards contained within the comprehensive plan and other appropriate adopted and approved plans, including but not limited to locational criteria within that comprehensive plan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="so-why-do-you-want-to-get-this-collection-of-lots-declared-a-pud"&gt;So why do you want to get this collection of lots declared a PUD?&lt;/h2&gt;

&lt;p&gt;Look at reason 18.28.300.1.a:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The PUD district is intended to provide a means of accomplishing the following objectives:&lt;/p&gt;

  &lt;p&gt;To provide for development concepts not otherwise permitted within standard zone districts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve got some development concepts not otherwise permitted within standard zone districts. Full stop. That’s why I want the PUD. I choose to not give up on what I want to accomplish because it’s not &lt;em&gt;currently&lt;/em&gt; permitted on my lot.&lt;/p&gt;

&lt;p&gt;For example - I’d love for there to be a vegetable stand that is sometimes operating on my street. I’d gladly build a space for someone, and let them operate it out of my lot. That structure, and that business, would be so illegal under current rules people laugh at me for even wanting to do this. &lt;a href="https://danluu.com/look-stupid/"&gt;I’m not afraid to look stupid&lt;/a&gt;, so here I am.&lt;/p&gt;

&lt;h2 id="whos-involved-are-you-trying-to-get-other-peoples-property-rezoned-out-from-under-them"&gt;Who’s involved? Are you trying to get other people’s property rezoned out from under them?&lt;/h2&gt;

&lt;p&gt;Nope. Everyone involved is strongly supportive. That means, at minimum:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The owners of all involved lots&lt;/li&gt;
  &lt;li&gt;If there are tenants/renters, the renters are strongly supportive&lt;/li&gt;
  &lt;li&gt;Many, many neighbors adjacent to the PUD, along Iowa St, Ford St, Washington St, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I know what sort of formal attestations of support I need to be able to demonstrate, and who I need it from, I’ll obtain it.&lt;/p&gt;

&lt;p&gt;If it’s people signing a document, they’ll sign a document.&lt;/p&gt;

&lt;p&gt;If it’s people showing up at a hearing, they’ll show up at a hearing.&lt;/p&gt;

&lt;p&gt;If, for political reasons, from the perspective of Zonning, Planning, or City Council, it is advantageous for more or less people/lots to be included within the zone of the PUD, I’ll adjust accordingly.&lt;/p&gt;

&lt;p&gt;There are many, many good things that will come about for the residents within and adjacent to the PUD, when this goes through.&lt;/p&gt;

&lt;h2 id="why-are-you-trying-to-get-a-pud-why-not-build-to-the-existing-code"&gt;Why are you trying to get a PUD? Why not build to the existing code?&lt;/h2&gt;

&lt;p&gt;I desire &lt;em&gt;flexibility&lt;/em&gt; more than almost anything else, so I (and my neighbors) can iterate and experiment with things, &lt;em&gt;and&lt;/em&gt; unlock traditional financing opportunities based off our land.&lt;/p&gt;

&lt;p&gt;We cannot finance development projects in a reasonable way, because we cannot use fractions of our land as collateral, which is a deviation from how land has traditionally been used.&lt;/p&gt;

&lt;p&gt;I spoke with a land-use lawyer at &lt;a href="https://www.foxrothschild.com/"&gt;Fox Rothschild&lt;/a&gt; about this, she agreed that a PUD made the most sense for the overall direction I’m trying to go.&lt;/p&gt;

&lt;p&gt;Part of the problem with building to the current code is that the first step of &lt;em&gt;every&lt;/em&gt; building project is to get permission for the project from the city, which requires that you submit a plan for the &lt;em&gt;finished&lt;/em&gt; project.&lt;/p&gt;

&lt;p&gt;I’m a software developer. Modern zoning forces all development projects into a “waterfall” project management strategy.&lt;sup id="fnref:waterfall"&gt;&lt;a class="footnote" href="#fn:waterfall" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt; This PUD basically allows for a property owner to adapt an agile project management approach&lt;sup id="fnref:agile"&gt;&lt;a class="footnote" href="#fn:agile" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;h2 id="what-do-you-want-to-eventually-build"&gt;What do you want to eventually build?&lt;/h2&gt;

&lt;p&gt;Critically, I am not quite sure. Once the City approves of the PUD, I’ll start building out projects. Nearly everything I’ll do, I plan on doing myself or with just a small group of people. I’ll GC my own projects, I’ll QA my own projects, and I’ll manage everything I build.&lt;/p&gt;

&lt;p&gt;I want to aid the neighborhood in implementing best-practices from:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.amazon.com/Pattern-Language-Buildings-Construction-Environmental/dp/0195019199"&gt;A Pattern Language: Towns, Buildings, Construction (Center for Environmental Structure Series)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.amazon.com/Timeless-Way-Building-Christopher-Alexander/dp/0195024028/ref=sr_1_2?dchild=1&amp;amp;keywords=christopher+alexander&amp;amp;qid=1634835871&amp;amp;s=books&amp;amp;sr=1-2"&gt;The Timeless Way of Building&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;the vast “sustainable architecture” resources out there&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:political-process"&gt;
      &lt;p&gt;Sequential approval starting with: &lt;code class="language-plaintext highlighter-rouge"&gt;planning department&lt;/code&gt;, then going to &lt;code class="language-plaintext highlighter-rouge"&gt;planning commission&lt;/code&gt;, and finally to &lt;code class="language-plaintext highlighter-rouge"&gt;city council&lt;/code&gt; &lt;a class="reversefootnote" href="#fnref:political-process"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:waterfall"&gt;
      &lt;p&gt;AKA “the linear sequential lifecycle model”. The entire software development industry jokes about the insanity of having the finished product spec’d out, and then building &lt;em&gt;that&lt;/em&gt;, regardless of what you learn/uncover/discover along the way. “Agile” is what everyone tries to do today, and I refuse to apply known-bad planning processes to my own property. &lt;a class="reversefootnote" href="#fnref:waterfall"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:agile"&gt;
      &lt;p&gt;“Agile” software development is… complicated. There’s a famous and rather nostalgia-inducing website that outlines the &lt;a href="http://agilemanifesto.org/principles.html"&gt;principles behind the agile manifesto&lt;/a&gt;. Check it out. &lt;a class="reversefootnote" href="#fnref:agile"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Fri, 01 Oct 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/pud</guid></item><item><title>Bike</title><link>https://mfashby.net/posts/2021-09-29-bike/</link><description/><author>Home on mfashby.net</author><pubDate>Fri, 01 Oct 2021 14:27:26 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-29-bike/</guid></item><item><title>Astrolabe - The October Update</title><link>https://www.marginalia.nu/log/25-october-update/</link><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="https://search.marginalia.nu"&gt;https://search.marginalia.nu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The October Update is live. It introduced drastically improved topic identification and an actual ranking algorithm; and the result is interesting to say the least. What&amp;rsquo;s striking is how much it&amp;rsquo;s beginning to feel like a search engine. When it fails to find stuff, you can kinda see how.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve played with it for a while now and it does seem to produce relevant results for a lot of topics. A trade down in whimsical results but a big step up if you are looking for something specific, at least within the domain of topics where there are results to find.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 01 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/25-october-update/</guid></item><item><title>Analyzing the Mario Themed Malware</title><link>https://serhack.me/articles/analyzing-mario-malware-en/</link><description>If you were a child with a Nintendo DS like myself, growing up you will undoubtedly have played (for countless hours) video games from the Mario series. Appearing for the first time in &lt;a href="https://it.wikipedia.org/wiki/Donkey_Kong"&gt;Donkey Kong&lt;/a&gt;, Mario is the undisputed protagonist of the video game brand of the Nintendo development house ― from sports to cars, most of the famous video games produced by Nintendo have Mario as the main character. -&lt;a href="https://serhack.me/articles/analyzing-mario-malware-en/"&gt;Continue to read the article on SerHack.me&lt;/a&gt;</description><author>SerHack - Security Researcher</author><pubDate>Fri, 01 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://serhack.me/articles/analyzing-mario-malware-en/</guid></item><item><title>Every Seattle Store</title><link>https://mattkeeter.com/projects/seattle</link><description>Based on an actual vacation</description><author>Matt Keeter</author><pubDate>Fri, 01 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://mattkeeter.com/projects/seattle</guid></item><item><title>Reliability is the most important feature - 123dev #26</title><link>https://justingarrison.com/123dev/2021-10-01-123dev-26/</link><description>&lt;img alt="A view at the internals of an oscillating fan" src="https://justingarrison.com/img/123dev/26-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="simplicity-vs-user-experience"&gt;Simplicity vs User Experience&lt;/h2&gt;
&lt;p&gt;Why do fans have that weird nob to turn on oscillation but they have buttons for speed control? Why not put an oscillation button on the front? The nob is hard to reach and especially difficult while the fan is oscillating. It’s terrible UX.&lt;/p&gt;
&lt;p&gt;It’s incredibly simple to implement which makes it extremely reliable. As a user of a fan’s interface I would prefer a button next to speed controls. As a builder, I rather not implement a secondary motor and deal with the guaranteed support and defective returns a more complex system would have.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 01 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-10-01-123dev-26/</guid></item><item><title>Cyclic Workflows with Cylc and StackStorm</title><link>https://kinoshita.eti.br/2021/10/01/cyclic-workflows-with-cylc-and-stackstorm.html</link><description>&lt;p&gt;I am aware of only two workflow managers that support cyclic workflows.
&lt;a href="https://cylc.github.io/"&gt;Cylc&lt;/a&gt; and &lt;a href="https://stackstorm.com/"&gt;StackStorm&lt;/a&gt;. I won&amp;rsquo;t
enter into details about these two tools, but I must note that I worked on Cylc
during my employment with NIWA, in New Zealand.&lt;/p&gt;
&lt;p&gt;In this post I will only show a very simple workflow called &lt;code&gt;five&lt;/code&gt; first using
Cylc, and then the same workflow with StackStorm.&lt;/p&gt;
&lt;h2 id="cylc"&gt;Cylc&lt;/h2&gt;
&lt;p&gt;First let&amp;rsquo;s take a look at the source code of this workflow with Cylc 8 and plot it.&lt;/p&gt;</description><author>About on kinow</author><pubDate>Fri, 01 Oct 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://kinoshita.eti.br/2021/10/01/cyclic-workflows-with-cylc-and-stackstorm.html</guid></item><item><title>Twitter, could you please fix your image servers?</title><link>https://utf9k.net/blog/twitter-image-servers-2021/</link><description>Throughout the year of 2021, I have run into a number of issues with Twitter's image servers</description><author>utf9k</author><pubDate>Fri, 01 Oct 2021 02:18:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/twitter-image-servers-2021/</guid></item><item><title>Startup financing - advices from a lawyer</title><link>https://yasha.solutions/posts/startup-financing-advice-from-a-lawyer/</link><description>Startup financing from a lawyer</description><author>Yasha Solutions</author><pubDate>Fri, 01 Oct 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/startup-financing-advice-from-a-lawyer/</guid></item><item><title>Undercover political campaigners are arrived</title><link>https://stop.zona-m.net/2021/09/undercover-political-campaigners-are-arrived/</link><description>&lt;p&gt;But handling them will not be easy.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 30 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/undercover-political-campaigners-are-arrived/</guid></item><item><title>Explaining explaining: a quick guide on explanatory writing</title><link>https://lucasfcosta.com/blog/explaining-in-writing/</link><description>This blog post is about how to explain things in writing.

You should learn to explain things in writing because it's the most efficient way of conveying an idea to a large, distributed audience. Whe...</description><author>Lucas F. Costa - Blog</author><pubDate>Thu, 30 Sep 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://lucasfcosta.com/blog/explaining-in-writing/</guid></item><item><title>Fundamental interactions on teams</title><link>https://adamcraven.com/writing/fundamental-interactions-on-teams/</link><description/><author>Writing on Adam Craven</author><pubDate>Thu, 30 Sep 2021 13:53:24 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/fundamental-interactions-on-teams/</guid></item><item><title>The History of My Creativity System</title><link>http://evantravers.com/articles/2021/09/30/the-history-of-my-creativity-system/</link><description>&lt;p&gt;While &lt;a href="http://evantravers.com/articles/2021/02/25/reflection-and-recall/"&gt;mapping my inputs and outputs&lt;/a&gt;… I realized the tools I used and goals I aimed for shaped my &lt;a href="/articles/series/creativity-systems"&gt;Creativity System&lt;/a&gt; over the years.&lt;/p&gt;
&lt;p&gt;This section was originally part of an overly long post… but for those of you who interested in the path I have taken to my current strategy… here you go:&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#inputs" id="inputs"&gt;&lt;/a&gt;Inputs&lt;/h3&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-links" id="-links"&gt;&lt;/a&gt;💻 Links&lt;/h4&gt;
&lt;p&gt;I have spent my career working on websites or web applications. A lot of my personal and professional growth is through blog posts and essays… &lt;a href="http://evantravers.com/articles/2020/04/15/reworking-my-rss-reading/"&gt;subscribed using RSS&lt;/a&gt; and read in a browser or often in &lt;a href="https://www.instapaper.com/"&gt;Instapaper&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-books" id="-books"&gt;&lt;/a&gt;📗 Books&lt;/h4&gt;
&lt;p&gt;Recently I've been aping &lt;a href="http://bookworm.fm"&gt;Bookworm&lt;/a&gt; and making mind maps as I read… turning the standout themes into standalone zettel notes. I have been exporting Kindle highlights/notes using &lt;a href="https://readwise.io/bookcision"&gt;Bookcision&lt;/a&gt; manually… but now &lt;a href="https://github.com/hadynz/obsidian-kindle-plugin"&gt;Obsidian Kindle Highlights&lt;/a&gt; handles that for me. (More on all this soon!)&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-meetings" id="-meetings"&gt;&lt;/a&gt;🗒 Meetings&lt;/h4&gt;
&lt;p&gt;While in meetings, listening to sermons, or even just conversations I'll often have Drafts or my Bullet Journal open, jotting down thoughts.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-shower-thoughts" id="-shower-thoughts"&gt;&lt;/a&gt;🚿 Shower Thoughts&lt;/h4&gt;
&lt;p&gt;Never underestimate shower thoughts. 🤷 More often than not… a new question or line of inquiry will suggest itself in the quiet nothingness of a walk, shower, or workout. If the notion has value… I want to be able to connect and use it later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Many&lt;/strong&gt; of my blog posts have started from such a note!&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#outputs" id="outputs"&gt;&lt;/a&gt;Outputs&lt;/h3&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-thinking" id="-thinking"&gt;&lt;/a&gt;🧠 Thinking&lt;/h4&gt;
&lt;p&gt;I want to invest in &lt;a href="https://notes.andymatuschak.org/z7kEFe6NfUSgtaDuUjST1oczKKzQQeQWk4Dbc"&gt;better thinking&lt;/a&gt;. Well made notes (especially linked!) have allowed me to have meaningful conversations with my own brain… finding new connections and revisiting old ones.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-bible-study" id="-bible-study"&gt;&lt;/a&gt;📖 Bible Study&lt;/h4&gt;
&lt;p&gt;I've always admired those whose Bibles are dog-eared and covered in marginalia. I've acquired half a dozen collections of notes and highlights across different apps and printed editions… I want to build something more permanent, portable, and searchable.&lt;/p&gt;
&lt;p&gt;I'm about six months-ish into this process… and it's starting to bear fruit. When the pastor mentions a passage… I can find every note, highlight, journal entry, or booknote that references that passage. It's hugely powerful, and hopefully over time will reduce the &amp;quot;huh, where did I hear that before?&amp;quot; feeling.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#️-writing" id="️-writing"&gt;&lt;/a&gt;✍️ Writing&lt;/h4&gt;
&lt;p&gt;I like to write blog posts. I have always toyed with the idea of writing a book someday? 🤷‍♂️&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#-ux-work" id="-ux-work"&gt;&lt;/a&gt;💼 UX Work&lt;/h4&gt;
&lt;p&gt;In my role as a User Experience Designer I deal with complex context: lost users, UX debt, oral histories, nuances of product surface, operations practices, and more. These product hypotheses and individual user insights don't belong to any single project… so hypertext notes are an &lt;em&gt;awesome&lt;/em&gt; tool.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#constraints" id="constraints"&gt;&lt;/a&gt;Constraints&lt;/h2&gt;
&lt;h3&gt;&lt;a class="anchor" href="#-data" id="-data"&gt;&lt;/a&gt;🔐 Data&lt;/h3&gt;
&lt;p&gt;I need to own my own data and avoid vendor lock-in. I prefer a &lt;a href="https://0data.app/"&gt;0data&lt;/a&gt; style application like Obsidian, and an easily portable file format like markdown.&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#-tool-investment" id="-tool-investment"&gt;&lt;/a&gt;🔨 Tool Investment&lt;/h3&gt;
&lt;p&gt;I've invested a lot of time in &lt;a href="http://evantravers.com/articles/tags/vim/"&gt;Vim&lt;/a&gt; and &lt;a href="http://evantravers.com/articles/tags/drafts-app/"&gt;Drafts.app&lt;/a&gt;. If you don't use these tools, some of my constraints may not matter, but it's worth noting.&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#history-and-obstacles" id="history-and-obstacles"&gt;&lt;/a&gt;History and Obstacles&lt;/h3&gt;
&lt;p&gt;Here's the tangled tale of my digital plaintext notes so far. I think it's most helpful to see the things that &lt;em&gt;didn't&lt;/em&gt; work, worked for a while… how I got here.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#1️⃣-random-plain-text" id="1️⃣-random-plain-text"&gt;&lt;/a&gt;1️⃣ Random plain text.&lt;/h4&gt;
&lt;p&gt;I started making simple Notepad ++/TextEdit notes at my first job and college.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-1" id="fnref-1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#2️⃣-evernote" id="2️⃣-evernote"&gt;&lt;/a&gt;2️⃣ Evernote&lt;/h4&gt;
&lt;p&gt;I flirted with this briefly… but I didn't want to be locked in a walled garden. (I was already investing in vim.)&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#3️⃣-nvaltsimplenote" id="3️⃣-nvaltsimplenote"&gt;&lt;/a&gt;3️⃣ nvAlt/SimpleNote&lt;/h4&gt;
&lt;p&gt;This was the answer for &lt;em&gt;years&lt;/em&gt;. I am 95% sure that I owe the link to Merlin Mann on MacBreak Weekly. It simplified capture by combining search and new note creation… still one of my favorite applications. I believe an nvAlt release note about &lt;code&gt;[[wikilinks]]&lt;/code&gt; introduced me to the idea of a Zettelkasten.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#4️⃣-vimwiki" id="4️⃣-vimwiki"&gt;&lt;/a&gt;4️⃣ VimWiki&lt;/h4&gt;
&lt;p&gt;Back when I was programming for a living VimWiki was an amazing option. Note-taking was always available in the same application as my work. I &lt;a href="https://web.archive.org/web/20170912030513/https://www.dailydrip.com/blog/vimwiki.html"&gt;wrote a (now archived) article&lt;/a&gt; on it because I couldn't find the info I wanted online.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#5️⃣-homebrew-zettelkasten-in-vim" id="5️⃣-homebrew-zettelkasten-in-vim"&gt;&lt;/a&gt;5️⃣ Homebrew Zettelkasten in Vim&lt;/h4&gt;
&lt;p&gt;I became frustrated by vimwiki taking over my markdown syntax in vim, and it had weird options… it's gotten a lot better since. Part of the problem for me is that I was attempting to do both a productivity system and a creativity system. Vimwiki and plaintext was absolutely capable… I just didn't have the discipline and practices required to &lt;a href="http://evantravers.com/articles/2019/10/09/sisyphus-and-the-focus-boulder/"&gt;keep from feeling overwhelmed&lt;/a&gt;. After I had &lt;a href="http://evantravers.com/articles/tags/bullet-journal/"&gt;used a Bullet Journal for a while&lt;/a&gt;… I was better prepared to understand and work productivity systems… and start to understand the value of a Creativity System.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#6️⃣-obsidian-using-zk-id" id="6️⃣-obsidian-using-zk-id"&gt;&lt;/a&gt;6️⃣ Obsidian using ZK ID&lt;/h4&gt;
&lt;p&gt;I started toying with Obsidian… and while reading about paper zettelkasten I bought into a (faulty) usage of the permanent ID. I &lt;a href="https://github.com/evantravers/plaintext-migration"&gt;wrote a transformation script&lt;/a&gt; to take all my old sources of plaintext notes and make a flat markdown folder with unique IDs.&lt;/p&gt;
&lt;h4&gt;&lt;a class="anchor" href="#7️⃣-obsidian-using-titled-notes" id="7️⃣-obsidian-using-titled-notes"&gt;&lt;/a&gt;7️⃣ Obsidian using Titled Notes&lt;/h4&gt;
&lt;p&gt;After discovering I was wrong… I arrived at the simpler &lt;a href="http://evantravers.com/articles/2021/09/28/simple-markdown-zettelkasten-1-5/"&gt;current version&lt;/a&gt;. It's been stable and slowly growing for six months. Less tinkering, more thinking and using. 🧠&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#themes" id="themes"&gt;&lt;/a&gt;Themes&lt;/h2&gt;
&lt;p&gt;I doubt &lt;em&gt;anyone&lt;/em&gt; is going to read this… but self-documentation is interesting.&lt;/p&gt;
&lt;p&gt;Some random takeaways as I consider my particular tooling journey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Plaintext is king&lt;/strong&gt;. Easily trackable, diff-able, portable.&lt;/li&gt;
&lt;li&gt;Consistent formats makes transformation by script easier.&lt;/li&gt;
&lt;li&gt;I should have tracked my notes in git &lt;em&gt;years&lt;/em&gt; ago. Having a consistent backup I understand allows me to fearlessly experiment.&lt;/li&gt;
&lt;li&gt;Notes are more just remembering facts. I never had a vision for tying together all the areas of my life… I guess I just kept my work/personal/family life compartmentalized before. Part of that productivity journey I guess.&lt;/li&gt;
&lt;li&gt;Simpler is better.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm going to be talking more in the coming weeks about specific tweaks and customizations that I've made… hopefully they'll make more sense given this context, and I'll likely link back to this where it makes sense.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-1"&gt;
&lt;p&gt;Up until my first job, each project or context (being school!) had come pre-packaged with a place to put context: a workbook, study guides, etc. Suddenly I needed a place to put useful info, and no adult was going to remember it for me. In my first year of college, I used google docs to build a note wiki… and six months into school they banned laptops in the classroom. I was an unhappy computer science major… but my handwriting did improve drastically. 😜 &lt;a class="footnote-backref" href="#fnref-1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Thu, 30 Sep 2021 10:35:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/09/30/the-history-of-my-creativity-system/</guid></item><item><title>The word for web is forest</title><link>https://nicolaiarocci.com/the-word-for-web-is-forest/</link><description>&lt;p&gt;When I read &lt;em&gt;Entangled Life: How Fungi Make Our Worlds, Change Our Minds
&amp;amp; Shape Our Futures&lt;/em&gt; by Merlin Sheldrake&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;, I was stunned by the scale and
importance of the mycorrhizal network that lies beneath the surface of any
given forest in the world. The &amp;ldquo;wood wide web&amp;rdquo;, as scientists started to call
it, sounded like the perfect metaphor for such an incredibly efficient,
symbiotic relation between fungi and trees.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 30 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/the-word-for-web-is-forest/</guid></item><item><title>Weekly Code Challenge (spring 2021)</title><link>https://muffinman.io/blog/weekly-code-challenge-spring-2021/</link><description>&lt;article class="article"&gt;&lt;p&gt;If you just want the see the submissions, jump to the &lt;a href="https://muffinman.io/code-challenge/"&gt;Weekly Code Challege&lt;/a&gt; website. But please bare in mind that the most of these were created in a couple of hours.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;This post is long overdue. Earlier this year, I came with an idea to organize a weekly code challenge in our &lt;a href="https://work.co/careers/offices/belgrade/"&gt;Belgrade office&lt;/a&gt;. With the whole  COVID situation going on, I hoped it could inspire people to start hacking and make programming fun again.&lt;/p&gt;
&lt;figure class="image__figure wide image__figure--wide"&gt;&lt;img alt="CSS only 3D people making waves" height="1000" src="./waves.png" width="1600" /&gt;&lt;/figure&gt;
&lt;p&gt;Some people were interested, so we ran the challenge in spring this year, from April to June. It lasted for ten weeks, each week had a different theme and there was a total of &lt;a href="https://muffinman.io/code-challenge/"&gt;47 submissions&lt;/a&gt;. We didn&amp;#x27;t impose a lot of rules, it just had to run in a browser, but you could use any library or framework and spend as much (or as little) time as you wanted.&lt;/p&gt;
&lt;p&gt;Challenge started hot, but as weeks passed by, interest slowly faded away. After ten weeks we decided to end it. To be honest, it lasted longer than I initially hoped. A lot of people tried new stuff, played around and had fun hacking little things. I dare to say it was a complete success and I have to say thanks to all the people who participated!&lt;/p&gt;
&lt;p&gt;Therefore, I urge you to organize something similar yourself, in your office, university or city, it doesn&amp;#x27;t matter as long as people are hacking for the sake of it. It is a wonderful and fulfilling process.&lt;/p&gt;
&lt;h2 id="shameless-self-promotion"&gt;Shameless self promotion &lt;a class="anchor-link" href="#shameless-self-promotion"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I was happy that people were interested to participate, and I enjoyed it myself tremendously. In the process, I learned a thing of two and hacked together a few things I would probably never try if there wasn&amp;#x27;t for the code challenge.&lt;/p&gt;
&lt;p&gt;Out of the nine things I created (I skipped the last week), these three are my favorite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://muffinman.io/weekly-code-challenge/week-three/index.html"&gt;ASCII rendered Flappy Bird clone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://muffinman.io/weekly-code-challenge/week-five/index.html"&gt;Sloppy clone of The Unfinished Swan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://muffinman.io/weekly-code-challenge/week-seven/index.html"&gt;CSS only 3D people making waves&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three are completely different and they vary from a vanilla JavaScript ASCII rendered game, followed by a simple ThreeJS demo to &lt;span class="sidenote__text" tabindex="0"&gt;the complex 3D shapes CSS objects.&lt;/span&gt;&lt;span class="sidenote__note"&gt;&lt;span class="sidenote__note-inner"&gt;&lt;span class="sidenote__note-text"&gt;To be fair, these are only complex in the context of using CSS to render them.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can check my submissions &lt;a href="https://muffinman.io/weekly-code-challenge/"&gt;here&lt;/a&gt;, and code is available on &lt;a href="https://github.com/Stanko/weekly-code-challenge"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description><author>Muffin Man</author><pubDate>Thu, 30 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://muffinman.io/blog/weekly-code-challenge-spring-2021/</guid></item><item><title>Writing Is Hard Work</title><link>https://ho.dges.online/words/commonplace/writing-is-hard-work/</link><description>&lt;p&gt;Writing is hard work. A clear sentence is no accident. Very few sentences come out right the first time, or even the third time. Remember this in moments of despair. If you find that writing is hard, it’s because it is hard.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;William Zinsser&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>ho.dges.online</author><pubDate>Thu, 30 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/writing-is-hard-work/</guid></item><item><title>HPA - How to gracefully scale down pods with PreStop hooks</title><link>https://shyamjos.com/kubernetes-prestop-hook/</link><description>&lt;p&gt;One of our application running in kubernetes have a background job feature (which is a bad practice and against stateless architecture) and recently we noticed some of these background jobs were killed in midway due to pod scale-down done by horizontal pod autoscaler. Our assumption was Kubernetes will only terminate pods with less utilization but we were wrong! and its a random selection.&lt;/p&gt;
&lt;p&gt;In order to handle this issue we found out that we can use prestop hook feature in kubernetes to check for any background process running in container and wait for it to complete before sending termination signal.&lt;/p&gt;
&lt;h2 class="relative group" id="how-prestop-hooks-works-in-kubernetes"&gt;How PreStop hooks works in kubernetes &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#how-prestop-hooks-works-in-kubernetes"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Kubernetes provides container lifecycle hook framework to run code triggered by events during their management lifecycle called PostStart and PreStop hooks. A PreStop hook is called immediately before sending termination signal and kubernetes will wait until the PreStop hook to complete or until it exceed the &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; value, Only after this kubernetes will sent the termination signal to the container.&lt;/p&gt;
&lt;h2 class="relative group" id="our-solution"&gt;Our solution &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#our-solution"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Our solution was to use a bash script for PreStop hook which will check for &lt;code&gt;.lock&lt;/code&gt; files created by the background process, if it finds a &lt;code&gt;.lock&lt;/code&gt; file the script will wait for the &lt;code&gt;.lock&lt;/code&gt; file to be removed and when the file is removed the script will exit then the container will receive termination signal. If the PreStop hook takes more time than &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; value then container will be terminated immediately once this value is crossed.&lt;/p&gt;
&lt;h3 class="relative group" id="prestop-hook-bash-script"&gt;PreStop hook bash script &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#prestop-hook-bash-script"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;#PreStop Script for checking background php tasks 
SECONDS=0
while true
do	
if [ -z "$(ls -A /app/demo-web/public/lock/*.lock 2&amp;gt; /dev/null)" ]; then
 echo "No lock files found!, container is safe to terminate [Time Elapsed: ${SECONDS}s]."
 exit 0
else
 echo "Lock files found!, waiting for background process to complete [Time Elapsed: ${SECONDS}s]."
 sleep 10
fi
done &amp;gt; /proc/1/fd/1 # sent outputs to conatiner's stdout
&lt;/code&gt;&lt;/pre&gt;&lt;h3 class="relative group" id="final-deployment-yaml"&gt;Final deployment yaml &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#final-deployment-yaml"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
 name: demo-web
 namespace: production
spec:
 progressDeadlineSeconds: 300
 revisionHistoryLimit: 5
 strategy:
 type: RollingUpdate
 rollingUpdate:
 maxSurge: 5
 maxUnavailable: 0
 selector:
 matchLabels:
 app: demo-web
 tier: web
 environment: production 
 template:
 metadata:
 labels:
 app: demo-web
 tier: web
 environment: production
 spec:
 terminationGracePeriodSeconds: 300
 containers:
 - name: demo-web
 image: demo/demoweb:v1
 lifecycle:
 preStop :
 exec:
 command: ["bash", "/app/scripts/prestop.sh"] 
 imagePullPolicy: Always
 resources:
 requests:
 cpu: 50m
 memory: 150Mi
 limits:
 memory: "650Mi" 
 ports:
 - containerPort: 80
 readinessProbe:
 httpGet:
 path: /healthz
 port: 80
 initialDelaySeconds: 2
 periodSeconds: 5
 successThreshold: 1
 failureThreshold: 2
 timeoutSeconds: 2
 env:
 - name: HOST 
 value: "demo.app.com"
&lt;/code&gt;&lt;/pre&gt;</description><author>Shyam Jos</author><pubDate>Thu, 30 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://shyamjos.com/kubernetes-prestop-hook/</guid></item><item><title>Integration Test Email #25 - 123dev #25</title><link>https://justingarrison.com/123dev/2021-09-30-123dev-25/</link><description>&lt;img alt="A building floor collapses as a construction crew lays concrete" src="https://justingarrison.com/img/123dev/25-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="whos-to-blame"&gt;Who’s to blame?&lt;/h2&gt;
&lt;p&gt;Many of us probably heard about the HBOMax email that was sent out by mistake last week. It wasn’t a major outage, but a lot of people were talking about it. I especially loved the outpouring of support I saw online for whomever triggered the integration job that sent the email. 💖&lt;/p&gt;
&lt;p&gt;Just like the gif, no one person is to blame. The person who poured cement isn’t any more to blame than the person who architected the building. With any incident, if a single person can cause an outage it is almost certainly not their fault.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 30 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-30-123dev-25/</guid></item><item><title>GitHub Actions Plugin for Cronicle</title><link>https://hartenfeller.dev/blog/github-actions-cronicle</link><description>I created a GitHub Actions Plugin for calling jobs on your Cronicle server.</description><author>Philipp Hartenfeller Blog RSS Feed</author><pubDate>Thu, 30 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://hartenfeller.dev/blog/github-actions-cronicle</guid></item><item><title>Recipe</title><link>https://mfashby.net/posts/2021-09-29-recipe/</link><description/><author>Home on mfashby.net</author><pubDate>Thu, 30 Sep 2021 00:56:01 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-29-recipe/</guid></item><item><title>Profiling for USA 2024 is already happening</title><link>https://stop.zona-m.net/2021/09/profiling-for-usa-2024-is-already-happening/</link><description>&lt;p&gt;On some websites, there are no innocent questions.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 29 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/profiling-for-usa-2024-is-already-happening/</guid></item><item><title>HyperX Cloud II sound card USB connector replacement</title><link>https://muffinman.io/blog/hyperx-cloud-II-sound-card-usb-connector-replacement/</link><description>&lt;article class="article"&gt;&lt;p&gt;If you are only interested in the wiring diagram, jump directly to the &lt;a href="#wiring"&gt;wiring&lt;/a&gt; section.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;HyperX&amp;#x27;s Cloud II headphones come with a USB sound card, which looks like this:&lt;/p&gt;
&lt;figure class="image__figure"&gt;&lt;img alt="USB sound card that comes with Cloud II headphones" height="1440" src="./usb-sound-card.jpg" width="1920" /&gt;&lt;/figure&gt;
&lt;p&gt;It is a convenient little dongle. I had it plugged in into the front USB port, and I bumped into it, bending the connector for ninety degrees. Naturally, it stopped working. Good thing about these headphones is that the sound card isn&amp;#x27;t mandatory for headphones to work, but the regular audio cable is pretty short.&lt;/p&gt;
&lt;p&gt;After searching for the replacement, I realized I can&amp;#x27;t buy it where I live, and that it costs around 35-40$ which is more than the half of the price for a whole new pair. That is why I decided to try to repair it on my own.&lt;/p&gt;
&lt;p&gt;The reason I&amp;#x27;m writing this blog post is that I couldn&amp;#x27;t find &lt;strong&gt;any&lt;/strong&gt; information on how to repair it, so hopefully it will help you fix the same problem.&lt;/p&gt;
&lt;h2 id="wiring"&gt;Wiring &lt;a class="anchor-link" href="#wiring"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The process is actually super simple. You&amp;#x27;ll need some soldering experience and a USB connector. Cut off the original connector and strip the wires.&lt;/p&gt;
&lt;p&gt;Solder wires to a connector in a following order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;span style="color: black;"&gt;Black&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;span style="color: green;"&gt;Green&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;span style="color: darkgoldenrod;"&gt;Copper&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;span style="color: red;"&gt;Red&lt;/span&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some diagrams will show you different order with middle (data) wires switched, but that won&amp;#x27;t work. I tried soldering it like that initially, but nor Windows nor Mac would recognize the headphones. The HyperX logo would light up, but Windows gave me a &lt;em&gt;&amp;quot;Unknown USB Device (Device Descriptor Request Failed)&amp;quot;&lt;/em&gt; message. That&amp;#x27;s how I figured out that power wires are connected properly, but the data ones are not.&lt;/p&gt;
&lt;figure class="image__figure"&gt;&lt;img alt="USB connector with wires soldered in the correct order" height="1440" src="./wires.jpg" width="1920" /&gt;&lt;/figure&gt;
&lt;p&gt;Sorry for the blurry photo, but I didn&amp;#x27;t want to disassemble everything to get a better one.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I hope this will help you fix your headphones and motivate you to try to fix more things in the future, before just ordering a replacement.&lt;/p&gt;&lt;/article&gt;</description><author>Muffin Man</author><pubDate>Wed, 29 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://muffinman.io/blog/hyperx-cloud-II-sound-card-usb-connector-replacement/</guid></item><item><title>Declarative and imperative - 123dev #24</title><link>https://justingarrison.com/123dev/2021-09-29-123dev-24/</link><description>&lt;img alt="Bowling pins are automatically set up" src="https://justingarrison.com/img/123dev/24-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="declarative-and-imperative"&gt;Declarative and imperative&lt;/h2&gt;
&lt;p&gt;Oftentimes in technology we say declarative things are better than imperative. Why write a script when you can create an abstraction? In reality there’s no such thing as something being completely declarative outside maybe binary data in memory, but even that requires sequential steps before it can be stored.&lt;/p&gt;
&lt;p&gt;However, we can hide imperative steps behind a declarative interface and a user never needs to know about the complexity. Just like these bowling pins, a bowler doesn’t know how they got there. They ask for another set of pins and it shows up.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 29 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-29-123dev-24/</guid></item><item><title>Review: Beast Breaker</title><link>https://xavd.id/blog/post/beast-breaker-review/</link><description>What if you were playing a billiards RPG, but your cue ball was you, an adorable warrior mouse armed with a variety of pointy objects?&lt;br /&gt;&lt;br /&gt;&lt;a href="https://xavd.id/blog/post/beast-breaker-review/"&gt;Read the whole thing&lt;/a&gt;.</description><author>The David Brownman Blog</author><pubDate>Wed, 29 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xavd.id/blog/post/beast-breaker-review/</guid></item><item><title>Confusion and conflicts around Open Source in Italy</title><link>https://stop.zona-m.net/2021/09/confusion-and-conflicts-around-open-source-in-italy/</link><description>&lt;p&gt;What happens when lots of funding comes to digitize a country?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 29 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/confusion-and-conflicts-around-open-source-in-italy/</guid></item><item><title>Decompression Meddlings</title><link>https://nevesnunes.github.io/blog/2021/09/29/Decompression-Meddlings.html</link><description/><author>onKeyPress</author><pubDate>Wed, 29 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://nevesnunes.github.io/blog/2021/09/29/Decompression-Meddlings.html</guid></item><item><title>Simple Markdown Zettelkasten 1.5</title><link>http://evantravers.com/articles/2021/09/28/simple-markdown-zettelkasten-1-5/</link><description>&lt;p&gt;I've been quietly using a new format of my zettelkasten&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-zk" id="fnref-zk"&gt;1&lt;/a&gt;&lt;/sup&gt; for almost six months. I wrote &lt;a href="https://github.com/evantravers/undo-zk"&gt;yet another giant migration script&lt;/a&gt;… but I wanted my system to stabilize before I wrote about it again.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-stable" id="fnref-stable"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In short: I'm doing the easiest possible thing that Obsidian recommends.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-gk" id="fnref-gk"&gt;3&lt;/a&gt;&lt;/sup&gt; 🤷&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I &lt;strong&gt;no longer&lt;/strong&gt; use a &amp;quot;Zettelkasten ID&amp;quot; in the filename of every file.&lt;/li&gt;
&lt;li&gt;I'm using YAML frontmatter instead of MultiMarkdown.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Simply:&lt;/p&gt;
&lt;p&gt;Filename: &lt;code&gt;Sentence style notes.md&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Content:&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-markdown" tabindex="0"&gt;&lt;div class="line"&gt;&lt;span&gt;---&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;optional&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;---&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;# Sentence Style Notes
&lt;/div&gt;&lt;div class="line"&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class="line"&gt;I'm using simple markdown notes for evergreen notes. This makes [[linking]] easier and doesn't mean I have to use aliases to compensate for a ZK ID in the filename… making [[Note-Making]] *much* easier. The YAML frontmatter is optional… mostly tags and aliases.
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;Related:
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;- &lt;/span&gt;I'll often have a block of related notes
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;- &lt;/span&gt;Maps of Content or hubs
&lt;/div&gt;&lt;div class="line"&gt;&lt;span&gt;- &lt;/span&gt;links to daily notes that make sense
&lt;/div&gt;&lt;div class="line"&gt;
&lt;/div&gt;&lt;div class="line"&gt;[Optional link to a source or reference](https://fakesourceorreference.com/facts)
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a class="anchor" href="#zettelkasten-id-in-filename" id="zettelkasten-id-in-filename"&gt;&lt;/a&gt;Zettelkasten ID in Filename&lt;/h2&gt;
&lt;p&gt;The reason I wanted to have the ZK ID in the filename was to preserve links throughout rewriting notes. While robust, this strategy sacrifices ease of writing… and creativity is what this is for!&lt;/p&gt;
&lt;p&gt;If the filename contains the ZK-ID (something like &lt;code&gt;2021092723-fake-name.md&lt;/code&gt;) then you have three options to reference another file as a link:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;know the exact second you created an unseen note from memory (impossible!)&lt;/li&gt;
&lt;li&gt;have the computer autocomplete the title (difficult, since most file autocompletes start from the first character)&lt;/li&gt;
&lt;li&gt;use the Alias feature in Obsidian to create a duplicate title… that you will mostly like use anyway. (extra useless typing!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As soon as I made the conversion back… there was no going back. Composing a note with links was &lt;em&gt;easy&lt;/em&gt; no matter what app I was using. I could even handwrite and scan a reference to &lt;code&gt;[[Exodus]]&lt;/code&gt; or &lt;code&gt;[[Border of the Known]]&lt;/code&gt; and &lt;em&gt;know&lt;/em&gt; which note to which I was linking.&lt;/p&gt;
&lt;p&gt;What about link robustness you may ask? Well… in part I am relying on Obsidian's toolchain for now, but I'm also a software developer, and my whole Creativity System is now backed up in Git. If I find a link missing, I can &lt;a href="http://evantravers.com/articles/2018/09/28/code-spelunking/"&gt;do some spelunking&lt;/a&gt; and find the missing file. If I need to apocalypse-proof my knowledge system… I have shown myself to be perfectly able to write textfile migrations. I'll generate a unique ID for each note and can append it to every link for printing.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#yaml-frontmatter" id="yaml-frontmatter"&gt;&lt;/a&gt;YAML Frontmatter&lt;/h2&gt;
&lt;p&gt;Using YAML frontmatter instead of MultiMarkdown is a less contentious choice… most Markdown applications these days support a version of YAML frontmatter even before Obsidian… and few indeed supported MMD.&lt;/p&gt;
&lt;h2&gt;&lt;a class="anchor" href="#where-to-now" id="where-to-now"&gt;&lt;/a&gt;Where To Now?&lt;/h2&gt;
&lt;p&gt;I've been doing a lot more writing and thinking with my Creativity System now that the friction of creation has decreased. I've got a lot more thinking and tricks to post… but I figured I'd talk about it in bite-sized chunks… I've been rewriting the same 3k word post for six months.&lt;/p&gt;
&lt;p&gt;The longer I learn about this world of PKM/ZK/Creativity Systems the more convinced I am that each person should be building their system to suit their needs and mind. It's a &lt;em&gt;deeply&lt;/em&gt; personal tool. You shouldn't copy my methods or tools directly… unless you have similar goals.&lt;/p&gt;
&lt;p&gt;Thank you especially to all the readers and internet friends who have sent me encouraging emails and questions. I don't have analytics on this blog… so your kind words keep me going!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have often had a fancy for writing a romance about an English yachtsman who slightly miscalculated his course and discovered England under the impression that it was a new island in the South Seas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I too have set sail to find the best zettel format… and found myself back home at the original and simplest.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-zk"&gt;
&lt;p&gt;Some will point out that this is no longer a &amp;quot;true Luhrmann Zettelkasten.&amp;quot; You'd be right. This is what has inspired my definition of &lt;a href="http://evantravers.com/articles/series/creativity-systems"&gt;Creativity Systems&lt;/a&gt;: a system designed to connect and create ideas. &lt;a class="footnote-backref" href="#fnref-zk"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-stable"&gt;
&lt;p&gt;I'm kind of regretting not originally writing a stand-alone &amp;quot;moc&amp;quot;-style essay page with a permalink. I had no clue the post would take off like it did. I may yet do that. &lt;a class="footnote-backref" href="#fnref-stable"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-gk"&gt;
&lt;p&gt;I'm reminded of the glorious word picture from &lt;em&gt;Orthodoxy&lt;/em&gt; by G.K. Chesterton: &lt;a class="footnote-backref" href="#fnref-gk"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Tue, 28 Sep 2021 10:51:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/09/28/simple-markdown-zettelkasten-1-5/</guid></item><item><title>Colophon</title><link>https://jay.jvf.cc/colophon/</link><description>&lt;p&gt;My blog, jay.jvf.cc, is built using &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; framework with a lightly modified version of the
&lt;a href="https://github.com/panr/hugo-theme-terminal"&gt;Terminal&lt;/a&gt; theme by &lt;a href="https://twitter.com/panr"&gt;panr&lt;/a&gt;. Once built, it is
hosted on Google Cloud and fronted with Cloudflare for HTTPS termination.&lt;/p&gt;
&lt;p&gt;For more information see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jay.jvf.cc/posts/about-itself/" title="About Itself"&gt;About Itself&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author/><pubDate>Tue, 28 Sep 2021 06:17:37 GMT</pubDate><guid isPermaLink="true">https://jay.jvf.cc/colophon/</guid></item><item><title>Nostalgia technology, DNS, and lessons learned as a senior engineer - 123dev #23</title><link>https://justingarrison.com/123dev/2021-09-28-123dev-23/</link><description>&lt;img alt="An old advertisement showing how an inflatable car jack works" src="https://justingarrison.com/img/123dev/23-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="nostalgia-technology"&gt;Nostalgia technology&lt;/h2&gt;
&lt;p&gt;Looking back at old technology products most of the time I think how bad they were. Occasionally, I remember something I really liked and I wish I could have it again. But then I realize the products were good at the time but probably would hold back innovations in ways I couldn’t predict.&lt;/p&gt;
&lt;p&gt;Much like this car jack which seems like a great idea! Takes up minimal amount of space, self inflates, and probably about as safe as scissor jacks are today. But when you think about it maybe if we all had self inflating car jacks the electric car would have one more obstacle to overcome.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 28 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-28-123dev-23/</guid></item><item><title>Urban air travel, this is not</title><link>https://stop.zona-m.net/2021/09/urban-air-travel-this-is-not/</link><description>&lt;p&gt;Even if there were enough air to do it.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 28 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/urban-air-travel-this-is-not/</guid></item><item><title>Balancing production and consumption in the digital era</title><link>https://www.unsungnovelty.org/posts/09/2021/balancing-production-and-consumption-in-the-digital-era/</link><description>&lt;blockquote&gt;
&lt;p&gt;License: &lt;a href="https://creativecommons.org/licenses/by-nc-nd/4.0/"&gt;Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
_International Public License (CC BY-NC-ND
_4.0)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most of us are addicted to consumption in this modern digital era. Or, &lt;a href="https://en.wikipedia.org/wiki/Information_Age"&gt;Information Age&lt;/a&gt;. Information is available in abundance now. The amount which was never accessible for us humans ever since the dawn of time. We watch documentaries about planets, listen to podcasts on varied topics and can read about virtually anything in just Wikipedia alone. But this has reached a point where we are consuming in excess. We are less focused on doing anything with the information and more focused on the consumption itself. We have reached a point where we are in a consumption spree whether it be books, blogs, podcasts, youtube channels or learning.&lt;/p&gt;
&lt;p&gt;A great way to counter the consumption spree is by balancing it out with production. Producing things that you want to and always have wanted to produce. It might be blog posts, youtube videos or art. You get the idea. The idea is to balance the consumption out with production. But I should tell you that there is a lot of unlearning that you have to do first in order to &lt;em&gt;balance excessive consumption out&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Unlearning means exactly what it sounds like. You will have to learn to do things in a different way by changing or forgetting the way you used to do things before. The first thing to do is to unlearn or remove the excess junk in your daily life.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our brain seeks things that we feed it regularly. If you feed useful and interesting things, our brain seeks more useful and interesting things. On the other hand if you feed it useless or uninteresting things, our brain will gladly seek more useless or uninteresting things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you are consuming in excess, it means there are many things like facebook, instagram, youtube or Twitter which are notorious for pushing you to an endless scroll of consuming useless information. You need to first clean up and unlearn them before having to do trade-offs between things that are more useful compared to these useless information. Like an interesting podcast or a youtube channel about history for instance are useful than endless scroll of useless information. Meanwhile learning a skill that will help you professionally is more useful than the aforementioned interesting podcast or the youtube channel about history.&lt;/p&gt;
&lt;p&gt;The process of unlearning and relearning is about constantly cleaning up our consumption habits and replacing with better, less excessive consumption habits until we reach a sweet spot we know is better for us.&lt;/p&gt;
&lt;p&gt;Going organic is great way to speed this process. I stopped having goals for reading books and writing blog posts. Or for the number of podcasts, youtube channels that I follow for that matter. Our brain needs time to process the information we have just consumed or are working with. So reading a book just after you finished another doesn’t let you extract the same amount of information. This is the same reason why you sometimes wake up with a solution you were working on the night before but couldn’t figure out. The same reason why sometimes short walks helps you solve a problem you were stuck with. So take your time to write a summary of the thing that you just consumed. I have personal notes for all the books that I have read and keep notes on podcast episodes I listen to. Going organic might seem slow for you at first. But if you really like consuming something, the best way to do it is by consuming it organically. This is because it will help you focus more and eventually the rate at which you can consume things increases.&lt;/p&gt;
&lt;p&gt;Once you’ve balanced out consumption, the main villain. It is time for you to start producing things. Write blog posts instead of just reading them. Create an app that you’ve always wanted to. Start working on your next big project and build things with the information you’ve learned. It is the best way to use your knowledge. So produce! …Instead of just consuming.&lt;/p&gt;
&lt;h2 id="references-and-links"&gt;References and links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.fastcompany.com/90387269/your-obsession-with-learning-is-holding-you-back"&gt;https://www.fastcompany.com/90387269/your-obsession-with-learning-is-holding-you-back&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have thoughts or feedback regarding my post/writing? DM me on Twitter &lt;a href="https://twitter.com/unsungNovelty"&gt;@unsungNovelty&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description><author>unsungNovelty</author><pubDate>Mon, 27 Sep 2021 19:32:43 GMT</pubDate><guid isPermaLink="true">https://www.unsungnovelty.org/posts/09/2021/balancing-production-and-consumption-in-the-digital-era/</guid></item><item><title>An nginx playground</title><link>https://nicolaiarocci.com/an-nginx-playground/</link><description>&lt;p&gt;Every single time I need nginx, I end up spending way too much time fiddling
around with its configuration. If you&amp;rsquo;re like me, rejoice! Julia Evans built
a lovely, helpful little tool called nginx playground.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello! On Wednesday I was talking to a friend about how it would be cool to
have an nginx playground website where you can just paste in an nginx config
and test it out. And then I realized it might actually be pretty easy to
build, so got excited and started coding and I built it.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 27 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/an-nginx-playground/</guid></item><item><title>Quickstart to Building Golang Apps using IPFS</title><link>https://bartlomiejmika.com/posts/2021/quickstart-to-building-golang-apps-using-ipfs/</link><description>&lt;p&gt;Do you want to write a Golang app which will use &lt;a href="http://ipfs.io"&gt;IPFS&lt;/a&gt;? The purpose of this post is to get you up and running &lt;a href="http://ipfs.io"&gt;IPFS&lt;/a&gt; as quickly as possible so you can see what&amp;rsquo;s involved.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Mon, 27 Sep 2021 06:58:04 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/quickstart-to-building-golang-apps-using-ipfs/</guid></item><item><title>Thoughts on Silly Hats</title><link>https://www.marginalia.nu/log/24-silly-hats/</link><description>&lt;p&gt;If you look back in history to the turn of the 20th century, you will find a lot of people wearing hats. Women wore some arguably pretty funny over the top hats. A while earlier, over the men were arguably funny top hats. This isn&amp;rsquo;t the first time men and women wore funny clothes. The aristocrats in 1680s France looked pretty silly too.&lt;/p&gt;
&lt;p&gt;The reason we do this, wear silly hats, is because it is fashionable. Compliance with the some perceived fashion trend is one way we compete with fellow human beings, a measuring stick we use to evaluate our standing within society. Oh, you merely wear a modest and peculiar hat? Well mine is bigger and sillier still, therefore I am better!&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Mon, 27 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/24-silly-hats/</guid></item><item><title>Node.js fork is slow; Deal with it</title><link>https://www.alexanderlolis.com/node-js-fork-is-slow-deal-with-it</link><description>Dealing with Node.js fork slowness</description><author>Alexander's Blog Blog</author><pubDate>Mon, 27 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.alexanderlolis.com/node-js-fork-is-slow-deal-with-it</guid></item><item><title>SFTP is super awesome</title><link>https://blog.imraniqbal.org/sftp/</link><description>&lt;p&gt;My dad has a site that he has been working on since the windows 3.1 days.
It's static site and up until now he had been paying a little over a hundred dollars a year to have it hosted.&lt;/p&gt;
&lt;p&gt;This came up during the winter of last year, where he had to pay for a renewal for his domain as well as his hosting.
The hosting company did not accept payment from where he's located, so he asked me to handle the payments on his behalf.
This is when I realized that he has been overpaying for a service that you can get without paying (e.g GitLab pages).
There was no getting away from the cost of the domain name, that's just the nature of having your own domain, but hosting (for static sites) can be costless.&lt;/p&gt;
&lt;p&gt;His current workflow was to make changes on his computer then upload the files via ftp to the hosting platform.
These files are served out of the ftp directory to the web.&lt;/p&gt;
&lt;p&gt;Getting my dad to use git was a no go from the start.
As much as I like it and use it everyday for personal and professional use, it isn't beginner friendly.
In fact there's a good chunk of professional programmers that cannot use it properly either.
On top of learning the git commands and workflow he would also need to understand ssh keys which are not that easy to explain either (or why they are important).
Especially coming from the old ftp workflow.&lt;/p&gt;
&lt;h1 id="the-solution"&gt;&lt;a class="anchor" href="https://blog.imraniqbal.org/sftp/#the-solution"&gt;
    #
&lt;/a&gt;
The solution&lt;/h1&gt;
&lt;p&gt;I still wanted to his site backed by git.
I think it's a great idea as it gives us source control instead of just files that live on his hard drive.
Having a git repository also lets me take advantage of the free hosting that GitLab pages provides.&lt;/p&gt;
&lt;p&gt;This means I would need to be able to provide a ftp like interface that ties into a git repository.
My first thought was that I need something that I can watch a directory for file system events, and after a set delay execute some commands.
&lt;a href="https://facebook.github.io/watchman/" rel="external"&gt;Watchman&lt;/a&gt; exists but its a little heavy handed for what I needed.&lt;/p&gt;
&lt;p&gt;I decided to write my own tool &lt;a href="https://gitlab.com/imran_iqbal/dcmd" rel="external"&gt;&lt;code&gt;dcmd&lt;/code&gt;&lt;/a&gt;.
This also gave me a convenient excuse to dabble in rust some more.&lt;/p&gt;
&lt;p&gt;This would cover the committing + pushing part of our solution.
All that I had to do was to provide ftp interface that pointed to the relevant part of the git project.&lt;/p&gt;
&lt;p&gt;I originally wanted to use &lt;code&gt;vsftp&lt;/code&gt; as it's included in arch's community repository.
At moment it seems to not be working (core dumps on access) as well as out of date.
The other thing I did not like about &lt;code&gt;vsftp&lt;/code&gt; is that it's underlying protocol is ftp and ftp is not secure (sends password over plain text).&lt;/p&gt;
&lt;p&gt;This is where SFTP comes in.&lt;/p&gt;
&lt;p&gt;It's provided by default by OpenSSH and as the &lt;code&gt;S&lt;/code&gt; indicates it's more secure.
I would need to do the following to get it setup&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a SFTP user&lt;/li&gt;
&lt;li&gt;Create a chroot jail for the user&lt;/li&gt;
&lt;li&gt;Create a symlink in the jail that maps to the static files in the git repository&lt;/li&gt;
&lt;li&gt;Edit my &lt;code&gt;sshd_config&lt;/code&gt; file to use the jail&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now I mentioned before that my dad is not familiar with ssh (and ssh keys) so I would need a password for him.
On my server I disable password logins for security.
I thought this would be a problem but turns out we can have both out of the box.
All I needed to add to my configuration was this:&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span&gt;&amp;lt;Rest of config&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Match User &amp;lt;my sftp user&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;        ChrootDirectory &amp;lt;path to my jail&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;        ForceCommand internal-sftp&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;        PasswordAuthentication yes&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Match all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This forces the SFTP user to the jail and allows it to login using a password.
I had concerns that this would also grant ssh access to my machine but turns out it does not:&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span&gt;❯ ssh &amp;lt;user&amp;gt;@&amp;lt;host&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;&amp;lt;user&amp;gt;@&amp;lt;hosts&amp;gt;'s password:&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;This service allows sftp connections only.&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;Connection to &amp;lt;host&amp;gt; closed.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if the password gets leaked I can be sure that my server remains secured (and why the title of this post is about SFTP being awesome).&lt;/p&gt;
&lt;p&gt;The last thing I wanted to do is limit the blast radius if somehow the git credentials were compromised.
Basically I do not want to lose any commits or have the repository deleted.
For this I ended up creating a "bot" account on GitLab (just a fresh account) and gave it its own ssh keys.
This bot would then have developer (not maintainer) access to the git project.
This means the bot can push directly to the repository and have CI run and deploy to pages, but it can't change repository settings or force push.&lt;/p&gt;
&lt;p&gt;The one other thing I was missing a systemd service file for &lt;code&gt;dcmd&lt;/code&gt; that would ensure it keeps running.
This was simple enough (I've done it before for other things) and there's a crap ton of resources on the internet for it (including the systemd man pages) so I won't be pasting the service file here.&lt;/p&gt;
&lt;p&gt;The last thing to do before handing the credentials over to my dad was to make sure that this whole setup actually worked.&lt;/p&gt;
&lt;p&gt;I SFTP'd in with the user I had set up and this is when I found out that you can't symlink to directories out of the chroot jail.
This kind of makes sense and it would not be a good jail if the user could access files outside of it.&lt;/p&gt;
&lt;p&gt;A bind mount remedied this problem.&lt;/p&gt;
&lt;pre class="giallo" style="color: #CCCAC2; background-color: #1F2430;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span&gt;mount --bind src_dir target_dir&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bind mounts basically copies a directory over to another place, but they point to the same files in the file system.
This means that if the files are added/removed in the SFTP directory they are also modified in the git repository.
This lets &lt;code&gt;dcmd&lt;/code&gt; do its job and create a commit and push it.&lt;/p&gt;
&lt;p&gt;My dad confirmed that the credentials worked for him and he can go back to his old workflow.
The difference this time is his work is now preserved somewhere else other than his computer and he gets to save some money! :D&lt;/p&gt;
&lt;p&gt;This was a super fun side project and I can already think of some improvements to &lt;code&gt;dcmd&lt;/code&gt; if I ever want dabble in rust some more.
It's a nice feeling when I can use my expertise/skills to help out my dad.&lt;/p&gt;</description><author>Imran's Blog</author><pubDate>Mon, 27 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.imraniqbal.org/sftp/</guid></item><item><title>🐱🕯️ Curious tools and resume tips - 123dev #22</title><link>https://justingarrison.com/123dev/2021-09-27-123dev-22/</link><description>&lt;img alt="A cat hits a candle and falls off a counter" src="https://justingarrison.com/img/123dev/22-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="curiosity"&gt;Curiosity&lt;/h2&gt;
&lt;p&gt;Sometimes our curiosity gets the best of us. I cannot count how many times I’ve run a command, changed a config, or pushed a button that burned me. Usually, I learn my lesson. Many times I’ve put up guardrails or warnings so other devs wouldn’t burn themselves in exactly the same way. People are very creative and until you remove the candle they’ll always find a way to learn on their own.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 27 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-27-123dev-22/</guid></item><item><title>Blockchain not Bitcoin, NFT not Art</title><link>https://zefram.xyz/posts/nft-not-art/</link><description>&lt;h2 id="the-origin-of-value-what-nfts-are-and-what-comes-next"&gt;The origin of value, what NFTs are, and what comes next&lt;/h2&gt;
&lt;p&gt;I was baffled by the rise of NFTs for quite some time.&lt;/p&gt;
&lt;p&gt;Why would people spend such exorbitant amounts on jpegs? The art is not even that good most of the time. If people wanted beautiful art, isn’t it more sensible to spend far less on local artists who will most likely produce much better art? Physical art, that is, not jpegs that anyone could “right click and save”.&lt;/p&gt;</description><author>Zefram's Blog</author><pubDate>Sun, 26 Sep 2021 22:31:24 GMT</pubDate><guid isPermaLink="true">https://zefram.xyz/posts/nft-not-art/</guid></item><item><title>Pine64</title><link>https://mfashby.net/posts/2021-09-26-pine64/</link><description/><author>Home on mfashby.net</author><pubDate>Sun, 26 Sep 2021 18:39:28 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-26-pine64/</guid></item><item><title>Migrating my Jekyll site to Netlify</title><link>https://josh.works/migrating-to-netlify</link><description>&lt;p&gt;Troubleshooting Netilify deploy&lt;/p&gt;

&lt;p&gt;Ugggh I moved &lt;a href="https://intermediateruby.com"&gt;intermediateruby.com&lt;/a&gt; to Netlify a few months ago in like 10 minutes, so my primary site, &lt;code class="language-plaintext highlighter-rouge"&gt;josh.works&lt;/code&gt;, should take maybe 20, right?&lt;/p&gt;

&lt;p&gt;I’m a few hours deep. Here’s what I get when Netlify tries to build:&lt;/p&gt;

&lt;p&gt;I should have done the following &lt;em&gt;first&lt;/em&gt;, instead of last:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;9:58:21 AM:   1. Build command from Netlify app                             
9:58:21 AM: ────────────────────────────────────────────────────────────────
9:58:21 AM: ​
9:58:21 AM: $ bundle exec jekyll build
9:58:22 AM: Configuration file: /opt/build/repo/_config.yml
9:58:22 AM:             Source: /opt/build/repo
9:58:22 AM:        Destination: /opt/build/repo/_site
9:58:22 AM:  Incremental build: disabled. Enable with --incremental
9:58:22 AM:       Generating...
9:58:23 AM:                     ------------------------------------------------
9:58:23 AM:       Jekyll 4.2.0   Please append `--trace` to the `build` command 
9:58:23 AM:                      for any additional information or backtrace. 
9:58:23 AM:                     ------------------------------------------------
9:58:23 AM: ​
9:58:23 AM: ────────────────────────────────────────────────────────────────
9:58:23 AM:   "build.command" failed                                        
9:58:23 AM: ────────────────────────────────────────────────────────────────
9:58:23 AM: ​
9:58:23 AM:   Error message
9:58:23 AM:   Command failed with exit code 1: bundle exec jekyll build
9:58:23 AM: ​
9:58:23 AM:   Error location
9:58:23 AM:   In Build command from Netlify app:
9:58:23 AM:   bundle exec jekyll build
9:58:23 AM: ​
9:58:23 AM:   Resolved config
9:58:23 AM:   build:
9:58:23 AM:     command: bundle exec jekyll build
9:58:23 AM:     commandOrigin: ui
9:58:23 AM:     publish: /opt/build/repo/_site
9:58:23 AM:     publishOrigin: ui
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, first thing I did was go into the &lt;code class="language-plaintext highlighter-rouge"&gt;build settings&lt;/code&gt; and update the &lt;code class="language-plaintext highlighter-rouge"&gt;build&lt;/code&gt; command, to append &lt;code class="language-plaintext highlighter-rouge"&gt;--trace&lt;/code&gt; to it:&lt;/p&gt;

&lt;p&gt;&lt;img alt="netlify build" src="/images/netlify-build.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Now I get:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;10:18:23 AM: $ bundle exec jekyll build --trace
10:18:23 AM: Configuration file: /opt/build/repo/_config.yml
10:18:23 AM:             Source: /opt/build/repo
10:18:23 AM:        Destination: /opt/build/repo/_site
10:18:23 AM:  Incremental build: disabled. Enable with --incremental
10:18:23 AM:       Generating...
10:18:24 AM: /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-crosspost-to-medium-0.1.16/lib/jekyll-crosspost-to-medium.rb:44:in `generate': MediumCrossPostGenerator: Environment variables not found (ArgumentError)
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:193:in `block in generate'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:191:in `each'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:191:in `generate'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:79:in `process'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:28:in `process_site'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/commands/build.rb:65:in `build'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/commands/build.rb:36:in `process'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:91:in `block in process_with_graceful_fail'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:91:in `each'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:91:in `process_with_graceful_fail'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/lib/jekyll/commands/build.rb:18:in `block (2 levels) in init_with_program'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `block in execute'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `each'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `execute'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/mercenary-0.4.0/lib/mercenary/program.rb:44:in `go'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/mercenary-0.4.0/lib/mercenary.rb:21:in `program'
	from /opt/build/cache/bundle/ruby/2.5.0/gems/jekyll-4.2.0/exe/jekyll:15:in `&amp;lt;top (required)&amp;gt;'
	from /opt/build/cache/bundle/ruby/2.5.0/bin/jekyll:23:in `load'
	from /opt/build/cache/bundle/ruby/2.5.0/bin/jekyll:23:in `&amp;lt;main&amp;gt;'
	from /opt/build/cache/bundle/ruby/2.5.0/bin/ruby_executable_hooks:22:in `eval'
	from /opt/build/cache/bundle/ruby/2.5.0/bin/ruby_executable_hooks:22:in `&amp;lt;main&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nailed it - a gem I hardly use is missing an environment variable. I’m just going to delete it for now, and re-try.&lt;/p&gt;

&lt;p&gt;[…]&lt;/p&gt;

&lt;p&gt;Not worth trying to clean out the gem, I did &lt;code class="language-plaintext highlighter-rouge"&gt;git rebase -i HEAD~~&lt;/code&gt; to delete the last commit (deleting the &lt;code class="language-plaintext highlighter-rouge"&gt;crosspost-to-medium&lt;/code&gt; gem), and instead added the environment variables to Netlify.&lt;/p&gt;

&lt;p&gt;I just copied/pasted from my &lt;code class="language-plaintext highlighter-rouge"&gt;.evn&lt;/code&gt; (A hidden file, it’s not tracked in Git, but I copied/obfuscated the values into &lt;code class="language-plaintext highlighter-rouge"&gt;env.sample&lt;/code&gt; in &lt;a href="https://github.com/josh-works/josh-works.github.io/commit/0d7eeb369d41034ffb67a784cc25e199af236b70"&gt;this commit&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;Huzzah! The site builds. We’re good to go.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id="related-reading"&gt;Related reading:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://answers.netlify.com/t/support-guide-frequently-encountered-problems-during-builds/213"&gt;https://answers.netlify.com/t/support-guide-frequently-encountered-problems-during-builds/213&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://answers.netlify.com/t/site-deploy-failed-need-guidance-please/32668/7"&gt;https://answers.netlify.com/t/site-deploy-failed-need-guidance-please/32668/7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>Josh Thompson</author><pubDate>Sun, 26 Sep 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/migrating-to-netlify</guid></item><item><title>Why I'm blogging and using hugo to do it</title><link>https://jay.jvf.cc/posts/about-itself/</link><description>&lt;p&gt;It&amp;rsquo;s been a long time since I&amp;rsquo;ve posted regularly to the internet &amp;ndash; short or long form. As I get older, I find I don&amp;rsquo;t
enjoy publishing in Facebook or Twitter-sized snippets. So I decided to try and start blogging on a longer form again.&lt;/p&gt;
&lt;p&gt;My primary topic will be the various personal projects I&amp;rsquo;ve started working on in the last few years. None of this is
to be seen as a review or endorsement of the products or software I&amp;rsquo;ve used. Some of it was picked nearly at random.
Such as the static site generator I&amp;rsquo;m using for this, &lt;a href="https://gohugo.io" title="gohugo.io"&gt;hugo&lt;/a&gt;. I found it by simply
Googling for a static site generator in go, and picking the first response on Google. It&amp;rsquo;s been so easy I&amp;rsquo;ve never
thought of trying anything else.&lt;/p&gt;</description><author/><pubDate>Sun, 26 Sep 2021 12:33:52 GMT</pubDate><guid isPermaLink="true">https://jay.jvf.cc/posts/about-itself/</guid></item><item><title>What getting old really feels like</title><link>https://nicolaiarocci.com/what-getting-old-really-feels-like/</link><description>&lt;p&gt;In a new study published in Ageing and Society, researchers Sam Carr and Chao
Fang spent over 130 hours interviewing older people to understand what it&amp;rsquo;s
like to get old and cope with loneliness. The Conversation UK features their
report, appropriately titled &lt;a href="https://theconversation.com/loneliness-loss-and-regret-what-getting-old-really-feels-like-new-study-157731"&gt;Loneliness, loss and regret: what getting old really
feels like&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We found that ageing brings about a series of inevitable losses that deeply
challenge people’s sense of connection to the world around them. Loneliness
can often be oversimplified or reduced to how many friends a person has or
how often they see their loved ones.But a particular focus for us was to
better understand what underpins feelings of loneliness in older people on
a deeper level. Researchers have used the term “existential loneliness” to
describe this deeper sense of feeling “separated from the world” – as though
there is an insurmountable gap between oneself and the rest of society. Our
objective was to listen carefully to how people experienced and responded to
this.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 26 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/what-getting-old-really-feels-like/</guid></item><item><title>Easy Netlify Functions for Contact Messaging</title><link>https://www.tderflinger.com/en/easy-netlify-functions-for-contact-messaging</link><description>Netlify Functions are serverless code that are easy to deploy. Here I want to show how to program a simple Netlify Function to send a contact message from my website to an email address and log it into an Airtable sheet.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Sun, 26 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/easy-netlify-functions-for-contact-messaging</guid></item><item><title>When does software end? How do we get there? - 123dev #21</title><link>https://justingarrison.com/123dev/2021-09-26-123dev-21/</link><description>&lt;img alt="An animation of flying through doors created by folding paper" src="https://justingarrison.com/img/123dev/21-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="end-results"&gt;End results&lt;/h2&gt;
&lt;p&gt;When I think about &lt;em&gt;how&lt;/em&gt; I create software it feels very haphazard. Some days are better than others but I rarely make the progress I want. By the time I’m ready to share with someone I realize how much a week or a month of steady progress adds up.&lt;/p&gt;
&lt;p&gt;The gif was created by making folds and creases in paper and then using stop motion to put them together. Progress must have been slow but the end result is stunning.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 26 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-26-123dev-21/</guid></item><item><title>Networks are in for light-out factories</title><link>https://stop.zona-m.net/2021/09/networks-are-in-for-light-out-factories/</link><description>&lt;p&gt;Lights can go off, and so can responsibility.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 26 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/networks-are-in-for-light-out-factories/</guid></item><item><title>Solving Newcomb’s paradox for classical and quantum predictors</title><link>https://diffxweyl.wordpress.com/2021/09/25/solving-newcombs-paradox-for-classical-and-quantum-predictors/</link><description>A recent HN post reminded me of Newcomb&amp;#8217;s paradox, which goes as follows (from Wiki): There is a reliable predictor, another player, and two boxes designated A and B. The player is given a choice between taking only box B, or taking both boxes A and B. The player knows the following: Box A is &amp;#8230; &lt;a class="more-link" href="https://diffxweyl.wordpress.com/2021/09/25/solving-newcombs-paradox-for-classical-and-quantum-predictors/"&gt;Continue reading&lt;span class="screen-reader-text"&gt; "Solving Newcomb&amp;#8217;s paradox for classical and quantum&amp;#160;predictors"&lt;/span&gt;&lt;/a&gt;</description><author>diff × weyl</author><pubDate>Sat, 25 Sep 2021 20:44:46 GMT</pubDate><guid isPermaLink="true">https://diffxweyl.wordpress.com/2021/09/25/solving-newcombs-paradox-for-classical-and-quantum-predictors/</guid></item><item><title>Shawbury 10km 2021</title><link>https://caiustheory.com/shawbury-10km-2021/</link><description>&lt;p&gt;RAF Shawbury host an annual 10km running race, and I entered this time because a friend suggested it as an event to aim for over the summer. Given the run is along runways/taxiways it&amp;rsquo;s going to be one of the flattest courses on offer. Seemed like a good idea at the time.&lt;/p&gt;
&lt;p&gt;Got there with plenty of time to register, figure out where the important things—coffee &amp;amp; ice cream vans—were and go for a nervous wee. Decided to start a workout on my watch early, set it to 10(km, I thought) and paused it straight away to wait for the gun. Stood around for another ~5 minutes waiting for the gun to go. Unpaused the watch as I went across the line and we were off.&lt;/p&gt;
&lt;p&gt;Started near the front of the midfield and set out at a pace that I thought was around 10:20/mile, didn&amp;rsquo;t pay too much attention to people coming past me as it was a mass start rather than paced start. Kept pace with some absolute legend running in an Elephant costume (I&amp;rsquo;d overheard they were targeting a 56 minute finish, something I later realised to be a joke.) Easy enough route, round a taxiway and down the main runway to start.&lt;/p&gt;
&lt;p&gt;Mile in, feeling good, lots of energy. Watch buzzes the mile pace … 10:55/mile. Yeesh, that&amp;rsquo;s way slower than I meant to start, and the rolling mile display hadn&amp;rsquo;t displayed anything until I&amp;rsquo;d hit the first mile mark. Oops, time to turn the pace up slightly. Kept going until the rolling mile showed under 10:00/mile, second mile came in at 9:27/mile and then tried to keep it under 10:00/mile whilst not blowing the heart rate up too much.&lt;/p&gt;
&lt;p&gt;Marshals around the course were lovely and supportive, although compared to a road 10km through town where people come out of their houses and cheer it felt quite quiet as an event. Seeing someone every time a corner occurred was a welcome sight though, and the water stop was quite well placed just before the halfway mark.&lt;/p&gt;
&lt;p&gt;Hit 3.1 miles in around 31-32 minutes which was on pace, legs were feeling okay and I took some water on board whilst running. Second half of the course I started using other people to pace myself, I&amp;rsquo;d pick someone 50-100 meters ahead who didn&amp;rsquo;t appear to be vanishing from me and then try and slowly overhaul them without increasing the heart rate too much.&lt;/p&gt;
&lt;p&gt;Around the 5 mile mark (~50 minutes) I wondered if I could be on for an hour finish. Also realised as the watch buzzed &amp;ldquo;Halfway there!&amp;rdquo; that it was set to a 10 mile run. Useful. Heart rate was creeping up to maximum, legs were starting to ache and the ball of my left foot was starting to complain about repeatedly slamming into the tarmac. Tried elongating my stride and springing off each step a bit more consciously, also hit the point of talking to myself which is a good sign I&amp;rsquo;m starting to tire and need to start concentrating on what I&amp;rsquo;m doing form-wise.&lt;/p&gt;
&lt;p&gt;Saw the 1km board, then the 500m board and was looking for a 250m board to kick harder from. Sailed past a friend who had already finished about the 200m mark and was really trying not to blow the heart rate through the roof. Found a 150m board—complete with amusingly sarcastic comment along the lines of &amp;ldquo;ooh, time to put some effort in&amp;rdquo;—and ramped the pace up slightly. Mindful of keeping the heart rate under control still, pushed a little too high according to my stats afterwards but felt okay doing so for a few seconds.&lt;/p&gt;
&lt;p&gt;Crossed the line with a gun time of 1:00:51. Super chuffed I nearly dipped under the 1 hour mark, expected to be around 1:04 having done a 1:06:30 cross-country 10km earlier in the summer. I &lt;strong&gt;really&lt;/strong&gt; must remember not to mess around with my equipment before events though, only make changes before going for training runs because something is &lt;em&gt;always&lt;/em&gt; setup wrong.&lt;/p&gt;
&lt;p&gt;In this case I&amp;rsquo;d switched from kilometres to miles for running events, reset my watch yesterday so it had no music/podcasts on to listen to and also changed the display for a running workout, crucially removing the average pace of the whole activity. What I hadn&amp;rsquo;t foreseen making those changes was I&amp;rsquo;d have to wait a mile before I got &lt;em&gt;any&lt;/em&gt; pace feedback from the watch.&lt;/p&gt;
&lt;p&gt;I also discovered uploading straight to &lt;a href="https://www.strava.com/athletes/2295429"&gt;Strava&lt;/a&gt; (through &lt;a href="https://www.rungap.com"&gt;Rungap&lt;/a&gt;) that pausing the watch ahead of time completely screws up the timings. Strava reckoned I&amp;rsquo;d done an 18:25/mile first mile! I knew it was slow, but it wasn&amp;rsquo;t &lt;em&gt;that&lt;/em&gt; slow. Ended up exporting a GPX and removing the first 6 minutes stood around with it paused to make Strava happy about the timings.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.strava.com/activities/6016842101"&gt;Race on Strava&lt;/a&gt;&lt;/p&gt;</description><author>Caius Theory</author><pubDate>Sat, 25 Sep 2021 19:05:00 GMT</pubDate><guid isPermaLink="true">https://caiustheory.com/shawbury-10km-2021/</guid></item><item><title>Typing Tuples in Python</title><link>https://bfontaine.net/2021/09/25/typing-tuples-in-python/</link><description>&lt;p&gt;Python added support for type hints in 3.5. These are not typing as you may be used to in other languages
since they have no effect on the compilation to bytecode nor at runtime, but rather hints for the
(tools of the) developer.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python3"&gt;def print_int(n: int):
    print(n)

print_int(1)
print_int("foo")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The code above doesn’t fail when you call &lt;code class="language-plaintext highlighter-rouge"&gt;print_int("foo")&lt;/code&gt; even though &lt;code class="language-plaintext highlighter-rouge"&gt;n&lt;/code&gt; is “typed” as an &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt;. This is because
this &lt;code class="language-plaintext highlighter-rouge"&gt;n: int&lt;/code&gt; is just a hint.&lt;/p&gt;

&lt;p&gt;While you can check for type issues by running &lt;a href="http://mypy-lang.org/"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;mypy&lt;/code&gt;&lt;/a&gt; by hand, type hints become really
powerful when your editor/IDE supports them.&lt;/p&gt;

&lt;p&gt;Types for collections can specify the inner types: a list (&lt;code class="language-plaintext highlighter-rouge"&gt;List&lt;/code&gt;) that contains strings (&lt;code class="language-plaintext highlighter-rouge"&gt;str&lt;/code&gt;) would be &lt;code class="language-plaintext highlighter-rouge"&gt;List[str]&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python3"&gt;from typing import List

stuff: list = []  # a list of anything (equivalent to List[Any])
stuff.append(1)
stuff.append("foo")

offsets: List[int] = []  # a list of ints
offsets.append(1)
offsets.append("foo")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the snippet above, the last line is highlighted as an error in any good IDE, and &lt;code class="language-plaintext highlighter-rouge"&gt;mypy&lt;/code&gt; would complain about it.&lt;/p&gt;

&lt;p&gt;Other container types exist as well, and they can be nested:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python3"&gt;from typing import List, Dict, Iterable

# a dictionary where keys are str and values are List[str], i.e. lists of strings
friends: Dict[str, List[str]] = {}
friends["Alice"] = ["Sam", "Maria"]

# a function that takes an iterable of ints. It can be a list, a tuple, a generator, a set, etc
def average(s: Iterable[int]):
    total = 0
    count = 0
    for element in s:
        total += element
        count += 1

    return total / count
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Given &lt;code class="language-plaintext highlighter-rouge"&gt;List[x]&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;Collection[x]&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;Sequence[x]&lt;/code&gt; and other &lt;code class="language-plaintext highlighter-rouge"&gt;Set[x]&lt;/code&gt;, one would expect &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[x]&lt;/code&gt; to be a hint for a tuple
that contains &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt;. Well… no.&lt;/p&gt;

&lt;p&gt;This is confusing at first, but &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[str]&lt;/code&gt; types a tuple of &lt;u&gt;a&amp;nbsp;single&amp;nbsp;element&lt;/u&gt; of type &lt;code class="language-plaintext highlighter-rouge"&gt;str&lt;/code&gt;. To add
more elements, you need to type them as well: a pair of ints would be &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[int, int]&lt;/code&gt;, while a triplet of a string,
a float and a boolean would be &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[str, int, bool]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While tuples can be used as sequences (e.g. for immutable/hashable equivalents to lists), I’d argue that their primary
use is for fixed-length representations, such as pairs of results:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python3"&gt;def match_object(data: bytes):
    # example code
    distances = [0.99, 0.97, 0.96]
    indices = [432, 12, 3923]
    return distances, indices
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this snippet, &lt;code class="language-plaintext highlighter-rouge"&gt;match_object&lt;/code&gt; returns a tuple of a list of floats and a list of integers
(aka &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[List[float], List[int]]&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you still want to type arbitrary-length homogeneous tuples, there’s a syntax for that: &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[int, ...]&lt;/code&gt;
types a tuple of any length, including 0, that contains only &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt; elements (and yes, &lt;code class="language-plaintext highlighter-rouge"&gt;...&lt;/code&gt; is valid in Python).&lt;/p&gt;

&lt;p&gt;For this and other interrogations (how to type a generator?), Mypy has a useful &lt;a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html"&gt;type hints cheat sheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: if you know the size of the tuple, use &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[x]&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[x, x]&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[x, x, x]&lt;/code&gt;, etc. If you don’t,
use &lt;code class="language-plaintext highlighter-rouge"&gt;Tuple[x, ...]&lt;/code&gt;, but all elements must be of type &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt;.&lt;/p&gt;</description><author>Baptiste Fontaine’s Blog</author><pubDate>Sat, 25 Sep 2021 10:51:00 GMT</pubDate><guid isPermaLink="true">https://bfontaine.net/2021/09/25/typing-tuples-in-python/</guid></item><item><title>Tolmie Peak</title><link>https://sam.hooke.me/trip/2021/09/tolmie-peak/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Sat, 25 Sep 2021 06:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/09/tolmie-peak/</guid></item><item><title>Implementing reverse translation</title><link>https://viralinstruction.com/posts/codonset/index.html</link><description>A walkthrough of a small, very satisfying Julia implementation of a cute biological problem.</description><author>viralinstruction</author><pubDate>Sat, 25 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://viralinstruction.com/posts/codonset/index.html</guid></item><item><title>Testing a compiler that can’t even print stuff out</title><link>http://blog.danieljanus.pl/testing-lithium/</link><description>&lt;div&gt;&lt;p&gt;I’m enjoying a week-long vacation. In addition to other vacationy things (a trip to Prague, yay!), I wanted to do some off-work programming Just For Fun™ and revisit one of my dormant pet projects, to see if I can make some progress.&lt;/p&gt;&lt;p&gt;I opted for Lithium, my toy x86 assembler and Lisp compiler that hasn’t seen new development since 2014. But before that, I had &lt;a href="/2012/05/14/lithium/"&gt;blogged&lt;/a&gt; &lt;a href="/2013/05/26/lithium-revisited/"&gt;about it&lt;/a&gt; and even &lt;a href="https://danieljanus.pl/talks/reveal.js/2013-euroclojure.html#/"&gt;talked about it&lt;/a&gt; at EuroClojure one time.&lt;/p&gt;&lt;p&gt;Over the week, I’ve re-read the &lt;a href="http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf"&gt;paper&lt;/a&gt; that I’ve been loosely following while developing Lithium. In it, Abdulaziz Ghuloum advocates to have a testing infrastructure from day one, so that one can ensure that the compiler continues to work after each small modification. I’d cut corners on it before, but today, I’ve finally added one.&lt;/p&gt;&lt;p&gt;What’s the big deal? And why not earlier?&lt;/p&gt;&lt;p&gt;One of the original goals that I set myself for Lithium is that it have no runtime dependencies. Not even a C library; not even an OS. It produces raw x86 binaries targetting real mode – non-relocatable blobs of raw machine code. I’m running them in DOSBox, because it’s convenient, but the point is it’s not necessary.&lt;/p&gt;&lt;p&gt;(Some day, I’ll write a mission statement to explain why. But that’s a story for another day.)&lt;/p&gt;&lt;p&gt;And because the setup is so minimalistic, the setup suggested by Ghuloum becomes unfeasible. Ghuloum presupposes the existence of a host C compiler and linker; I have no such privilege. By itself, Lithium can barely output stuff to screen. There’s a &lt;code&gt;write-char&lt;/code&gt; primitive that emits one character, but nothing more than that. And there’s as yet no library to add things to, because there’s no &lt;code&gt;defn&lt;/code&gt; and not much of a global environment.&lt;/p&gt;&lt;p&gt;So what to do? I thought about the invariant in Ghuloum’s design, one that Lithium inherits as well:&lt;/p&gt;&lt;p&gt;&lt;em&gt;Every expression is compiled to machine code that puts its value in the &lt;code&gt;AX&lt;/code&gt; register.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;If I could somehow obtain the values that the CPU registers have at the end of executing a Lithium-compiled program, then I could compare them to the expected value in a test. But how to grab those registers?&lt;/p&gt;&lt;p&gt;That turned out to be easier than expected. Instead of extending Lithium to support printing decimal or hexadecimal numbers, I just grabbed &lt;a href="http://www.fysnet.net/yourhelp.htm"&gt;some pre-existing assembly code&lt;/a&gt; to affix to the program as an epilog. (It does depend on DOS’s interrupt &lt;code&gt;21h&lt;/code&gt;, but hey, it doesn’t hurt to have it for debugging/testing only.) Surprise: the snippet failed to compile, because Lithium’s assembler is woefully incomplete! But it was easy enough to extend it until it worked.&lt;/p&gt;&lt;p&gt;So this gave me a way to view the program’s results.&lt;/p&gt;&lt;img src="/img/blog/lithium-testing.png" /&gt;
&lt;p&gt;But there’s another problem: these results are printed within DOSBox. In the emulated DOS machine. I needed a way to transfer them back to the host. Can you guess how?&lt;/p&gt;&lt;p&gt;Yes, you’re right: the simplest thing (DOS redirection to a file, as in &lt;code&gt;PROG.COM &gt;REG.TXT&lt;/code&gt;) works. And you’ll laugh at me that it hasn’t occurred to me until now, when I’m writing up the &lt;a href="https://github.com/nathell/lithium/commit/27563b3c5b92f32b24f750d98248d013f924a700"&gt;commit&lt;/a&gt; that’s already out in the wild. Another proof that it pays to write documentation.&lt;/p&gt;&lt;p&gt;My original idea was… SCREEN CAPTURE!&lt;/p&gt;&lt;p&gt;I’ve scavenged Google for a DOS screen grabber that can produce text files and is not a TSR, &lt;a href="http://www.pc-tools.net/dos/dosutils/"&gt;found one&lt;/a&gt;, bundled it with Lithium, and wrote &lt;a href="https://github.com/nathell/lithium/blob/27563b3c5b92f32b24f750d98248d013f924a700/src/lithium/driver.clj#L23-L36"&gt;some duct-tape code&lt;/a&gt; that invokes the compiled program and the screen grabber in turn and then parses the output. With that, I can finally have &lt;a href="https://github.com/nathell/lithium/blob/27563b3c5b92f32b24f750d98248d013f924a700/test/lithium/compiler_test.clj"&gt;tests&lt;/a&gt; that check whether &lt;code&gt;(+ 3 4)&lt;/code&gt; is really &lt;code&gt;7&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;And now let me go refactor it…&lt;/p&gt;&lt;/div&gt;</description><author>code · words · emotions: Daniel Janus’s blog</author><pubDate>Sat, 25 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://blog.danieljanus.pl/testing-lithium/</guid></item><item><title>How to redirect PreStop hook output to pod logs</title><link>https://shyamjos.com/redirect-prestop-hook-logs/</link><description>&lt;p&gt;You might have noticed that command output of PreStop hook won&amp;rsquo;t appear in pod logs and only thing you can see is whether the PreStop hook command failed or not is by checking kubernetes events log.&lt;/p&gt;
&lt;p&gt;So how can we see the PreStop hook command output in pod logs?&lt;/p&gt;
&lt;p&gt;By default container engine only redirects the stdout/err of the main process (running with &lt;code&gt;PID 1&lt;/code&gt;) to the logging driver. Since the PreStop hook runs as a separate process the output from this command won&amp;rsquo;t appear in container logs.&lt;/p&gt;
&lt;h2 class="relative group" id="how-to-redirect-prestop-hook-command-output-to-container-logs-"&gt;How to redirect PreStop hook command output to container logs ? &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#how-to-redirect-prestop-hook-command-output-to-container-logs-"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;In Linux you can easily redirect stdout/err of one process to another by writing to the other process&amp;rsquo;s stdout/err file descriptor (/proc/PID/fd/1 or /proc/PID/fd/2). In our case we can redirect the stdout of PreStop hook command to our main process&amp;rsquo;s stdout using the command &lt;code&gt;echo &amp;quot;Hello from the other side&amp;quot; &amp;gt; /proc/1/fd/1&lt;/code&gt; and this message will be appear in the container logs.&lt;/p&gt;
&lt;h2 class="relative group" id="example-prestop-hook-command-with-output-redirection"&gt;Example PreStop hook command with output redirection &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#example-prestop-hook-command-with-output-redirection"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; lifecycle:
 preStop :
 exec:
 command: ["bash", "uptime &amp;gt; /proc/1/fd/1"] 
&lt;/code&gt;&lt;/pre&gt;</description><author>Shyam Jos</author><pubDate>Sat, 25 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://shyamjos.com/redirect-prestop-hook-logs/</guid></item><item><title>What does complexity look like? 🕰️ - 123dev #20</title><link>https://justingarrison.com/123dev/2021-09-25-123dev-20/</link><description>&lt;img alt="A mechanical clock draws the time" src="https://justingarrison.com/img/123dev/20-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="self-made-complexity"&gt;Self made complexity&lt;/h2&gt;
&lt;p&gt;Everyone is familiar with their own complexity. I’m sure this clock makes sense to the person who built it. When you’re the one who creates it it doesn’t seem complex but perfectly logical.&lt;/p&gt;
&lt;p&gt;Complexity shows up in our daily lives, in code, in systems, and especially organizations. Fighting complexity isn’t always the right solution, but finding a complexity that can be reasoned about and understood by others is often the best solution.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sat, 25 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-25-123dev-20/</guid></item><item><title>Is a school backpack app bad, or good?</title><link>https://stop.zona-m.net/2021/09/is-a-school-backpack-app-bad-or-good/</link><description>&lt;p&gt;It&amp;rsquo;s obvious, but not SO obvious as it may seem..&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 25 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/is-a-school-backpack-app-bad-or-good/</guid></item><item><title>What's the entropy of a fair coin toss?</title><link>https://bytepawn.com/what-is-the-entropy-of-a-fair-coin-toss.html</link><description>&lt;p&gt;What's the entropy of a fair coin toss? What if the coin almost always returns Heads? My recruiter reports that very few candidates can answer these entropy related DS screening questions. &lt;br /&gt;&lt;br /&gt;&lt;img alt="Entropy" src="/images/entropy3.png" style="width: 300px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 25 Sep 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/what-is-the-entropy-of-a-fair-coin-toss.html</guid></item><item><title>Who is software even for?</title><link>https://utf9k.net/blog/who-is-software-even-for/</link><description>Maybe it's for you? Maybe it's for me? Maybe it isn't for any of us!</description><author>utf9k</author><pubDate>Fri, 24 Sep 2021 11:00:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/who-is-software-even-for/</guid></item><item><title>Keycloak password hashing</title><link>https://b.agilob.net/programming/java/benchmarks/keycloak-password-hashing/</link><description>This post doesn&amp;rsquo;t contain full context of the works performed, only benchmarking part
I had to test how number of iterations impacts login request time to KeyCloak and if or how we can improve it. After investigating a few other options I decided to check what&amp;rsquo;s the difference for password hashing times using default hashing mechanism in KeyCloak. I found and extracted parts of password hashing mechanism from KeyCloak to my repo, developed small parametrised JMH benchmark, comparing:</description><author>Random Notes by agilob</author><pubDate>Fri, 24 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/programming/java/benchmarks/keycloak-password-hashing/</guid></item><item><title>Kubernetes EU 2021 edition - 123dev #19</title><link>https://justingarrison.com/123dev/2021-09-24-123dev-19/</link><description>&lt;img alt="A movie scene where the camera pans over a series of tables" src="https://justingarrison.com/img/123dev/19-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="wings-1927"&gt;Wings (1927)&lt;/h2&gt;
&lt;p&gt;The gif is from a silent film I’ve never seen but it won the first ever Academy Award (1930). Ninety years later I would have a hard time reproducing that shot, and I love the vision someone had to make it happen. It reminded me of one of Clark’s laws; I’ll list all three for you here:&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 24 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-24-123dev-19/</guid></item><item><title>Enough of batteries, already!</title><link>https://stop.zona-m.net/2021/09/enough-of-batteries-already/</link><description>&lt;p&gt;But is it bad, or good?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 24 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/enough-of-batteries-already/</guid></item><item><title>Wiki-Loader: Loading Wikipedia into Elasticsearch</title><link>https://joshuarogers.net/articles/2021-09/wiki-loader-loading-wikipedia-into-elasticsearch/</link><description>Over the last two posts I explored the process of extracting a very large dataset (an XML export of Wikipedia) into various types of databases (an RDBMS1 and a Graph database) that I can run locally as a precaution against being marooned in the past should some portal, machine, or ancient artifact that send me careening backwards in history. While these two databases have satisfied my need to quickly look up information and find relational paths between said pieces of information, they still leave me needing to know exactly what I'm looking for.</description><author>Joshua Rogers</author><pubDate>Thu, 23 Sep 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://joshuarogers.net/articles/2021-09/wiki-loader-loading-wikipedia-into-elasticsearch/</guid></item><item><title>You Can't Eat Relative Growth</title><link>https://whoisnnamdi.com/relative-growth/</link><description>In startup land, we talk way too much about relative growth. We'd do better to ground our thinking in absolute growth.</description><author>Who is Nnamdi?</author><pubDate>Thu, 23 Sep 2021 12:07:29 GMT</pubDate><guid isPermaLink="true">https://whoisnnamdi.com/relative-growth/</guid></item><item><title>ASP.NET 6 Migration Cheatsheet and FAQ</title><link>https://nicolaiarocci.com/asp.net-6-migration-cheatsheet-and-faq/</link><description>&lt;p&gt;David Fowler has a very informative gist up on GitHub. It&amp;rsquo;s titled [Migration
to ASP.NET Core. NET6][3] and it&amp;rsquo;s filled with details, recipes and FAQs on
migrating an ASP.NET Core 5 web app to ASP.NET Core 6&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. The focus is on the
new, streamlined hosting model, also known as Minimal APIs&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;. To be clear,
You don&amp;rsquo;t &lt;em&gt;have&lt;/em&gt; to move to the new model. As the FAQ section emphasizes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do I have to migrate to the new hosting model&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 23 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/asp.net-6-migration-cheatsheet-and-faq/</guid></item><item><title>A new project-oriented Haskell book</title><link>https://gilmi.me/post/2021/09/23/lhbg</link><description>Announcing a new project-oriented, online, and free Haskell book titled 'Learn Haskell by building a blog generator'</description><author>λm.me</author><pubDate>Thu, 23 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://gilmi.me/post/2021/09/23/lhbg</guid></item><item><title>The feeling the first time your code works - 123dev #18</title><link>https://justingarrison.com/123dev/2021-09-23-123dev-18/</link><description>&lt;img alt="A baby is surprised by flipping a water bottle" src="https://justingarrison.com/img/123dev/18-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="when-code-works"&gt;When code works&lt;/h2&gt;
&lt;p&gt;Sometimes I’m equally surprised as this baby when my code works. Especially when I either do it on the first try or I fight with it for days and finally figure it out. It’s exciting and then I try to figure out why it works.&lt;/p&gt;
&lt;h2 id="know-where-to-look-for-a-problem"&gt;Know where to look for a problem&lt;/h2&gt;
&lt;p&gt;I struggled with something for the new 123dev website for a few nights this past week. I thought the problem was because of my lack of CSS skills and I spent most of my time trying to figure it out. It turned out to be completely unrelated to CSS—full write up in next weeks post—and reminded me of the importance of finding the simplest reproducible issue before making assumptions.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 23 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-23-123dev-18/</guid></item><item><title>Building a Website With Haskell &amp;amp; Nix</title><link>https://fbrs.io/fp/</link><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I built a website so trivial that you might wonder why I even bother writing
about it. It consists of a static site which is, unfortunately, still built
with Gatsby, a Javascript framework. The content comes from a content
management system called Contentful. The most complex part is the members-only
area, which is the part I rebuilt and what this post is all about.&lt;/p&gt;
&lt;p&gt;The members area consists of a handful of routes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A news feed where administrators can post new messages. It’s plain text with the exception of URLs, which are replaced with proper anchor links. No images.&lt;/li&gt;
&lt;li&gt;User management area, where administrators can invite new members or edit and delete existing members. Here you can also filter by user groups and send emails to users you select through checkboxes. That’s the part where I used progressive enhancement. This route also hooks into email sending functionality for messaging someone who was invited to the site.&lt;/li&gt;
&lt;li&gt;People can create events, and others can then sign up for these events and also indicate how many guests they bring. Everyone can see who’s coming and who declined, how many guests there are in total. Events can also have attachements, such as PDF files, which are stored on the droplet itself.&lt;/li&gt;
&lt;li&gt;People can request a link that let’s them change their password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first version was hacked together with Firebase and some serverless functions,
because I didn’t want to maintain a proper backend. The UI was awkwardly fit
into the static site. It worked but it was ugly.&lt;/p&gt;
&lt;p&gt;And so one day I decided that it was time for a rewrite! The official reason is
that I considered it too risky to depend on Firebase for everything. Also the
local developer experience was a bit lacking at that time. The honest answer is
that, for quite a long time, I had been looking for a real-world project to
which I could apply all the fancy functional programming (FP) technologies I
picked up over the years. I was holding an FP-shaped hammer and I was just
looking for a suitable nail. This project seemed perfect, since it’s small,
has actual users and even a deadline, but since it’s a project for a family
member there’s no money on the line and I could do whatever I wanted in terms
of tech. And so the idea was born to rewrite the entire website with Haskell,
Nix and PureScript.&lt;/p&gt;
&lt;p&gt;In this post I want to share my experiences building this simple backend in
Haskell, Nix and PureScript. You don’t need to know any of these technologies
to follow the blog post. You might not understand every little detail, but I
hope that the overall message is still valuable. I’ll start the post with a
short summary, so you don’t have to read the entire article if you just want
the main conclusions.&lt;/p&gt;
&lt;p&gt;Here’s the &lt;a href="https://github.com/cideM/lions-backend"&gt;source code&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;h3 id="haskell"&gt;Haskell&lt;/h3&gt;
&lt;p&gt;I’m neutral about using Haskell again in the future.&lt;/p&gt;
&lt;p&gt;On the one hand, algebraic data types and an excellent type system make it very
easy to translate my thoughts into code, since branching control flow, based on
a finite number of options, seems to make up the majority of my coding. It’s
kind of liberating to make sweeping changes across the code base, knowing that
the compiler will prevent a lot of mistakes. Haskell also has a surprisingly
large selection of web development frameworks and libraries. I didn’t have to
implement any mission critical functionality myself (encryption, database
communication, routing, and so on).&lt;/p&gt;
&lt;p&gt;On the other hand, one of the biggest time sinks was mere plumbing. Many
Haskell libraries revolve around specific Monads, created just for that
library. You don’t need to know what Monads are for this blog post, but the
gist is that you often need to write a little bit of glue code, so that
different Monads of different libraries can talk to each other. I’ll write more
about this later. But the end result was I spent way more time on glue code
than I thought. To make things worse, this glue code did not feel like
meaningful progress. It’s a Haskell solution for a Haskell problem after all.&lt;/p&gt;
&lt;p&gt;Haskell also has its fair share of historical baggage, which includes, but is
not limited to, an annoying module system, complicated tooling, and a rather
anemic standard library. The last point means you’ll have way too many lines of
code dedicated to imports.&lt;/p&gt;
&lt;h3 id="nix"&gt;Nix&lt;/h3&gt;
&lt;p&gt;I would 100% use Nix again to provide a basic developer environment, which
includes compilers, formatters, database libraries, and so on. It is, in my
opinion, best in class in this area and entirely unrivalled. Think &lt;code&gt;nix develop&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Whether or not I’d use it to build the actual project depends on the
programming language and how well supported it is in Nix. Haskell is the Nix
posterchild, Node on the other hand can be hit and miss.&lt;/p&gt;
&lt;p&gt;I would not use Nix again to deploy my code. It was fun playing around with
Systemd and SOPS, but the container ecosystem is just too big. Luckily you can
generate Docker containers with nix, which is something I’d like to explore in
the future. Running my NixOS image locally through QEMU was and still is,
frustrating, especially on MacOS. Additionally, my GitHub action workflow that
builds the application, runs end-to-end tests and deploys it, takes 25 minutes.
For a website of this size that is insane.&lt;/p&gt;
&lt;h3 id="sqlite"&gt;SQLite&lt;/h3&gt;
&lt;p&gt;I love SQLite. It has quirks and historical baggage but at the end of the day
it’s a robust, battle tested and simple tool that I find to be a joy to use.
You can easily spin up an in-memory database for unit tests, it comes with a
surprising number of features, such as JSON tooling, and it’s just a file at
the end of day. This kind of simplicity is refreshing. Also shoutout to
&lt;a href="https://litestream.io/"&gt;Litestream&lt;/a&gt; for database backups.&lt;/p&gt;
&lt;p&gt;10/10 would use again.&lt;/p&gt;
&lt;h3 id="purescript"&gt;PureScript&lt;/h3&gt;
&lt;p&gt;I’ve written 140 lines of PureScript (PS) code in this project, which is
nothing. It’s used for progressive enhancement of markup rendered on the
server, which means inserting DOM elements and interactivity into existing DOM
nodes. Unfortunately I couldn’t find any good PS libraries for this, so I had
to resort to writing very verbose and tedious code that looks like a one-to-one
translation from Javascript. All of the PS web frameworks and libraries I saw
want to own the markup they render, like React. So instead of surgically
injecting interactivity into existing DOM elements, you hand control over an
entire subtree of the DOM to PS. But that would have meant duplicating the
rendering (first render with Haskell on the server then with PS on the client)
of some routes in PS, which seemed like too much work.&lt;/p&gt;
&lt;p&gt;For this project PS was not a good choice, as it meant additional complexity
and an unnecessarily large bundle for little to no gain. I like the language
though. As such I just can’t say if I would use PS again in the future based on
this project alone.&lt;/p&gt;
&lt;h2 id="deep-dive"&gt;Deep Dive&lt;/h2&gt;
&lt;h3 id="haskell-1"&gt;Haskell&lt;/h3&gt;
&lt;p&gt;Haskell the language is really not complicated. But the ecosystem can be. I
frequently found myself spending too much time solving problems that only exist
in Haskell. These problems are usually the result of having to combine custom
Monads (which you can think of as small, domain specific languages) from
different libraries, something I alluded to already in the summary. For
example, I struggled &lt;a href="https://www.reddit.com/r/haskell/comments/jyzc3w/how_to_avoid_infinite_type_when_lifting/"&gt;quite a
bit&lt;/a&gt;
with logging, which has only ever happened once before, in Clojure, where
logging is &lt;a href="https://lambdaisland.com/blog/2020-06-12-logging-in-clojure-making-sense-of-the-mess"&gt;the final
frontier&lt;/a&gt;.
But let me explain in a bit more detail why logging can be surprisingly tricky.&lt;/p&gt;
&lt;p&gt;Here’s a snippet from the README of
&lt;a href="https://hackage.haskell.org/package/scotty"&gt;scotty&lt;/a&gt;, a well known web
framework:&lt;/p&gt;
&lt;pre class="language-haskell "&gt;&lt;code class="language-haskell"&gt;main = scotty 3000 $
  get &amp;quot;&amp;#x2f;:word&amp;quot; $ do
    beam &amp;lt;- param &amp;quot;word&amp;quot;
    html $ mconcat [&amp;quot;&amp;lt;h1&amp;gt;Scotty, &amp;quot;, beam, &amp;quot; me up!&amp;lt;&amp;#x2f;h1&amp;gt;&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It doesn’t matter if you know Haskell or not. I’d like to direct your attention
at &lt;code&gt;param "word"&lt;/code&gt;, or, applying the function &lt;code&gt;param&lt;/code&gt; to the string &lt;code&gt;"word"&lt;/code&gt;.
Maybe you can guess that this extracts the value of the route parameter we
defined in the preceding line, with &lt;code&gt;"/:word"&lt;/code&gt;. But isn’t it weird that we’re
not passing the HTTP request to the &lt;code&gt;param&lt;/code&gt; function? Where does it get
the &lt;em&gt;request&lt;/em&gt; paramter from, then? The answer is too complicated for this blog
post, but suffice it to say that there’s some magic going on behind the scenes.
And this magic is made possible by the Scotty Monad.&lt;/p&gt;
&lt;p&gt;Now, what if you want to also do some logging in your HTTP handlers? There’s a
really nice library called
&lt;a href="https://hackage.haskell.org/package/katip-0.8.5.0"&gt;katip&lt;/a&gt;, and it also has
this very neat but also weird looking code:&lt;/p&gt;
&lt;pre class="language-haskell "&gt;&lt;code class="language-haskell"&gt;main :: IO ()
main = do
  -- ellided for brevity
  katipAddNamespace &amp;quot;additional_namespace&amp;quot; $ katipAddContext (sl &amp;quot;some_context&amp;quot; True) $ do
    $(logTM) WarningS &amp;quot;Now we&amp;#x27;re getting fancy&amp;quot;
  katipNoLogging $ do
    $(logTM) DebugS &amp;quot;You will never see this!&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;katipAddNamespace&lt;/code&gt; function does the same thing as any other structured,
hierarchical logging framework. Any log expressions in the indentend block of
code on the following lines will have this new namespace added to them. This
way the caller can add some additional context to the logger and the callee
doesn’t need to know or care about these things. As a proponent of structured,
hierarchical logging, I find this super neat. But, unlike in Go, where you’d do
something like&lt;/p&gt;
&lt;pre class="language-go "&gt;&lt;code class="language-go"&gt;newLogger := oldLogger.With(&amp;quot;key&amp;quot;, &amp;quot;value&amp;quot;)
newLogger.Info(&amp;quot;whatever&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the &lt;code&gt;newLogger&lt;/code&gt; variable is nowhere to be seen. You might at first glance think
that we’re passing an anonymous function to &lt;code&gt;katipAddNamespace&lt;/code&gt; (the &lt;code&gt;$ do&lt;/code&gt;
part maybe?), but even if that were the case, we’re clearly not accepting any
arguments in that anonymous function. So what happens to the modified logger?
Well, the answer is, it’s complicated. Just like with Scotty, there’s a custom Monad
that takes care of all this plumbing for us behind the scenes.&lt;/p&gt;
&lt;p&gt;Of course the logger and its contexts and namespaces need to live somewhere. In
a typical setup you create a record that holds your application environment.
And in that environment you store the logger. You then need to teach Katip how
it can access and modify the logger in that environment. It’s essentially like
implementing an interface for a struct, if you’re a Go person.&lt;/p&gt;
&lt;p&gt;But then you also need to teach Scotty about this custom Monad. Instead of
working with
&lt;a href="https://hackage.haskell.org/package/scotty-0.12/docs/Web-Scotty.html"&gt;&lt;code&gt;Web.Scotty&lt;/code&gt;&lt;/a&gt;
you import
&lt;a href="https://hackage.haskell.org/package/scotty-0.12/docs/Web-Scotty-Trans.html"&gt;&lt;code&gt;Web.Scotty.Trans&lt;/code&gt;&lt;/a&gt;,
which states in its opening paragraph:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The functions in this module allow an arbitrary monad to be embedded in
Scotty’s monad transformer stack in order that Scotty be combined with other
DSLs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is exactly what I wanted to do. And at this point the whole house of
cards may or may not fall apart. Because custom Monads often come with
constraints. As in, if you want to run this custom Monad you need to make sure
that the context in which it runs supplies X, Y and Z. And then you start
wondering how those constraints will fit into the bigger picture of all the
other custom Monads you need to satisfy. For Scotty and Katip this ended up
being not too
&lt;a href="https://www.reddit.com/r/haskell/comments/jyzc3w/how_to_avoid_infinite_type_when_lifting/"&gt;crazy&lt;/a&gt;,
but for another library – &lt;code&gt;co-log&lt;/code&gt; – I was unable to achieve my goal at all.
If this whole paragraph sounds a bit hand-wavy, please check out &lt;a href="https://www.reddit.com/r/haskell/comments/krke1o/how_to_create_colog_instance_for_scotty/"&gt;this Reddit
question&lt;/a&gt;
&lt;a href="https://stackoverflow.com/questions/65599741/how-to-make-co-logs-withlog-work-with-scotty"&gt;and this StackOverflow
(SO)&lt;/a&gt;
post.&lt;/p&gt;
&lt;p&gt;So yes, Haskell can be concise, expressive and type safe. But getting there can
be a pain, at least in the beginning. I do believe that over time this stops
being a problem, as you get more and more familiar with the Haskell type system
and the ecosystem.&lt;/p&gt;
&lt;p&gt;Aside from Monads, there was at least one other recurring topic that made me
scratch my head, and that’s how to deal with control flow and early return.&lt;/p&gt;
&lt;p&gt;Here’s what 90% of my Go code looks like:&lt;/p&gt;
&lt;pre class="language-go "&gt;&lt;code class="language-go"&gt;someValue, err := someFunc()
if err != nil {
  return errors.WithMessagef(err, &amp;quot;something went wrong with thing %s&amp;quot;, id)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Countless people have complained about the repetitiveness and boilerplate, but
I actually don’t mind it at all. The pattern is always the same, so it’s very
easy for me to follow the control flow inside a function. Additionally, it’s
trivial to return as early as possible. In fact, returning early is something
most people probably don’t even give a second thought to, since it’s just so
easy in imperative languages with statements.&lt;/p&gt;
&lt;p&gt;What does the above look like in Haskell then?&lt;/p&gt;
&lt;pre class="language-haskell "&gt;&lt;code class="language-haskell"&gt;case someFunc of
  Left err -&amp;gt; Left ([i|something went wrong with thing #{s}: #{err}|])
  Right -&amp;gt; -- keep going
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I suspect that many seasoned Haskellers will take fault with the above snippet
though, because it’s needlessly verbose. And I kind of agree, and I’ve spent
way too much time converting to and from various control flow patterns in this
project. とにかく, anyway, what’s going on here? &lt;code&gt;someFunc&lt;/code&gt; doesn’t return a
normal value and an error, it also doesn’t throw exceptions, rather it returns
a &lt;code&gt;Result&lt;/code&gt; type (called &lt;code&gt;Either&lt;/code&gt;). We then pattern match on the two possible
variations of this result type. &lt;code&gt;Left&lt;/code&gt; if there’s an error, &lt;code&gt;Right&lt;/code&gt; if
everything’s fine. Think &lt;a href="https://doc.rust-lang.org/std/result/"&gt;Rust’s result&lt;/a&gt;
type.&lt;/p&gt;
&lt;p&gt;But what if you have several function calls that all return different kinds of
results? Consider the following scenario:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unmarshal query parameters&lt;/li&gt;
&lt;li&gt;Check if user is authorized&lt;/li&gt;
&lt;li&gt;Get entity from database&lt;/li&gt;
&lt;li&gt;Perform some business logic with user and database entity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pretty much all of these things can fail in some expected way (meaning
exceptions wouldn’t be appropriate). Here’s a snippet from an older commit that
shows what such code could look like.&lt;/p&gt;
&lt;pre class="language-haskell "&gt;&lt;code class="language-haskell"&gt;getTokenByValue dbConn token &amp;gt;&amp;gt;= \case
  Nothing -&amp;gt; return $ Left $ TokenNotFound token
  Just tok@Token {..} -&amp;gt; do
    ok &amp;lt;- hasUser dbConn tokenUserId
    if not ok
      then (return . Left $ UserForTokenNotFound tokenUserId)
      else do
        now &amp;lt;- Time.getCurrentTime
        if now &amp;gt;= tokenExpires
          then return $ Left $ TokenExpired tok
          else do undefined -- ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see where I’m going with this, right? The increasing indentation level
looks ugly. In all fairness though, this isn’t any more verbose than the
equivalent Go or Typescript code. &lt;strong&gt;I really wish that I had just stuck with
the ugly and verbose version&lt;/strong&gt; instead of trying out various different
patterns. I believe that this code is simple, and readable and very easy to
understand. Refactoring this for vanity reasons is not something I’m proud of.&lt;/p&gt;
&lt;p&gt;There are &lt;a href="https://www.haskellforall.com/2021/05/the-trick-to-avoid-deeply-nested-error.html"&gt;some
tricks&lt;/a&gt;
for dealing with the ugliness, but in &lt;strong&gt;my personal experience&lt;/strong&gt; they often don’t work
in real world scenarios, where more complicated types are involved,
particularly anything with &lt;code&gt;IO&lt;/code&gt;. What does work is making liberal use of syntax
sugar, combinators and Monad transformers though.&lt;/p&gt;
&lt;p&gt;Here’s a snippet I copied verbatim from my code base, which shows what the
previous code looks like once you throw Monad transformers at the problem:&lt;/p&gt;
&lt;pre class="language-haskell "&gt;&lt;code class="language-haskell"&gt;token@Token {..} &amp;lt;- Token.get value &amp;gt;&amp;gt;= E.note&amp;#x27; (NotFound value)
ok &amp;lt;- User.exists tokenUserId
E.unless ok $ E.throwError (NoUser tokenUserId)
now &amp;lt;- liftIO $ Time.getCurrentTime
E.when (now &amp;gt;= tokenExpires) (E.throwError $ Expired token)
return $ Valid token
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is probably illegible to folks who are not familiar with Haskell, so here’s a hopefully human readable translation.&lt;/p&gt;
&lt;pre class="language-text "&gt;&lt;code class="language-text"&gt;Get some value and abort with a NotFound error if it&amp;#x27;s not there
Unless the user exists, abort with a NoUser error
Get the current time
When the current time is greater than the token expiration, abort
Return a valid token
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This version of the Haskell snippet doesn’t suffer from increasing indentation.
We also return early since behind the scenes Haskell still sort of pattern
matches on the various result types and it knows how it can short-circuit the
computation. For example, if &lt;code&gt;ok&lt;/code&gt; is false, this will immediately return the
&lt;code&gt;NoUser&lt;/code&gt; error, it will not run the remaining lines of that function. There’s
also very little to no Go-style line noise about error checking. I dare say
it’s expressive and concise. But it took me a lot of experimenting to get there
and also required writing &lt;a href="https://github.com/cideM/lions-backend/blob/770f3e481ee0a7fed27742d0cd8d5f050acfcbfb/backend/src/Error.hs"&gt;some
utility&lt;/a&gt;
functions for translating between various custom Monads, again. There are more things I
could complain about here&lt;sup class="footnote-reference"&gt;&lt;a href="https://fbrs.io/fp/#1"&gt;1&lt;/a&gt;&lt;/sup&gt;, but I hope that my main point here is clear:
translating a simple pattern, early return, to Haskell, without making
the code untolerably ugly, is not straight forward and it can be hard to find
this kind of advice in tutorials and books.&lt;/p&gt;
&lt;p&gt;That was a lot of negativity now, so let’s move on to something more positive:
algebraic data types (ADT) and pattern matching. Together, these two features
form the basis for how I model the world in code. Nothing beats the ease with
which this let’s me design branching control flow based on a finite number of
options. I currently write Go for a living and the lack of ADTs has caused more
than one logic bug and runtime panic. Generally, few languages give me the same
confidence as Haskell. It’s really hard to overstate the peace of mind that
comes from not having to worry about nil pointer exceptions and unhandled
cases. While I was writing this blog post I suddenly had an insight and removed
one field from a record that’s used everywhere in my application. In many
languages I would have dreaded this task but in Haskell I simply removed the
field and then followed the compiler errors. At the end I was pretty much 100%
certain that I didn’t break anything.&lt;/p&gt;
&lt;p&gt;There are a lot of other things I could write about here, but I think most of
the usual suspects have already been covered in great detail in other blog
posts, on Reddit or on Hackernews. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Historical baggage like a string type no one really uses&lt;/li&gt;
&lt;li&gt;An anemic standard library resulting in lots and lots of imports&lt;/li&gt;
&lt;li&gt;No one likes records&lt;/li&gt;
&lt;li&gt;Slightly confusing build systems&lt;/li&gt;
&lt;li&gt;Modules that are at the same time tedious and not powerful&lt;/li&gt;
&lt;li&gt;Lazy evaluation can be tricky for performance and debugging&lt;/li&gt;
&lt;li&gt;The typical issues of working with a niche language&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But I don’t find any of these things particularly intersting and they’re also
not deal breakers for me.&lt;/p&gt;
&lt;p&gt;I wanted to write about the experience of using Haskell, not the nitty gritty
technical details. So what does it feel like, then? For the most part it’s just
like writing a backend in any other language. Define routes, parse query
parameters, do some authorization and authentication checks, fetch something
from the database, render a document. On a good day, Haskell makes me more
efficient and productive because I spend less time fixing bugs and less time
translating the problem to data structures, because ADTs and the type checker
are just that good. On a bad day, I still find myself deciphering runtime
issues, for example caused by a mismatch between database and Haskell types,
while also spending several evenings just solving Haskell issues.&lt;/p&gt;
&lt;p&gt;I’m neutral about using Haskell for future projects, because there are things
about the language I love and others that drive me crazy.&lt;/p&gt;
&lt;h3 id="nix-1"&gt;Nix&lt;/h3&gt;
&lt;p&gt;According to &lt;code&gt;tokei&lt;/code&gt; this project has 1206 lines of Nix code, which fall into
three categories: developer environment, building parts of the application,
building and deploying the NixOS image that runs on a digital ocean droplet.&lt;/p&gt;
&lt;p&gt;I can’t think of a better way to provide all the tools necessary to work with a
project than Nix. Every new project I start uses Nix&lt;sup class="footnote-reference"&gt;&lt;a href="https://fbrs.io/fp/#2"&gt;2&lt;/a&gt;&lt;/sup&gt; to provide
instructions for how to create a shell that includes things like compiler,
formatter, database tools, terraform, and so on. In combination with
&lt;a href="https://fbrs.io/fp/direnv.net/"&gt;direnv.net&lt;/a&gt; whenever I &lt;code&gt;cd&lt;/code&gt; into such a project my shell
environment is updated automatically. Doing this is as easy as adding the stuff
you need to &lt;a href="https://github.com/cideM/lions-backend/blob/3aff0eabd91bfd37824a6b814b8dbcdb1edb4c58/shell.nix#L44"&gt;a
list&lt;/a&gt;,
and it usually just works (and keeps working).&lt;/p&gt;
&lt;p&gt;Building the various parts of this application with Nix was also not too much
work, but your mileage here depends on the languages and tools you use in your
project and how well they’re supported in Nix. For Haskell there’s a tool you
can use – &lt;code&gt;cabal2nix&lt;/code&gt; – which generates Nix code that contains instructions
for how to build your project, including its Haskell dependencies. You can
mostly just follow the documentation to get something up and running. But
Haskell is an outlier and many other languages don’t enjoy the same level of
support. For some of those languages there are tools that translate lock files
to Nix instructions, but they’re &lt;a href="https://discourse.nixos.org/t/status-of-lang2nix-approaches/14477/20"&gt;not without
issues&lt;/a&gt;.
If no such tool exists you’re out of luck and building your application with
Nix will quickly become very, very challenging. Nix limits your ability to make
HTTP requests and interact with the file system. You therefore can’t just take
a command like &lt;code&gt;yarn install&lt;/code&gt;, which would download Javascript libraries, and
wrap it in some Nix code. Instead, Nix needs to download each of those
dependencies, and for that it needs at least a URL and a hash to verify the
contents. Good luck quickly whipping up a tool that does that.&lt;/p&gt;
&lt;p&gt;In this project I ran into little to no problems in that area though. What I
get in return for building everything with Nix is great caching of build
artefacts, and incredible reproducibility. I can clone the project, run a
single command, and it Just Works™. Additionally, even if it took me a few
hours to figure something out, it was usually a one-time cost.&lt;/p&gt;
&lt;p&gt;What did not spark a lot of joy was running my application with Nix, both
locally and remotely. Nix is already a niche technology, but deploying with Nix
is a niche within a niche. Documentation is lackluster and there’s a real
chance that you’ll be the first person to use a bunch of tools in a certain
combination and as such there’s no StackOverflow answer you can just copy from.
Here are some things that tripped me up in no particular order.&lt;/p&gt;
&lt;p&gt;You can’t build a NixOS image on a Darwin machine, so you need to run things
through Docker. It’s another layer of indirection and resource usage for no
gain. It’s generally frustrating if you develop on one machine for a while,
only to realize that everything’s broken when you to switch to another OS,
especially in 2021. I wouldn’t go so far as calling Darwin a second class
citizen in Nix, but you’ll have to put in extra effort here and there.&lt;/p&gt;
&lt;p&gt;If you run your application inside a NixOS image, how do you actually
(re-)start and monitor the application? I went with Systemd, which was another
piece of technology I needed to learn. Documentation is OK but outside of the
official manpages I found far fewer articles that talked about using Systemd
for backend services than I expected. It therefore took me longer than I wished
to figure out how to pass credentials to my application, share a database
directory between different Systemd services, which syntax goes where, and so
on. If I wanted to debug things on MacOS I needed to build the image through
Docker and then I also ended up running a QEMU VM through Docker. You’re
really on your own for a lot of these tasks and at some point I was tired of
figuring out one thing only be the stuck on the next task.&lt;/p&gt;
&lt;p&gt;The biggest time sink is just figuring out what arguments a Nix function takes,
which arguments are magically and silently passed through but can be customized
(and how), and what the function returns. My secret weapon here is GitHub code
search. Nothing beats seeing working code. I rarely benefit from reading the
official manual, since it doesn’t contain enough runnable examples. I do spend
a lot of time in issues, PRs and in source code.&lt;/p&gt;
&lt;p&gt;Consider Nix a slider that goes from local environment all the way to
production environment. The further right you go, the less likely it is that
you can just follow a cookbook. It’s therefore not a big surprise that I would:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always use Nix for local development&lt;/li&gt;
&lt;li&gt;Sometimes use it for building the application&lt;/li&gt;
&lt;li&gt;Probably not use it again for deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="purescript-1"&gt;PureScript&lt;/h3&gt;
&lt;p&gt;This project was originally meant to be a super simple, single Haskell file
that implements a server that does not require any client side code. I really missed that mark.&lt;/p&gt;
&lt;p&gt;I added PureScript (PS) because there’s no HTML-only way of toggling a bunch of
checkboxes at once. The use case is to select a few (or all) users from a list
and then click a button to send an email with all selected users added as
recipients.&lt;/p&gt;
&lt;p&gt;The idea was to progressively enhance the server-side markup. On page load,
reveal the hidden “send email” button and inject a checkbox into every cell of
the users table. Do not replace the entire users table with markup rendered on
the client, because that would have meant reimplementing all of the markup in
PS. Or figuring out a way of sharing my Haskell template code with PS, but that
sounded like a lot of work. Unfortunately PS doesn’t seem to have any
libraries that help with that kind of task. Most of its ecosystem seems to
revolve around React-like libraries that want full control over a DOM node.&lt;/p&gt;
&lt;p&gt;So I ended up translating Javascript to PS almost word by word and as such the code looks like this:&lt;/p&gt;
&lt;pre class="language-purescript "&gt;&lt;code class="language-purescript"&gt;getCheckboxState :: Node -&amp;gt; Effect { email :: String, checked :: Boolean }
getCheckboxState checkbox = do
  checkboxAsEl &amp;lt;- maybe (throw &amp;quot;couldn&amp;#x27;t convert checkbox to element&amp;quot;) pure $ Element.fromNode checkbox
  checkbox&amp;#x27; &amp;lt;- maybe (throw &amp;quot;couldn&amp;#x27;t convert checkbox to input element&amp;quot;) pure $ InputElement.fromNode checkbox
  isChecked &amp;lt;- InputElement.checked checkbox&amp;#x27;
  email &amp;lt;- Element.getAttribute &amp;quot;data-email&amp;quot; checkboxAsEl &amp;gt;&amp;gt;= maybe (throw &amp;quot;no data-email attr&amp;quot;) pure
  pure { email: email, checked: isChecked }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the same as chaining &lt;code&gt;document.querySelector&lt;/code&gt; and getting and setting
DOM node attributes. Except that in PS I had to cast from one node type to
another a lot. It’s a bit puzzling and I’m still not sure if I overlooked
something really obvious, but take a look at
&lt;a href="https://pursuit.purescript.org/packages/purescript-web-dom/5.0.0/docs/Web.DOM.Element"&gt;&lt;code&gt;Web.DOM.Element&lt;/code&gt;&lt;/a&gt;
and see for yourself. I think you’re not meant to write “low-level” code like
that. It’s probably better to wrap it in more expressive, high-level APIs.
Since I couldn’t find any of those in the form of ready-to-use libraries, and
didn’t want to abstract something I only did once, ugly code it is.&lt;/p&gt;
&lt;p&gt;I was pleasently surprised by the Nix support though, which seems to mostly be
the work of a single person,
&lt;a href="https://github.com/justinwoo"&gt;https://github.com/justinwoo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wish I could say more about PS, because it’s actually a nice language, but it
just wasn’t a good fit for this project. I think the client side code is in the
kilobytes, whereas a vanilla JS implementation would have been much, much
leaner.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Haskell is still my favorite programming language. Rust sounds like a modern
and more refined take on the concept of pair programming with a very capable
compiler, but whenever I tried it I quickly missed the ease of writing code in
a high level language. But as much as I enjoy using Haskell, I can’t ignore the
frustration it has caused me, and how much slower it often makes me during the
initial implementation of something.&lt;/p&gt;
&lt;p&gt;I still believe that Haskell, with a good enough understanding of its more
advanced language extensions, can be an incredibly productive language. The
more potential bugs you can rule out through the type system, the less
maintenance down the road. But getting there takes a long time.&lt;/p&gt;
&lt;p&gt;My goal for the next 12 months is to do another project of roughly the same
complexity level, but use tools that value simplicity above everything else.&lt;/p&gt;
&lt;p&gt;For example, instead of devising a complicated secret management solution, that
could end up being risky because you don’t understand how it works, I could
also just &lt;code&gt;ssh&lt;/code&gt; into the server and create a file with the secrets and call it
a day. Instead of using Nix to manage the complexity of a sprawling dependency
tree, what if my app barely had any dependencies?&lt;/p&gt;
&lt;hr /&gt;
&lt;div class="footnote-definition" id="1"&gt;&lt;sup class="footnote-definition-label"&gt;1&lt;/sup&gt;
&lt;p&gt;For example, I would strongly recommend to immediately catch potential
exceptions thrown by a function that has &lt;code&gt;MonadThrow&lt;/code&gt; (if I recall
correctly), because I’ve had surprising errors when the exception type was
not what I thought, because somewhere a function happened to have a
&lt;code&gt;MonadThrow&lt;/code&gt; in its signature but with the exception type hardcoded to
string. I’ve also created subtle bugs in code where I thought I was
short-circuiting but I was actually just returning &lt;code&gt;IO Left&lt;/code&gt; instead of
&lt;code&gt;Left&lt;/code&gt;. So this &lt;code&gt;mtl&lt;/code&gt;-style control flow is not without its pitfalls.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="footnote-definition" id="2"&gt;&lt;sup class="footnote-definition-label"&gt;2&lt;/sup&gt;
&lt;p&gt;More specifically a Nix Flake&lt;/p&gt;
&lt;/div&gt;</description><author/><pubDate>Thu, 23 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://fbrs.io/fp/</guid></item><item><title>My Math Confession</title><link>https://adriano.fyi/posts/2021-09-23-my-math-confession/</link><description>&lt;p&gt;I did math for the first time when I was 29 years old. This isn&amp;rsquo;t a self-deprecating snipe or feigned humility. I did math for the first time when I was 29 years old. When I finally did it for the first time &amp;ndash; at least, the first recorded time &amp;ndash; I didn&amp;rsquo;t know it had happened. It was six years later reading Paul Lockhart&amp;rsquo;s excellent piece entitled, &lt;a href="https://www.maa.org/external_archive/devlin/LockhartsLament.pdf"&gt;&amp;ldquo;A Mathematician&amp;rsquo;s Lament&amp;rdquo;&lt;/a&gt; that I realized what I&amp;rsquo;d done; I&amp;rsquo;d finally done math all those years ago. At the time I was pretty sure I was programming, or something close to it. It was one part statistics, one part algorithms, and one part math. I&amp;rsquo;m not even sure today what constitutes the whole of what I was doing, but those are without a doubt three of the parts. I was also pretty sure that I was doing &lt;em&gt;something&lt;/em&gt; related to cancer. After all, I was doing what I was doing because I was getting paid for it. And I knew that the money came circuitously to me by way of the National Cancer Institute. And if there&amp;rsquo;s one thing I know about the NCI, I know that they care about fighting &lt;em&gt;cancer&lt;/em&gt;. So I was probably doing something related to cancer, too.&lt;/p&gt;
&lt;p&gt;So how is it that someone who studied Computer Science in college was able to graduate without having previously done any math? Computer Science is a lot of math, right? My entire schooling, all the way through high school, I never thought that math was my strong suit. And if you&amp;rsquo;ve read my introductory sentence, you can probably deduce that I&amp;rsquo;m not getting rich as a mathematician any time soon. As far as I had a plan to go to college (and you could hardly call it that), the plan was to study English. I wasn&amp;rsquo;t a good writer and I didn&amp;rsquo;t know what it meant to study English or have a profession in it. What I did know was that I heard people studied English in college, and in high school I was able to exert a minimal amount of effort in English courses without failing. English was a safe bet for a soon-to-be Halo addict. So needless to say, I didn&amp;rsquo;t trouble myself with a lot of math in high school. My senior year of high school I took a course called &lt;strong&gt;Business Math&lt;/strong&gt;. Business Math was a real doozy of a course. And somewhere between the end of high school and the beginning of college, the &amp;ldquo;plan&amp;rdquo; changed and I decided to study computer science since I was already making money doing computer stuff.&lt;/p&gt;
&lt;p&gt;Now let me tell you a few things about Business Math. Business Math has never saved a single soul from a Freshman year Computer Science 101 culling. Those things are butal; I think we lost 50% of the class by the end of the semester. Business Math also hasn&amp;rsquo;t gotten a single computer science student through Discrete Math, Calculus, &lt;em&gt;pre&lt;/em&gt; Calculus, or even pre-Calculus for Business Majors (sorry, biz folks). Business Math couldn&amp;rsquo;t possibly do that because it&amp;rsquo;s a truly garbage course; unparalleled in its uselessness. But the other reason is that Business Math is not even a mathematics course. And neither are Discrete, CS 101, Calc, or Pre-calc. Sure, these classes all teach about numbers, functions, and how to fiddle with formulas, but none of them teach any mathematics. No, mathematics courses hardly exist. I&amp;rsquo;d love to take one as an adult, if you&amp;rsquo;re aware of any.&lt;/p&gt;
&lt;p&gt;I have an idea for how to rename a few math courses&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Business Math&lt;/strong&gt;: Easy Numbers for Suits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pre-Calculus&lt;/strong&gt;: Harder Numbers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Calculus&lt;/strong&gt;: Hard Numbers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Discrete Math&lt;/strong&gt;: Hard Numbers for Computers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Algorithms&lt;/strong&gt;: Logic &amp;amp; Hard Numbers for Computers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m dodging the rhetorical question here. How did someone who studied Computer Science in college graduate without doing math? I did it by taking &lt;strong&gt;Business Math&lt;/strong&gt; in high school. Because graduating with an undergraduate computer science degree doesn&amp;rsquo;t require mathematics at all. At least not so much that you can&amp;rsquo;t temporarily memorize some general mathematical techniques for an exam and forget it all the next day. They won&amp;rsquo;t be used outside of an exam setting; trust me. I&amp;rsquo;m looking at you, proofs. But even proofs aren&amp;rsquo;t really mathematics. Students do proofs because instructors ask them to do proofs; they&amp;rsquo;re not actually proving anything novel. If they were, they&amp;rsquo;d &lt;em&gt;actually&lt;/em&gt; be doing math. They&amp;rsquo;d be doing math because they&amp;rsquo;d be thinking, getting stuck, and engaging in fruitful conjecture about the nature of &lt;em&gt;problems&lt;/em&gt;. But most &amp;ldquo;computer science&amp;rdquo; doesn&amp;rsquo;t require much of that, and I can guarantee that the average student still stands on a podium in four years even if they do absolutely no math. Their robe may not have much in the way of flair, but they&amp;rsquo;ll surely be cleared to walk across that podium and grab a ludicrously expensive piece of paper that constitutes a degree. No, computer science students generally ask computer to do the numbers, and the computers tend to do the numbers &lt;em&gt;really&lt;/em&gt; well; even the hard numbers.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Like most people working in a technical field, my early days were riddled with imposter syndrome. I was waiting for math to rear its head and reveal me as the fraud that I was. Because as we all know, computer science is &amp;ldquo;all math&amp;rdquo; and I&amp;rsquo;d hardly learned a lick of it. I occasionally added and multiplied numbers in code, but that&amp;rsquo;s hardly what I&amp;rsquo;d call &amp;ldquo;all math&amp;rdquo;. The real computer science was out there, it would find me, and I&amp;rsquo;d die destitute when it inevitably did.&lt;/p&gt;
&lt;p&gt;And then the math found me. This was it. It took five years to find me post-college. What took it so long? It&amp;rsquo;s a shame I hadn&amp;rsquo;t saved any money. Business Math should have prepared me better for this! I should have had a savings account primed to keep me afloat and at least temporarily stave off destitution. I knew this was coming and there was some consolation in that.&lt;/p&gt;
&lt;p&gt;But the notation, the formulas &amp;hellip; holy shit the &lt;em&gt;notation&lt;/em&gt; was everywhere. This was worse than I anticipated.&lt;/p&gt;
&lt;p&gt;
&lt;input hidden="hidden" id="zoomCheck-434cf" type="checkbox" /&gt;
&lt;label for="zoomCheck-434cf"&gt;
  &lt;img alt="fastBCS in mathematical notation" class="zoomCheck" src="https://nextcloud.adriano.fyi/s/WcAx7CrzgG6bidN/preview" /&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;p&gt;I know you know what you&amp;rsquo;re looking at in this image. You&amp;rsquo;re looking at cold, hard &lt;em&gt;Math&lt;/em&gt; with a capital &amp;ldquo;m&amp;rdquo;; no question about it. This is it. The real deal. The math professor in white socks with Jesus sandals stuff. The potting soil in which to grow one&amp;rsquo;s neck beard. There&amp;rsquo;s an upside down &amp;ldquo;A&amp;rdquo; in the code for chrissake. It&amp;rsquo;s real math; righteous math.&lt;/p&gt;
&lt;p&gt;But after I cut through all this absurd notation in which an algorithm should never be expressed, I found some really basic building blocks. And more importantly, I found a problem that needed solving, and problems generate ideas. Then eventually, ideas give way to solutions and more absurd notation in which algorithms should never be expressed.&lt;/p&gt;
&lt;p&gt;Behold&lt;/p&gt;
&lt;p&gt;
&lt;input hidden="hidden" id="zoomCheck-53d26" type="checkbox" /&gt;
&lt;label for="zoomCheck-53d26"&gt;
  &lt;img alt="fastBCS2 in mathematical notation" class="zoomCheck" src="https://nextcloud.adriano.fyi/s/KRpnJZH66bR4kFi/preview" /&gt;
&lt;/label&gt;
&lt;/p&gt;
&lt;p&gt;The solution. This is the culmination of a lot of &lt;em&gt;math&lt;/em&gt;. But I&amp;rsquo;m not talking about the asinine notation, functions, or codes. I&amp;rsquo;m talking about the existence of one line of code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;colsum[j] &amp;lt;- colsum[j] - C[pi(i),pi(j)]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This was it. This is what I feared would render me destitute. It doesn&amp;rsquo;t matter what any of this code means. What matters is that the math was a single insight. It was the realization that if a single column of a matrix was subtracted before recalculating sums, we could ask the computer to crunch a lot fewer hard numbers. It meant maybe some day a researcher in a lab could mash a button and have a chemical candidate for a novel chemotherapy enter their brain for the first time. It meant they could do that every couple seconds instead of every couple weeks. The math was the thought process, not the numbers, functions, rules or other tools of the trade taught in your average Hard Numbers for Computers course.&lt;/p&gt;
&lt;p&gt;I was never bad at math. I have a bad memory for formulas and never had a real problem to solve.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Before my dear friend and business partner Wayne Johnson died a year ago this month, he gave me permission to open source this small piece of the work we did together referenced above (&lt;a href="https://github.com/acaloiaro/topk-taupath%29"&gt;https://github.com/acaloiaro/topk-taupath)&lt;/a&gt;. I&amp;rsquo;m forever grateful to him for being a great mentor and teaching me how to do math before 30. I&amp;rsquo;ll always remember handing this work over to Wayne. It was the culmination of years of time together and nonstop, profound lessons from Wayne about how to think more methodically. I was pretty nervous about it because Wayne was a very precise man who demanded pricise work. I was even more nervous about the fact that I hadn&amp;rsquo;t heard from him an entire day after handing it over. Finally he called and while I don&amp;rsquo;t remember much about that conversation, I do remember the first thing he said to me: &amp;ldquo;it&amp;rsquo;s beautiful&amp;rdquo;. And that&amp;rsquo;s how I know math was done, because formulas and rules are not beautiful, but math is art and art is beautiful.&lt;/p&gt;
&lt;p&gt;I challenge you to tell someone they&amp;rsquo;ve done something beautiful. I promise they&amp;rsquo;ll never forget it.&lt;/p&gt;</description><author>Adriano Caloiaro's personal blog</author><pubDate>Thu, 23 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://adriano.fyi/posts/2021-09-23-my-math-confession/</guid></item><item><title>We focus too much on the low ends</title><link>https://stop.zona-m.net/2021/09/we-focus-too-much-on-the-low-ends/</link><description>&lt;p&gt;Because we feel our status wrongly.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 22 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/we-focus-too-much-on-the-low-ends/</guid></item><item><title>Book review - Library as Place</title><link>https://river.me/blog/book-review-library-as-place/</link><description>A review of &amp;ldquo;The Library as Place: History, Community, and Culture&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 22 Sep 2021 13:07:18 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-library-as-place/</guid></item><item><title>Book Review: Nausea</title><link>https://nicolaiarocci.com/book-review-nausea/</link><description>&lt;p&gt;Antoine Roquentin, the protagonist of the novel, is a former adventurer who has
been living for three years in Bouville, a fictional French seaport town,
researching the life of an 18th-century diplomat. During his previous life
around the world, Antoine has seen many places, met many interesting people,
done exciting things. For the last three years, however, he&amp;rsquo;s been alone in
Bouville. He has no friends and no desire to make some or meet anyone. He&amp;rsquo;s
interested in nothing, not even in his work that he keeps neglecting. His days
are mostly spent walking around town, listening to conversations and observing
people around him. A &amp;ldquo;sweeting sickness&amp;rdquo; he calls nausea increasingly impinges
on almost everything he does and enjoys.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 22 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-nausea/</guid></item><item><title>Update for September, year of the new hope 2021</title><link>https://ciesie.com/post/update_22092021/</link><description>&lt;p&gt;I&amp;rsquo;ve not written one of those at all this year. Not surprisingly, the most productive year for this
website was 2020&amp;hellip; thanks global pandemic&amp;hellip;?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve posted the &lt;a href="https://ciesie.com/post/rfid_explained/"&gt;RFID Explained&lt;/a&gt; blog post. I&amp;rsquo;m trying to
gather everything I&amp;rsquo;ve learned, while working on my RFID project, in one place. This is an
assumption but in the maker&amp;rsquo;s space a lot of technologies are just an Arduino libraries. What I
mean by that is that very few people actually learn how a specific technology works. They build a
device using Arduino, using the most popular library. I don&amp;rsquo;t think there is anything wrong with
that. It was probably one of the goals of the Arduino creator: remove the technical obstacles to
make technology more approachable for people without deep technical knowledge. What if you want to
learn more? You need to read, often times badly written, documentation spread over multiple
websites and documents. Sometimes, if you really want to understand something, you should read the
documentation of the standard, which is a document you have to pay for&amp;hellip; My RFID blog post is
supposed to be a quickstart for those that want to understand the common RFID solutions.&lt;/p&gt;</description><author>ciesie.com</author><pubDate>Wed, 22 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ciesie.com/post/update_22092021/</guid></item><item><title>Is magic real? - 123dev #17</title><link>https://justingarrison.com/123dev/2021-09-22-123dev-17/</link><description>&lt;img alt="An animatronic beast transforms into a prince" src="https://justingarrison.com/img/123dev/17-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="i-still-dont-know-how-this-works"&gt;I still don’t know how this works&lt;/h2&gt;
&lt;p&gt;I was at Disney Animation when they first showed us the animatronics for the new Beauty and the Beast ride for Tokyo Disneyland. I never saw this transformation and still don’t understand how it works.&lt;/p&gt;
&lt;p&gt;Honestly a lot of code I write feels the same way. The fact that I can tell transistors what to do with any resemblance of English language is pretty magical.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 22 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-22-123dev-17/</guid></item><item><title>University: Nearing the End</title><link>https://www.chriswales.uk/blog/university-nearing-end</link><description>University has been a fun and interesting experience so far. It's given me many amazing opportunities and has taught me much about computer…</description><author>Christopher Wales' Blog</author><pubDate>Wed, 22 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.chriswales.uk/blog/university-nearing-end</guid></item><item><title>Facebook knows Instagram is dangerous. Who else should?</title><link>https://stop.zona-m.net/2021/09/facebook-knows-instagram-is-dangerous.-who-else-should/</link><description>&lt;p&gt;It&amp;rsquo;s an easy answer, really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 22 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/facebook-knows-instagram-is-dangerous.-who-else-should/</guid></item><item><title>Manjaro</title><link>https://mfashby.net/posts/2021-09-21-manjaro/</link><description/><author>Home on mfashby.net</author><pubDate>Tue, 21 Sep 2021 22:03:48 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-21-manjaro/</guid></item><item><title>Cruella</title><link>https://olshansky.info/movie/cruella/</link><description>Olshansky's review of Cruella</description><author>🦉 olshansky 🦁</author><pubDate>Tue, 21 Sep 2021 07:58:51 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/cruella/</guid></item><item><title>Re: Software and Branding</title><link>https://www.marginalia.nu/log/23-re-software-and-branding/</link><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="#ZgotmplZ"&gt;gemini://friends.riverside.camp/~clarity/journal/branding.gmi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#ZgotmplZ"&gt;gemini://idiomdrottning.org/re-branding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some interesting thoughts going around on the topic of branding in software and websites. I&amp;rsquo;ve had thoughts like these too, and designed a lot of my website purposefully un-branded. I have no logos, no banners, barely navigational links. I figured I would just see what happens if you subvert this paradigm of web design, since it is stuff you typically just &amp;ldquo;scroll past&amp;rdquo; to get to what you care about like you skip the beginning of every youtube video. Since the point has never been to create a brand, I figured I would elevate the message so far above everything else that there simply is nothing else.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Tue, 21 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/23-re-software-and-branding/</guid></item><item><title>What does Blockbuster and misquoting Darwin have in common? - 123dev #16</title><link>https://justingarrison.com/123dev/2021-09-21-123dev-16/</link><description>&lt;a href="https://twitter.com/JonErlichman/status/1383457845081825285"&gt;
    &lt;img alt="A map of the united states showing Blockbuster video locations over time." src="https://justingarrison.com/img/123dev/16-0.gif" width="100%" /&gt;
&lt;/a&gt;
&lt;p&gt;Click the image for the full length video. It&amp;rsquo;s worth it.&lt;/p&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="survival"&gt;Survival&lt;/h2&gt;
&lt;p&gt;In the struggle for survival, the fittest win out at the expense of their rivals because they succeed in adapting themselves best to their environment.&lt;/p&gt;
&lt;p&gt;You may think that quote is from Charles Darwin but it is actually a misquote. I’ll let you do your own homework to find the true author.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 21 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-21-123dev-16/</guid></item><item><title>Enumerating and analyzing 40+ non-V8 JavaScript implementations</title><link>http://notes.eatonphil.com/javascript-implementations.html</link><description>&lt;p&gt;V8 is, I'm sure, the most used implementation of JavaScript
today. Used in Chrome, (and by extension) Microsoft Edge, Node.js,
etc. Safari's JavaScriptCore and Firefox's SpiderMonkey are also
contenders for extremely mainstream implementations.&lt;/p&gt;
&lt;p&gt;But what else is out there? What if I want to embed JavaScript in a C
program, or a Go program, or a Rust program, or a Java program(, and
so on)? Or what if I want to run JavaScript on a microcontroller? Or
use it as a base for language research? It turns out there are many
high-quality implementations out there.&lt;/p&gt;
&lt;p&gt;This post describes a number of them and their implementation
choices. I'm not going to cover V8, JavaScriptCore, or SpiderMonkey
because they are massive and hide multiple various interpreters and
compilers inside. Plus, you already know about them.&lt;/p&gt;
&lt;p class="note"&gt;
  I'm going to miss some implementations and get some details
  wrong. Please &lt;a href="https://twitter.com/phil_eaton"&gt;Tweet&lt;/a&gt; or
  &lt;a href="mailto:phil@eatonphil.com"&gt;email&lt;/a&gt; me with your corrections! I'd be
  particularly interested to hear about pure-research; and commercial,
  closed-source implementations of JavaScript.
&lt;/p&gt;&lt;h3 id="corporate-backed"&gt;Corporate-backed&lt;/h3&gt;&lt;p&gt;These are implementations that would make sense to look into for your
own commercial, production applications.&lt;/p&gt;
&lt;h4 id="on-the-jvm"&gt;On the JVM&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oracle/graaljs"&gt;Oracle's GraalJS&lt;/a&gt;: compiles JavaScript to JVM bytecode or GraalVM&lt;ul&gt;
&lt;li&gt;Support: Full compatibility with latest ECMAScript specification&lt;/li&gt;
&lt;li&gt;Implementation language: Java&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://www.graalvm.org/"&gt;GraalVM&lt;/a&gt; or &lt;a href="https://www.graalvm.org/reference-manual/js/RunOnJDK/"&gt;stock JDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/oracle/graaljs/blob/master/graal-js/src/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/oracle/graaljs/releases/tag/vm-19.0.0"&gt;2019?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Replaced Nashorn as the default JavaScript implementation in JDK.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mozilla/rhino"&gt;Mozilla's Rhino&lt;/a&gt;: interprets and compiles JavaScript to JVM bytecode&lt;ul&gt;
&lt;li&gt;Support: ES6&lt;/li&gt;
&lt;li&gt;Implementation language: Java&lt;/li&gt;
&lt;li&gt;Runtime: Both &lt;a href="https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/Interpreter.java"&gt;interpreted through custom bytecode VM&lt;/a&gt; and interpreted &lt;a href="https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/optimizer/Codegen.java"&gt;after compiling to JVM bytecode&lt;/a&gt; as an optimization&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/mozilla/rhino/blob/master/src/org/mozilla/javascript/Parser.java"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="http://udn.realityripple.com/docs/Mozilla/Projects/Rhino/History"&gt;1998?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Replaced by Nashorn as the default JavaScript engine on the JVM, but remains actively developed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openjdk/nashorn"&gt;Oracle's Nashorn&lt;/a&gt;: compiles JavaScript to JVM bytecode&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: Java&lt;/li&gt;
&lt;li&gt;Runtime: compiles to &lt;a href="https://github.com/openjdk/nashorn/tree/main/src/org.openjdk.nashorn/share/classes/org/openjdk/nashorn/internal/codegen"&gt;JVM bytecode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/openjdk/nashorn/blob/main/src/org.openjdk.nashorn/share/classes/org/openjdk/nashorn/internal/parser/Parser.java"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://blogs.oracle.com/nashorn/open-for-business"&gt;2012?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Replaced Rhino as default JavaScript implementation on JVM. Replaced by GraalJS more recently, but remains actively developed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="embeddable"&gt;Embeddable&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nginx/njs"&gt;Nginx's njs&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/nginx/njs/blob/master/src/njs_vmcode.c"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/nginx/njs/blob/master/src/njs_parser.c"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mp2.dk/techblog/chowjs/"&gt;ChowJS&lt;/a&gt;: proprietary AOT compiler based on QuickJS for game developers&lt;ul&gt;
&lt;li&gt;Support: everything QuickJS does presumably (see further down for QuickJS)&lt;/li&gt;
&lt;li&gt;Implementation language: C presumably&lt;/li&gt;
&lt;li&gt;Runtime: QuickJS's bytecode interpreter but also an AOT compiler&lt;/li&gt;
&lt;li&gt;Parser: QuickJS's presumably&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://mp2.dk/techblog/chowjs/"&gt;2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Code is not available so exact analysis on these points is not possible at the moment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ccxvii/mujs"&gt;Artifex's mujs&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5, probably&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/ccxvii/mujs/blob/master/jsrun.c"&gt;Bytecode stack-oriented VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/ccxvii/mujs/blob/master/jsparse.c"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/ccxvii/mujs/releases/tag/1.0.0"&gt;2017?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Originally part of MuPDF viewer, but now broken out. Thanks to &lt;a href="https://twitter.com/rwoodsmall"&gt;@rwoodsmalljs&lt;/a&gt; for mentioning!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="embedded-systems"&gt;Embedded Systems&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Samsung/escargot"&gt;Samsung's Escargot&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES2020&lt;/li&gt;
&lt;li&gt;Implementation language: C++&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/Samsung/escargot/tree/master/src/interpreter"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/Samsung/escargot/tree/master/src/parser"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/Samsung/escargot/graphs/contributors"&gt;2017?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/espruino/Espruino"&gt;Espruino&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: parts of ES5, ES6, ES7/8&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: Seems like direct recursive interpreting without an AST/intermediate form&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/espruino/Espruino/blob/master/src/jsparse.c"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/espruino/Espruino/releases/tag/BEFORE_FUNCTION_REFACTOR"&gt;2012?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cesanta/elk"&gt;Cesanta's Elk&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: subset of ES6&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/cesanta/elk/blob/master/elk.c"&gt;Direct recursive interpreter without AST or bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/cesanta/elk/blob/master/elk.c"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/cesanta/elk/releases/tag/0.0.1"&gt;2019?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: It does all of this with a GC and FFI in &amp;lt;1400 lines of readable C code. Damn.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cesanta/mjs"&gt;Cesanta's mJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: subset of ES6&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/cesanta/mjs/blob/master/mjs.c#L3411"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/cesanta/mjs/blob/master/mjs.c#L12780"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/cesanta/mjs/releases/tag/1.5"&gt;2017?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Moddable-OpenSource/moddable/blob/public/xs/sources/xsSyntaxical.c"&gt;Moddable's XS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES2018&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/Moddable-OpenSource/moddable/blob/public/xs/sources/xsRun.c"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/Moddable-OpenSource/moddable/blob/public/xs/sources/xsSyntaxical.c"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://www.moddable.com/XS7-TC-39"&gt;2017?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: More details at &lt;a href="https://www.moddable.com/XS7-TC-39"&gt;https://www.moddable.com/XS7-TC-39&lt;/a&gt; and &lt;a href="https://www.moddable.com/faq#what-is-xs"&gt;https://www.moddable.com/faq#what-is-xs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="other"&gt;Other&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/facebook/hermes"&gt;Facebook's Hermes&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES6 &lt;a href="https://hermesengine.dev/docs/language-features"&gt;with exceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Implementation language: C++&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/facebook/hermes/tree/main/lib/VM"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/facebook/hermes/blob/main/lib/Parser/JSParserImpl.cpp"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/facebook/hermes/releases/tag/v0.0.1"&gt;2019?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/qt/qtdeclarative/tree/dev/src/qml/jsruntime"&gt;Qt's V4&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: C++&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/qt/qtdeclarative/blob/dev/src/qml/jsruntime/qv4vme_moth.cpp"&gt;Bytecode VM&lt;/a&gt; and JIT compiler&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/qt/qtdeclarative/blob/dev/src/qml/parser/qqmljs.g"&gt;qlalr custom parser generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: 2013&lt;/li&gt;
&lt;li&gt;Notes: Unclear if can be run standalone outside of Qt.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don't know whether to put Microsoft's ChakraCore into this list or
the next. So I'll put it here but note that as of this year 2021, they
are transitioning it to become a community-driven project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/chakra-core/ChakraCore"&gt;Microsoft's ChakraCore&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES6, probably more&lt;/li&gt;
&lt;li&gt;Implementation language: C++&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/chakra-core/ChakraCore/tree/master/lib/Backend"&gt;Bytecode VM and JIT on x86/ARM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/chakra-core/ChakraCore/blob/master/lib/Parser/Parse.cpp"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: 2015?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mature,-community-driven"&gt;Mature, community-driven&lt;/h3&gt;&lt;p&gt;Implementations toward the top are more reliable and
proven. Implementations toward the bottom less so.&lt;/p&gt;
&lt;p&gt;If you are a looking to get involved in language development, the
implementation further down on the list can be a great place to start
since they typically need work in documentation, testing, and language
features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/bellard/quickjs"&gt;Fabrice Bellard's QuickJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES2020&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://raw.githubusercontent.com/bellard/quickjs/master/quickjs.c"&gt;Bytecode VM&lt;/a&gt; (this is a single large file)&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://raw.githubusercontent.com/bellard/quickjs/master/quickjs.c"&gt;Hand-written&lt;/a&gt; (this is a single large file)&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/bellard/quickjs/commit/91459fb6723e29e923380cec0023af93819ae69d#diff-ead07c84baac57a9542f388a07a2a5209456ce790b04251bc9bd7d179ea85cb1R84"&gt;2019&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/svaarala/duktape"&gt;DuktapeJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5, some parts of ES6/ES7&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/svaarala/duktape/blob/master/src-input/duk_js_executor.c"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/svaarala/duktape/blob/master/src-input/duk_js_compiler.c"&gt;Hand-written&lt;/a&gt;, notably with no AST. It just directly compiles to its own bytecode.&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://duktape.org/download.html"&gt;2013&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/engine262/engine262"&gt;engine262&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: 100% spec compliance&lt;/li&gt;
&lt;li&gt;Implementation language: JavaScript&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/engine262/engine262/blob/14f50592362d889289e133fff4200e8e304c995a/src/runtime-semantics/IfStatement.mjs"&gt;AST interpreter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/engine262/engine262/blob/main/src/parser/ExpressionParser.mjs"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jerryscript-project/jerryscript"&gt;JerryScript&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: C&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/jerryscript-project/jerryscript/blob/master/jerry-core/vm/vm.c"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/jerryscript-project/jerryscript/blob/master/jerry-core/parser/js/js-parser.c"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/jerryscript-project/jerryscript/releases/tag/v1.0"&gt;2016?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibJS"&gt;Serenity's LibJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: &lt;a href="https://libjs.dev/test262/"&gt;Progressing toward compliance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Implementation language: C++&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibJS/Bytecode"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibJS/Parser.cpp"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Might also work outside of Serenity but documentation on building/running it on Linux is hard to find.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dop251/goja"&gt;goja&lt;/a&gt;: JavaScript interpreter for Go&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: Go&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/dop251/goja/blob/master/vm.go"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/dop251/goja/blob/master/parser/statement.go"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/dop251/goja/graphs/contributors"&gt;2017?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/robertkrimen/otto"&gt;otto&lt;/a&gt;: JavaScript interpreter for Go&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: Go&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/robertkrimen/otto/blob/373ff54384526e8336b5b597619d0923a4a83ae0/cmpl_evaluate_expression.go#L183"&gt;AST interpreter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/robertkrimen/otto/blob/master/parser/statement.go"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/robertkrimen/otto/graphs/contributors"&gt;2012?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: The AST interpreter-only implementation might suggest this implementation is slower than Goja. I don't have benchmarks for that.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paulbartrum/jurassic"&gt;Jurassic&lt;/a&gt;: JavaScript parser and interpreter for .NET&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: C#&lt;/li&gt;
&lt;li&gt;Runtime: Compiles to &lt;a href="https://github.com/paulbartrum/jurassic/blob/ee6f4fa17e6205e15412a214b24d7575b0bd461c/Jurassic/Compiler/MethodGenerator/GlobalOrEvalMethodGenerator.cs#L139"&gt;.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/paulbartrum/jurassic/blob/master/Jurassic/Compiler/Parser/Parser.cs"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/paulbartrum/jurassic/graphs/contributors"&gt;2011?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sebastienros/jint"&gt;Jint&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5, most of ES6/7/8&lt;/li&gt;
&lt;li&gt;Implementation language: C#&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/sebastienros/jint/blob/main/Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs"&gt;AST interpreter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/sebastienros/esprima-dotnet/blob/main/src/Esprima/JavascriptParser.cs"&gt;Hand-written via Esprima.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/sebastienros/jint/graphs/contributors"&gt;2014?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: Thanks &lt;a href="https://news.ycombinator.com/user?id=fowl2"&gt;fowl2&lt;/a&gt; for mentioning!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nilproject/NiL.JS"&gt;NiL.JS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES6&lt;/li&gt;
&lt;li&gt;Implementation language: C#&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/nilproject/NiL.JS/blob/develop/NiL.JS/Expressions/Assignment.cs"&gt;AST interpreter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/nilproject/NiL.JS/blob/develop/NiL.JS/Core/Parser.cs"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/nilproject/NiL.JS/graphs/contributors"&gt;2014?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NeilFraser/JS-Interpreter"&gt;Neil Fraser's JS-Interpreter&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: JavaScript&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/NeilFraser/JS-Interpreter/blob/master/interpreter.js"&gt;AST interpreter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/NeilFraser/JS-Interpreter/blob/master/acorn.js"&gt;Hand-written, uses Acorn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/NeilFraser/JS-Interpreter/graphs/contributors"&gt;2014?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BeRo1985/besen"&gt;BESEN&lt;/a&gt;: Bytecode VM and JIT compiler in Object Pascal&lt;ul&gt;
&lt;li&gt;Support: ES5&lt;/li&gt;
&lt;li&gt;Implementation language: Object Pascal&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/BeRo1985/besen/blob/master/src/BESENCode.pas"&gt;Bytecode VM&lt;/a&gt; with &lt;a href="https://github.com/BeRo1985/besen/blob/master/src/BESENCodeJITx86.pas"&gt;JIT for x86&lt;/a&gt; and &lt;a href="https://github.com/BeRo1985/besen/blob/master/src/BESENCodeJITx64.pas"&gt;x86_64&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/BeRo1985/besen/blob/master/src/BESENParser.pas"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/BeRo1985/besen/graphs/contributors"&gt;2015?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These last few are not toys but they are also more experimental or, in
AssemblyScript's case, not JavaScript.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/boa-dev/boa"&gt;boa&lt;/a&gt;: JavaScript interpreter for Rust&lt;ul&gt;
&lt;li&gt;Support: &lt;a href="https://boa-dev.github.io/boa/test262/"&gt;Unclear&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Implementation language: Rust&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/boa-dev/boa/tree/master/boa/src/vm"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/boa-dev/boa/tree/master/boa/src/syntax/parser"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;First release: &lt;a href="https://github.com/boa-dev/boa/releases/tag/v0.2.0"&gt;2019?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/AssemblyScript/assemblyscript"&gt;AssemblyScript&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Subset of TypeScript&lt;/li&gt;
&lt;li&gt;Implementation language: AssemblyScript subset of TypeScript&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/AssemblyScript/assemblyscript/blob/main/src/compiler.ts"&gt;webassembly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/AssemblyScript/assemblyscript/blob/main/src/parser.ts"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nickmain/kawa-scheme/tree/master/gnu/ecmascript"&gt;JavaScript in Kawa Scheme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wingolog.org/archives/2009/02/22/ecmascript-for-guile"&gt;JavaScript in GNU Guile Scheme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ReevaJS/reeva"&gt;ReevaJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES5 (with exceptions)&lt;/li&gt;
&lt;li&gt;Implementation language: Kotlin&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/ReevaJS/reeva/blob/master/src/main/kotlin/com/reevajs/reeva/interpreter/Interpreter.kt"&gt;Stack machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/ReevaJS/reeva/blob/master/src/main/kotlin/com/reevajs/reeva/parsing/Parser.kt"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="research-implementations"&gt;Research Implementations&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/higgsjs/Higgs"&gt;Higgs&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: D&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/higgsjs/Higgs/blob/master/source/runtime/vm.d"&gt;Bytecode VM&lt;/a&gt; and &lt;a href="https://github.com/higgsjs/Higgs/tree/master/source/jit"&gt;JIT compiler on x64&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/higgsjs/Higgs/blob/master/source/parser/parser.d"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tugawa/ejs-new"&gt;eJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: Java&lt;/li&gt;
&lt;li&gt;Runtime: Bytecode VM&lt;/li&gt;
&lt;li&gt;Parser: ANTLR&lt;/li&gt;
&lt;li&gt;Notes: eJS is a framework to generate JavaScript VMs that are specialised for applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/endojs/Jessie"&gt;Jessie&lt;/a&gt;: safe subset of JavaScript non-exploitable smart contracts&lt;ul&gt;
&lt;li&gt;Support: some subset of ES2017&lt;/li&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://github.com/agoric-labs/jessica"&gt;https://github.com/agoric-labs/jessica&lt;/a&gt; for more info.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/b9org/b9"&gt;https://github.com/b9org/b9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.defensivejs.com/"&gt;https://www.defensivejs.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="note"&gt;
  Thanks to &lt;a href="https://twitter.com/smarr"&gt;@smarr&lt;/a&gt; for contributing eJS, Higgs, and b9!
&lt;/p&gt;&lt;h3 id="notable-abandoned"&gt;Notable Abandoned&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/DigitalMars/DMDScript"&gt;DMDScript&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: D&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/DigitalMars/DMDScript/blob/master/engine/source/dmdscript/opcodes.d#L15"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/DigitalMars/DMDScript/blob/master/engine/source/dmdscript/parse.d"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: It's possible this is commercially maintained by DigitalMars but I'm not sure. There are also references in this repo to another C++ implementation of DMDScript that may be commercial. Thanks to &lt;a href="https://twitter.com/moon_chilled"&gt;@moon_chilled&lt;/a&gt; for mentioning!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/toshok/echojs"&gt;EchoJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: JavaScript&lt;/li&gt;
&lt;li&gt;Runtime: Native through LLVM&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/toshok/esprima/tree/e4445c9cc2530d672c4e9f68f5e2a53673b57af0"&gt;Hand-written via Esprima&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/haileys/twostroke"&gt;twostroke&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: Ruby&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/haileys/twostroke/blob/master/lib/twostroke/runtime/vm_frame.rb"&gt;Bytecode VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/haileys/twostroke/blob/master/lib/twostroke/parser.rb"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/progval/rpython-langjs"&gt;PyPy-JS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: RPython&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/progval/rpython-langjs/blob/master/js/jscode.py"&gt;RPython&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/progval/rpython-langjs/blob/master/js/jsgrammar.txt"&gt;EBNF parser generator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jterrace/js.js/"&gt;js.js&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: JavaScript&lt;/li&gt;
&lt;li&gt;Runtime: Too scared to look at the gigantic files in this repo.&lt;/li&gt;
&lt;li&gt;Parser: Ditto.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fholm/IronJS"&gt;IronJS&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: ES3&lt;/li&gt;
&lt;li&gt;Implementation language: F#&lt;/li&gt;
&lt;li&gt;Runtime: .NET through &lt;a href="https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/dynamic-language-runtime-overview"&gt;DLR&lt;/a&gt;, I think.&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/fholm/IronJS/blob/master/Src/IronJS/Compiler.Parser.fs"&gt;Hand-written&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/polydojo/jispy"&gt;jispy&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;Support: Unclear&lt;/li&gt;
&lt;li&gt;Implementation language: Python&lt;/li&gt;
&lt;li&gt;Runtime: &lt;a href="https://github.com/polydojo/jispy/blob/master/jispy.py#L730"&gt;AST interpreter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Parser: &lt;a href="https://github.com/polydojo/jispy/blob/master/jispy.py#L311"&gt;Unclear&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://metacpan.org/pod/JE#Simple-Use"&gt;JE: Pure-Perl JavaScript Engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.racket-lang.org/javascript/index.html"&gt;Dave Herman's JavaScript for PLT Scheme&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="notable-toy-implementations"&gt;Notable toy implementations&lt;/h3&gt;&lt;p&gt;Great for inspiriration if you've never implemented a language before.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/timruffles/js-to-c"&gt;js-to-c&lt;/a&gt;: A JavaScript to C compiler, written in C&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mras0/mjs"&gt;mjs&lt;/a&gt;: AST interpreter for not just ES5 or even ES3 but also ES1&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gojisvm/gojis"&gt;gojis&lt;/a&gt;: AST interpreter in Go&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DelSkayn/toyjs"&gt;tojs&lt;/a&gt;: Bytecode VM in Rust&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CrimsonAS/v2"&gt;v2&lt;/a&gt;: Bytecode VM in Go&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/githubyang/SparrowJS"&gt;SparrowJS&lt;/a&gt;: AST interpreter in C++&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/eatonphil/jsc"&gt;jsc&lt;/a&gt;: My own experiment compiling JavaScript to C++/libV8&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;New post is up! Enumerating and analyzing 40+ non-V8 JavaScript implementations; of course with links to source code and parser &amp;amp; runtime/backend decisions.&lt;br /&gt;&lt;br /&gt;I hope you enjoy learning about JavaScript engines as much as I did. 😁&lt;a href="https://t.co/dEX06WU38f"&gt;https://t.co/dEX06WU38f&lt;/a&gt; &lt;a href="https://t.co/AoYScphG6m"&gt;pic.twitter.com/AoYScphG6m&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1440436962305789952?ref_src=twsrc%5Etfw"&gt;September 21, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Tue, 21 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/javascript-implementations.html</guid></item><item><title>Digital murder? We knew it was coming</title><link>https://stop.zona-m.net/2021/09/digital-murder-we-knew-it-was-coming/</link><description>&lt;p&gt;Don&amp;rsquo;t say you were not warned.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 20 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/digital-murder-we-knew-it-was-coming/</guid></item><item><title>Why do Webdevs keep trying to kill REST?</title><link>https://www.swyx.io/client-server-battle</link><description>How I broke out of the tiresome REST vs GraphQL debate - by realizing that the real battle is between Smart Clients and Smart Servers!</description><author>swyx's site RSS Feed</author><pubDate>Mon, 20 Sep 2021 17:46:38 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/client-server-battle</guid></item><item><title>How to replace estimations and guesses with a Monte Carlo simulation</title><link>https://lucasfcosta.com/blog/monte-carlo-forecasts/</link><description>There are many ways of estimating how long a software project will take. All of them are a waste of time.

Some prefer to spend days analysing and planning their changes so that they can estimate mor...</description><author>Lucas F. Costa - Blog</author><pubDate>Mon, 20 Sep 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://lucasfcosta.com/blog/monte-carlo-forecasts/</guid></item><item><title>Structural pattern matching in Python 3.10</title><link>https://benhoyt.com/writings/python-pattern-matching/</link><description>A critical but informative look at the new structural pattern matching feature in Python 3.10, with real-world code examples.</description><author>Ben Hoyt's technical writing</author><pubDate>Mon, 20 Sep 2021 10:30:00 GMT</pubDate><guid isPermaLink="true">https://benhoyt.com/writings/python-pattern-matching/</guid></item><item><title>Paper Summary: Defending Against Neural Fake News</title><link>https://blog.lukesalamone.com/posts/grover-paper-summary/</link><description>&lt;p&gt;&lt;a href="https://arxiv.org/abs/1905.12616"&gt;&lt;em&gt;Defending Against Neural Fake News&lt;/em&gt;&lt;/a&gt; by Zellers et al. presents a model for controllable text generation called Grover. This model can be used to create highly believable computer-generated news articles. The authors present this paper as a method of detecting and preventing the spread of fake news. They claim their model is 92% accurate at detecting fake news stories, partially due to artifacts that generators include in the generated text.&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Mon, 20 Sep 2021 04:13:09 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/grover-paper-summary/</guid></item><item><title>How to Evaluate Startup Offers - A Beginner's Guide</title><link>https://faingezicht.com/articles/2021/09/20/evaluating-startup-offers/</link><description>&lt;small&gt;TL;DR, &lt;a href="https://docs.google.com/spreadsheets/d/1AW0WVzsaEQOan_oHHIFHO0lAfpjtATsywe18hvh1H3Q/edit?usp=sharing"&gt;here&lt;/a&gt; is a sample spreadsheet you can duplicate to evaluate your offers.&lt;/small&gt;

&lt;small&gt;&lt;em&gt;Este ensayo también está disponible &lt;a href="/articles/2021/09/20/evaluating-startup-offers-es/"&gt;en español&lt;/a&gt;.&lt;/em&gt;&lt;/small&gt;

I recently &lt;a href="/2021/04/16/heres-to-the-crazy-ones/"&gt;decided to leave my big tech job&lt;/a&gt; at Apple, where I had been for 6 years, to join a startup called &lt;a href="https://www.vouch.us/"&gt;Vouch&lt;/a&gt; (we're &lt;a href="https://www.vouch.us/careers"&gt;hiring&lt;/a&gt;!). As I was considering the switch, multiple people told me I was making a big mistake, losing out on both job stability and compensation. Those people had no idea what they were talking about. If you're joining a startup for money, you're doing it for the wrong reasons, but that doesn't mean that money shouldn't factor into your decision.

You can build financial models around salaries and equity, and I'll show you how to do that below, but remember your job experience will give you _&lt;a href="https://www.investopedia.com/terms/i/intangibleasset.asp"&gt;intangibles&lt;/a&gt;_ that you can't represent in a spreadsheet. A FAANG offer will win on sheer numbers every time. If you have flexibility, pick your next job based on how it can help you get to where you want to end up. Only you can tell whether you value the stability of a brand name or learning how to navigate a bureaucracy more or less than seeing a new business come to life or experiencing hypergrowth. The people you'll work with and the network you'll build matter a lot, too. Make sure you have a clear north star, or you'll have a much harder time making a choice. For example, when I &lt;a href="/articles/2021/04/26/new-beginnings/"&gt;chose to join Vouch&lt;/a&gt;, I tried to maximize my learning about the venture ecosystem and how startups operate.

If you are considering taking a role at a startup, the framework below should give you basic tools to compare startup offers, but it doesn't pretend to give you exact answers.

### 1. How venture capital funding works

When you hear that a startup has raised a round with an eye-popping valuation, all that means is that there was a venture capital (VC) firm willing to buy a portion of their business for cash, and the startup was willing to take their deal. The figures they agree to imply the company's new valuation. Startups use other financial instruments to raise capital, but the standard venture fundraise is simply that: an inflow of dollars matched by the issue of new shares of **preferred stock**, creating value almost out of thin air.

To illustrate the mechanics, let's imagine[^1] a company raising a seed round from a single firm:

* Before the deal, the startup has 10M shares outstanding.
* A VC firm values the company at $5M _pre-money_ and agrees to invest $1M on that price. This effectively gives the company a _post-money_ valuation of $6M.
* The valuation implies a price for the company's outstanding shares. Since _pre-money_ the 10M shares are worth $5M, the price is $5M / 10M shares = $0.50/share
* In exchange for the $1M, the company will issue new shares and hand them to the VC. The number of shares issued is mostly a matter of accounting: ($1M) / ($0.50/share) = 2M new shares. Now the company has 10M + 2M = 12M shares outstanding.
* At this point, an additional set of **common shares** could be issued to create/restock the &lt;a href="https://carta.com/blog/how-to-size-employee-option-pool/"&gt;employee option pool&lt;/a&gt;. These shares would also factor into pricing but we'll ignore them to keep the analysis simple.

For shareholders, the fundraise is usually good news. However, they now own a smaller piece of a larger pie. When your percentage of ownership is decreased via the issuance of new shares you're experiencing **dilution**. If you were the lucky holder of 100,000 shares, you used to own 1% of the company. After this round, you only own 0.83%, and future rounds of funding will further dilute your shares.

There are many nuances to startup funding[^2], but the basics described above on how equity is priced and how those prices evolve throughout a company's life are a big part of the puzzle. &lt;a href="https://www.failory.com/blog/startup-failure-rate"&gt;Most startups fail&lt;/a&gt;, but if you're lucky, you'll join one of the few that is not worth $0 in the long run.

### 2. Compensation package structure and options

On top of your salary and maybe a signing bonus, startups offer equity as part of their compensation. Employee ownership is ingrained in the startup mentality for a reason. Stock compensation aligns incentives, translating employees' hard work to potential shared gains. At the same time, it allows companies to offer the prospect of a big payout in the future in exchange for a smaller paycheck today.

While bigger companies might offer stock in the form of RSUs, which are functionally similar to the public company shares you could buy on a brokerage account, smaller startups give their employees &lt;a href="https://www.investopedia.com/terms/o/option.asp"&gt;stock options&lt;/a&gt; instead. These securities come in many different flavors, mostly differing on tax rules, but to keep things simple we'll assume we're talking about &lt;a href="https://carta.com/blog/what-are-incentive-stock-options/"&gt;incentive stock options (ISOs)&lt;/a&gt;. An ISO gives the holder the right to buy a share of common stock in the future, at a prespecified price. This is called the **strike price**, and it comes from an independent appraisal of the fair market value (FMV) of the stock. It is interchangeably called the **409a valuation**, after the &lt;a href="https://en.wikipedia.org/wiki/Internal_Revenue_Code_section_409A"&gt;section&lt;/a&gt; in the IRS code that regulates it. The strike price you're offered will always be a fraction of the most recent preferred share price, set so that the current value of common stock makes your options nominally worth $0, ensuring no tax events are triggered when the employee receives them.

Using the example from before, the $0.50 preferred stock price paid by the investors might translate to a strike price of $0.15[^3]. At this price, when a prospective employee is offered $50k in equity she'd get a grant for 100,000 options. If the company does poorly, those options might end up being &lt;a href="https://www.investopedia.com/terms/u/underwater.asp"&gt;underwater&lt;/a&gt; and worthless, but let's assume the company does well, and that a few years down the line their shares are priced at $50 (a 100x increase!). This lucky employee has the right to **exercise** her options, redeeming shares by paying $15,000 ($0.15 * 100,000 options) to get $5M worth of stock ($50 * 100,000 shares).

But we're getting ahead of ourselves. Even before the equity gets to appreciate, it needs to **vest**. Big equity numbers sound exciting to a hypothetical new-joiner, but getting a grant upon signing her offer doesn't mean that equity will immediately be transferred to her. Once again this is about incentive alignment. If she immediately got her equity, she could quit the next day. To ensure people are committed for the long run, the industry-standard vesting schedule spreads the distribution over four years, with a one year **cliff**. During the first year of employment, the employee doesn't get any equity. After hitting the cliff at the one year mark, the first 25% of the grant vests, and every month after that another 2.0833% (75% over 36 months) hits her account.

The terms discussed above vary a bit from company to company, and some might be cagier than others about giving up this information during negotiation. Two valuable terms to ask about are &lt;a href="https://www.cooleygo.com/early-exercisable-stock-options-what-you-need-to-know/"&gt;early exercise&lt;/a&gt; and exercise windows[^4]. In essence, at the time you leave the company, you'll have a limited amount of time to shell out cash to turn your vested options into shares. The standard window used to be &lt;a href="https://zachholman.com/posts/fuck-your-90-day-exercise-window/"&gt;90 days after leaving&lt;/a&gt;, although some companies have started to change to favor employees with multi-year windows. These terms will have a big impact on net compensation.

Transparency in valuation and prices is a signal of a well-run operation, an open culture, and a healthy business. If they don't share those numbers it might mean things aren't going that well. A dollar number on an equity package means nothing without the broader context. You should always ask for specific numbers for:

* Total shares outstanding
* Last preferred price
* 409a/strike price
* Number of options in your grant

Armed with these values, and setting aside a lot of complexity about taxation, we can move on to the calculations that will allow us to compare various offers.

### 3. Expected value
If you have taken a basic probability class, feel free to skip this section.

Expected value is what it sounds like: a way to measure the potential outcome of something that has an unknown value today — such as the valuation of a startup in 5, 10, or 20 years.

Let’s use a coin flip as an example. If it comes up heads, you get $100. If it comes up tails, you get $0. The expected value (denoted as E[x]) is defined as the sum of the outcomes weighted by the probability that each one might occur:

    E[x] = (½ * $0) + (½ * $100) = $50

True, under this payout scheme no one bet over a coin flip will ever reward you with $50, but the anticipated value is still clearly given by this weighted sum. If you repeat the coin flip a thousand times on average you'd end up at $50, as &lt;a href="https://en.wikipedia.org/wiki/Law_of_large_numbers"&gt;the law of large numbers&lt;/a&gt; tells us. More complex events can be modeled similarly. If you toss a standard 6-sided die, the expected value to be rolled is 3.5, even if no roll can ever have its outcome suspended between 3 and 4:

    (⅙ * 1) + (⅙ * 2) + (⅙ * 3) + (⅙ * 4) + (⅙ * 5) + (⅙ * 6) = 3.5

Applying this idea, you can estimate the future value of anything: the expected rewards from pulling the arm on a slot machine, the return on buying a new house, the price of Apple stock tomorrow, or the potential payoff of your startup IPOing. The nuance here is that unlike a coin flip where the odds are well understood and discrete (half the time you'll see heads, and half the time tails) and the payoffs are well defined ($0 or $100) most interesting problems involve more uncertainty. With a slot machine, the player doesn't really know the payoff structure, nor the probability distribution, even though they're both baked into the game by its designer. In the housing, stock, and venture markets, not only are the payoffs and their probability distributions unknown to us, but they are not defined by anyone else. They are emergent phenomena.

Uncertainty about uncertainty is the reason why there's a whole industry devoted to analyzing patterns in the noise. Just like there is expected value, there's also a way to measure &lt;a href="https://www.investopedia.com/terms/r/risk.asp"&gt;expected risk&lt;/a&gt;, which I'll leave for a different blog post.

### 4. Discounted cash flow analysis

If you have taken a basic finance class, feel free to skip this section.

Evaluating any financial project requires accounting for &lt;a href="https://www.investopedia.com/terms/t/timevalueofmoney.asp"&gt;the time value of money&lt;/a&gt;. On top of the uncertainty aspects discussed earlier, the payment schedule for most startups is asymmetrical: You’re accepting less compensation today for the promise of a potential payout later. Setting aside any liquidity constraints (e.g. wanting to save $X/year for a down payment, or needing $Y/month to pay your student loans and rent), we need some way to account for that time difference to calculate the real value of any offer on the table.

In short, the problem we're solving is that $1 today is worth more than $1 tomorrow. With $1 today, you can pay off $1 of debt, and avoid accruing interest on it; or you can put it in the bank and earn that interest yourself. The process of laying out projects side by side to compare their returns is called &lt;a href="https://www.investopedia.com/terms/d/dcf.asp"&gt;discounted cash flow&lt;/a&gt; analysis, or DCF.

For example, assuming there's a bank willing to pay you a rate of r = 1% on your cash, you know that depositing $100 today means having $101 next year. The math also works the other way around, though, and we can see that if

    ($100 today) * (1 + 1%) = $101 next year

then

    $100 today =  ($101 next year) / (1 + 1%)

Abstractly,

    $X today = $X next year/(1+r)

Dividing by that factor is called discounting. You can use the same logic to discount further into the future, dividing each cash flow by (1+r) again each period.

    $X next year/(1+r) = ($X in two years/(1+r))/(1+r) = $X in two years/(1+r)^2

The further out you go the more the discounting will eat away at the value of those future cash flows. Picking &lt;a href="https://twitter.com/avyfain/status/1372198641910288386"&gt;the right value for r&lt;/a&gt; is a science of its own, but in some ways it represents the alternative uses you'd have for that cash if you had it today.

For example, let's imagine two startups. One offers you a higher starting salary and steady pay increases, while the other lets you know they won't give you a raise after your first year, promising bigger payouts down the line.

&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;
   &lt;/td&gt;
   &lt;td&gt;Year 1
   &lt;/td&gt;
   &lt;td&gt;Year 2
   &lt;/td&gt;
   &lt;td&gt;Year 3
   &lt;/td&gt;
   &lt;td&gt;Year 4
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Offer A
   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$110&lt;/p&gt;

   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$115&lt;/p&gt;

   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$120&lt;/p&gt;

   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$125&lt;/p&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Offer B
   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$100&lt;/p&gt;

   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$100&lt;/p&gt;

   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$120&lt;/p&gt;

   &lt;/td&gt;
   &lt;td&gt;&lt;p style="text-align: right;"&gt;
$150&lt;/p&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

If we choose r = 5%, for example, we can discount those cash flows to get to each offers' &lt;a href="https://www.investopedia.com/terms/n/npv.asp"&gt;net present value&lt;/a&gt; (NPV):

    NPV(A) = $110 + $115/(1+r) + $120/(1+r)^2 + $125/(1+r)^3 = $415.57
    NPV(B) = $100 + $100/(1+r) + $120/(1+r)^2 + $150/(1+r)^3 = $413.01

If we naively add up each stream of payments they both add up to $470, but the pay structure is very different and (merely financially) you'd be better off taking A's offer than B's. The NPV you end up with depends entirely on the value you pick for r.

### Evaluating your offer

Now let's put it all together, getting into &lt;a href="https://www.investopedia.com/terms/s/scenario_analysis.asp"&gt;scenario&lt;/a&gt; and &lt;a href="https://www.investopedia.com/terms/s/sensitivityanalysis.asp"&gt;sensitivity&lt;/a&gt; analysis.

Let's go back to hypothetical companies, but now instead of just assuming sure exits, let's model out these payments based on expected values, including dilution and exercise costs. This is where scenario analysis comes in. You can guesstimate the odds of potential outcomes based on what you know about the market - just make sure to always err on the side of being too negative, not too optimistic.

You can find a spreadsheet you can copy and edit to plug in your own values &lt;a href="https://docs.google.com/spreadsheets/d/1AW0WVzsaEQOan_oHHIFHO0lAfpjtATsywe18hvh1H3Q/edit?usp=sharing"&gt;here&lt;/a&gt;.

![Equity Spreadsheet](https://cdn.faingezicht.com/evaluating_startup_offers/equity_tool.png
 "Equity Spreadsheet")

The values in the light blue represent offer terms that will be given to you by the person you're negotiating with (this is why transparent terms matter). The values in red can be filled-in based on what you learned in the interviews, and your own research about the market. How big can this startup really get? Lastly, the values in yellow are rough rules of thumb around dilution which will depend on what round the company is currently on, and overall market sentiment.

You can build a table like this one to model scenarios for the offers you have at hand. For simplicity, the sheet linked above makes assumptions across offers that don't need to hold. For example, all these fictional startups exit after 4 years, but you could model uncertainty around that as well. Take the sheet as a template and update it to fit your needs. You could further extend the calculations to include dilution from additional funding rounds, secondary sales, &lt;a href="https://review.firstround.com/The-Right-Way-to-Grant-Equity-to-Your-Employees"&gt;refresh/evergreen grants&lt;/a&gt;, etc. Ultimately, what we're looking for is to boil down each offer to a set of future expected cash flows, which we can then discount at different rates.

In the sample linked above, for example, Foo Inc gives a very nice salary and bonus up front but meager exit potential, while Bar.ly and Baz.io's compensation weighs a lot more on the potential exit. On paper, it looks like Bar.ly isn't offering very compelling compensation.

![NPV Sensitivity Analysis](https://cdn.faingezicht.com/evaluating_startup_offers/NPV_sensitivity_analysis.png
 "NPV Sensitivity Analysis")

Only your gut can tell you which one feels right from here on. VCs get to make many small bets with their fund. That gives them a lot more leeway to be wrong on multiple counts while still turning a profit. When picking your new job you are putting all your eggs in one basket, so you better pick one where the bottom is unlikely to drop out. This is why choosing a startup just for the financial possibilities is a bad idea. If you have a couple of options that check the boxes on what you're looking for, and you're equally excited about them, only then it comes down to the financials. If the equity is worth $0, will you have learned the things that you want to learn? Will you meet the right people? Will you work closely enough with the leadership team? These won't show up in any financial model, but probably matter a lot more in the long arc of your career.

### A few thoughts on negotiating

Most startups are willing to trade equity for cash. If you need to pay off student loans, or are otherwise cash-strapped, you can ask for more cash and less equity. If you can live on a smaller paycheck, and truly believe in the vision, you can ask for more equity and take on that risk. If you feel like you have leverage, asking for two offers, one with high equity and low salary and vice versa, can give you a hint at how they value their own stock. Asking for more equity is often easier than more cash, depending on their funding and option pool situation, but having both offers can give you an idea of what an all-cash offer would look like for this startup, which is a valuable data point in itself.

I'll leave the rest of my negotiating advice for a different post, but if you're looking for help on that front I'd recommend you read &lt;a href="https://www.kalzumeus.com/2012/01/23/salary-negotiation/"&gt;Patrick McKenzie's guide&lt;/a&gt;.

Do you have your own modeling suggestions, negotiation tips, startup advice, general feedback, or any questions? &lt;a href="/contact/"&gt;I'd love to hear it&lt;/a&gt;.

&lt;small&gt;_This is the third in a series I’m writing about my recent career transition out of Apple. See part one, on &lt;a href="/2021/04/16/heres-to-the-crazy-ones/"&gt;things I learned in my time at Apple&lt;/a&gt;, and part two, on &lt;a href="/articles/2021/04/26/new-beginnings/"&gt;how I picked my role at Vouch&lt;/a&gt;. Make sure to check back in a few weeks for upcoming pieces on **things to ask in interviews with startups**, and **tips on how to make your job transition as smooth as possible**, etc._&lt;small&gt;

Thanks to Hannah Doherty, Max Faingezicht, Moi Stern, and Sandeep Paruchuri for their feedback on drafts of this post.

&lt;hr /&gt;

&lt;small&gt;&lt;em&gt;Photo: Big Wheel, Las Vegas, by me. Previously posted on &lt;a href="/photos/2021/07/10/vaxd-travel/"&gt;Vax'd Travel&lt;/a&gt;.&lt;/em&gt;&lt;/small&gt;

&lt;hr /&gt;

[^1]:
    Lifted from Y Combinator's &lt;a href="https://www.ycombinator.com/library/4A-a-guide-to-seed-fundraising"&gt;fundraising guide&lt;/a&gt;

[^2]:
     If you want to scratch the surface, &lt;a href="https://bookshop.org/books/venture-deals-be-smarter-than-your-lawyer-and-venture-capitalist/9781119594826"&gt;Brad Feld's Venture Deals&lt;/a&gt; is the book to get started.

[^3]:
     This implies a FMV of 30% of the latest round's valuation. While the appraisal is a much more complex process, depending on the round, ⅕ to ⅓ of the preferred price is a standard rule of thumb to follow.

[^4]:
     Both of these are out of scope for this blog post, but the former can have a big impact on your taxes, while the latter might make your options worth even less than you thought if your financial situation doesn't allow you to acquire your stock.</description><author>Avy Faingezicht</author><pubDate>Mon, 20 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/articles/2021/09/20/evaluating-startup-offers/</guid></item><item><title>Writing things from scratch and containers - 123dev #15</title><link>https://justingarrison.com/123dev/2021-09-20-123dev-15/</link><description>&lt;img alt="" src="https://justingarrison.com/img/123dev/15-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="planning-work"&gt;Planning work&lt;/h2&gt;
&lt;p&gt;I’m sure many of you saw this gif in various places recently. There’s lots of similarities that are obvious from a software engineering perspective. To me it shows how difficult a re-write really is.&lt;/p&gt;
&lt;h2 id="estimating"&gt;Estimating&lt;/h2&gt;
&lt;p&gt;“Most people overestimate what they can achieve in a year and underestimate what they can achieve in ten years.”&lt;/p&gt;
&lt;p&gt;The same is true for what I can achieve in an afternoon and what I can achieve in a week.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 20 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-20-123dev-15/</guid></item><item><title>Art Sunday: Untitled artwork I plan to print out and hang above the fireplace</title><link>http://michaelhoney.com/writing/2021/9/20/art-sunday-untitled-artwork-i-plan-to-print-out-and-hang-above-the-fireplace</link><description>&lt;figure class="
              sqs-block-image-figure
              intrinsic
            "&gt;
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                &lt;img alt="" height="2500" src="https://images.squarespace-cdn.com/content/v1/5244d189e4b0136e0a44003d/1632091936219-OGKG0O5SF6ARY5I9AJT6/colorscales_2021-9-19-18-11-0.png?format=1000w" width="2500" /&gt;

            
          
        
          
        

        
      
        &lt;/figure&gt;</description><author>Michael Honey: Writing - michaelhoney.com</author><pubDate>Mon, 20 Sep 2021 01:55:16 GMT</pubDate><guid isPermaLink="true">http://michaelhoney.com/writing/2021/9/20/art-sunday-untitled-artwork-i-plan-to-print-out-and-hang-above-the-fireplace</guid></item><item><title>Speedrunning Computer Games History with a 6yr Old - Part 4</title><link>https://duncanlock.net/blog/2021/09/19/speedrunning-computer-games-history-with-a-6yr-old-part-4/</link><description>&lt;section id="preamble"&gt;&lt;figure class="image-block"&gt;&lt;img alt="Hand-drawn map of the Kingdom of Hyrule." height="500px" src="https://duncanlock.net/images/posts/speedrunning-computer-games-history-with-a-6yr-old-part-4/IMG_20210919_231752-resized.webp" width="500px" /&gt;
&lt;figcaption&gt;Figure 1. Our hand-drawn map of the Kingdom of Hyrule. I drew a 2x1 grid in Inkscape and printed it out, then we filled it in as we went along.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;It&amp;#8217;s been about six months since the last update. We&amp;#8217;ve mostly been camping, playing outside and generally enjoying the summer - although we have also squeezed in some gaming, here and&amp;nbsp;there.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_favourite_games_so_far"&gt;Favourite Games So&amp;nbsp;Far&lt;/h2&gt;&lt;p&gt;We spent almost all of our game time playing Legend of Zelda. It&amp;#8217;s a towering achievement&amp;#8201;&amp;#8211;&amp;#8201;a &lt;em&gt;truly&lt;/em&gt; great game. I&amp;#8217;m amazed that this thing is from 1986&amp;#8201;&amp;#8211;&amp;#8201;it doesn&amp;#8217;t feel like&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s also a great example of a &lt;em&gt;different kind&lt;/em&gt; of co-op game. One where you play together by collaborating: discussing what to do &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; where to go next, how to tackle things, etc&amp;#8230;&amp;#8203; We also took turns with the controller, with me often doing the &amp;#8220;hard&amp;nbsp;bits&amp;#8221;.&lt;/p&gt;
&lt;figure class="image-block"&gt;&lt;img alt="Original Blue Darknut sprite from Legend of Zelda." height="200px" src="https://duncanlock.net/images/posts/speedrunning-computer-games-history-with-a-6yr-old-part-4/blue-darknut.png" width="200px" /&gt;
&lt;figcaption&gt;Figure 2. Oh crap, Darknuts.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;We got pretty stuck for a few weeks trying to get past the blue Darknuts in the 5th level dungeon. I &lt;em&gt;almost&lt;/em&gt; caved and was &lt;em&gt;considering&lt;/em&gt; editing the save game file, rather than having the kid lose interest&amp;#8201;&amp;#8211;&amp;#8201;but it didn&amp;#8217;t come to&amp;nbsp;that.&lt;/p&gt;
&lt;p&gt;We &lt;em&gt;eventually&lt;/em&gt; completed it in the traditional, time-honoured way&amp;#8201;&amp;#8211;&amp;#8201;sitting on the sofa in our pyjamas on Saturday morning, banging our heads against the level, until we eventually cracked&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;There was less reading in Zelda than I was hoping - but what little there was, the kid did, which was good practice. I did most of the cartography, but the kid did do&amp;nbsp;some.&lt;/p&gt;
&lt;p&gt;It was great fun and also, I think, the kid learnt something about being persistent and working towards longer term&amp;nbsp;objectives.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_updates_changes_to_the_system"&gt;Updates &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Changes to the&amp;nbsp;System&lt;/h2&gt;&lt;section class="doc-section level-2"&gt;&lt;h3 id="_2021_09_04"&gt;2021-09-04&lt;/h3&gt;&lt;p&gt;I thought it was about time we started on some adventure games&amp;#8201;&amp;#8211;&amp;#8201;partly to help with reading practice, partly because they&amp;#8217;re&amp;nbsp;great.&lt;/p&gt;
&lt;section class="doc-section level-3"&gt;&lt;h4 id="_scummvm"&gt;ScummVM&lt;/h4&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Maniac_Mansion"&gt;Maniac Mansion&lt;/a&gt;&amp;nbsp;(1987)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_2021_09_18"&gt;2021-09-18&lt;/h3&gt;&lt;p&gt;We tentatively started having play-dates again - two so far. In preparation for this, I added some more co-op games. All of these games are 2 to 4 player co-op games, except for Gods &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Populous, which &lt;em&gt;I&lt;/em&gt; just wanted to&amp;nbsp;play.&lt;/p&gt;
&lt;p&gt;These are a little but of a leap in time from where we were, but I think we need to speed up anyway,&amp;nbsp;so&amp;#8230;&amp;#8203;&lt;/p&gt;
&lt;section class="doc-section level-3"&gt;&lt;h4 id="_nes"&gt;&lt;span class="caps"&gt;NES&lt;/span&gt;&lt;/h4&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Bomberman_II"&gt;Bomberman &lt;span class="caps"&gt;II&lt;/span&gt;&lt;/a&gt;&amp;nbsp;(1991)&lt;/li&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Salamander_(video_game)"&gt;Life Force&lt;/a&gt;&amp;nbsp;(1987)&lt;/li&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Chip_%27n_Dale_Rescue_Rangers_(video_game)"&gt;Chip &amp;#8216;n Dale Rescue Rangers&lt;/a&gt;&amp;nbsp;(1990)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-3"&gt;&lt;h4 id="_amiga_1"&gt;Amiga &lt;a class="footnote-ref" href="#_footnote_1" id="_footnoteref_1" title="View footnote 1"&gt;[1]&lt;/a&gt;&lt;/h4&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Gods_(video_game)"&gt;Gods&lt;/a&gt;&amp;nbsp;(1991)&lt;/li&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Golden_Axe"&gt;Golden Axe&lt;/a&gt;&amp;nbsp;(1989)&lt;/li&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Chaos_Engine"&gt;Chaos Engine&lt;/a&gt;&amp;nbsp;(1993)&lt;/li&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Populous_(video_game)"&gt;Populous&lt;/a&gt;&amp;nbsp;(1989)&lt;/li&gt;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Fidelity_Chessmaster_2100"&gt;The Fidelity Chessmaster 2100&lt;/a&gt;&amp;nbsp;(1988)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_ill_report_back_later"&gt;I&amp;#8217;ll report back&amp;nbsp;later&amp;#8230;&amp;#8203;&lt;/h2&gt;&lt;p&gt;We&amp;#8217;ve started on Maniac Mansion and that went &lt;span class="caps"&gt;OK&lt;/span&gt;, considering that it&amp;#8217;s a new type of game that the kid hasn&amp;#8217;t tried before - and I&amp;#8217;m making him do all the reading&amp;nbsp;😉.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ve also played a few games of Bomberman, which is as ever, loads of fun. I&amp;#8217;ll report back later on how we get&amp;nbsp;on.&lt;/p&gt;
&lt;hr /&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_footnotes_references"&gt;Footnotes &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;References&lt;/h3&gt;&lt;/section&gt;&lt;/section&gt;&lt;section class="footnotes"&gt;&lt;hr /&gt;&lt;ol class="footnotes"&gt;&lt;li class="footnote" id="_footnote_1"&gt;The &lt;a href="https://mega.nz/folder/gdozjZxL#uI5SheetsAd-NYKMeRjf2A/folder/5AgyxZCL"&gt;motherload of amiga games&lt;/a&gt;, in &lt;span class="caps"&gt;WHDL&lt;/span&gt; format. &lt;a class="footnote-backref" href="#_footnoteref_1" title="Jump to the first occurrence in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Mon, 20 Sep 2021 00:04:34 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/09/19/speedrunning-computer-games-history-with-a-6yr-old-part-4/</guid></item><item><title>Note-Making on Physical Books in Drafts</title><link>http://evantravers.com/articles/2021/09/19/note-making-on-physical-books-in-drafts/</link><description>&lt;p&gt;&lt;a href="https://docs.getdrafts.com/changelog/#280"&gt;Drafts 28&lt;/a&gt; has been released and with it the new Scan Document feature. Even before I got my hands on it, I knew that this feature is &lt;em&gt;huge&lt;/em&gt;… this is the missing link in getting notes and quotes from physical books into my digital &lt;a href="http://evantravers.com/articles/series/creativity-systems/"&gt;Creativity System&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The simplest version is already done: simply use the Scan Documents feature. From a new draft, hold the &lt;code&gt;+&lt;/code&gt; Button in the top left to select Scan Documents. Scan your document, and hey presto! The image is OCR'd and brought into a new draft.&lt;/p&gt;
&lt;p&gt;Of course this is Drafts, and so more &lt;a href="http://evantravers.com/articles/tags/automation/"&gt;automation&lt;/a&gt; is possible. You can use &lt;a href="https://actions.getdrafts.com/g/16J"&gt;the &amp;quot;Booknotes&amp;quot; family of Actions I wrote&lt;/a&gt; to build a &amp;quot;shelf&amp;quot; of books you are currently reading, then link scanned Drafts to your main booknote with an MLA styled link… or just use the &amp;quot;Insert MLA Citation&amp;quot; action I created to create a reference to your scanned note. It's up to you!&lt;/p&gt;
&lt;p&gt;I'm sure over the coming months the community will figure out even more clever ways to manage this, but I'm thrilled to have OCR in the Drafts toolbox. I'll probably start writing actions here to auto-fix some of the quirks of the scanning process… combining newlines into paragraphs, prefixing with &lt;code&gt;&amp;gt;&lt;/code&gt; for quotes… and honestly it'll probably cause me to buy some more physical books.&lt;/p&gt;
&lt;p&gt;I'm eyeing a couple of volumes I haven't started reading yet… excited to be able to capture notes and quotes without having to retype the whole passage.&lt;/p&gt;</description><author>trv.rs</author><pubDate>Sun, 19 Sep 2021 20:21:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/09/19/note-making-on-physical-books-in-drafts/</guid></item><item><title>Rejections, Resilience, and Rejoicing: Getting a Tenure-Track Position</title><link>https://bastian.rieck.me/blog/2021/tenure_track/</link><description>&lt;p&gt;Soon, a new chapter in my career as a researcher beckons: I will
join the &lt;a href="https://www.helmholtz-muenchen.de/aih"&gt;Institute of AI for Health&lt;/a&gt;,
an institute of the &lt;a href="https://www.helmholtz-muenchen.de/helmholtz-zentrum-muenchen/index.html"&gt;Helmholtz Zentrum München&lt;/a&gt;,
to lead my own research group, the &lt;a href="https://aidos.group"&gt;AIDOS Lab&lt;/a&gt;.
I intend to use this position to reify my vision of topology-based
methods for addressing challenges in biomedicine and healthcare. In this
post, I want to briefly detail the long, rocky, and certainly winding
road that led me to this position.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="A winding road through a mountain landscape" src="https://bastian.rieck.me/images/winding_road.jpg" width="500" /&gt;&lt;figcaption&gt;
			&lt;p&gt;This is what it felt like traversing academia at times. In the end, I hope we all arrive where we want to arrive, even if it might take quite long. &lt;small&gt;(Photo by &lt;a href="https://unsplash.com/@jack_anstey?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Jack Anstey&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/winding-road?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;)&lt;/small&gt;&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;h1 id="but-why"&gt;But why?&lt;/h1&gt;
&lt;p&gt;The social media streams are chock full of people winning awards,
prestigious grants, and getting coveted professorships. Most of us tend
to craft an image of perpetual success, soaring ever higher. Few of us
tend to discuss the path that led to these successes. Like the
proverbial iceberg, we only observe a fraction of the things that were
required to get to a certain point. I want to give you the &amp;lsquo;full scoop&amp;rsquo;
here, in the hope that people find inspiration in it. I will also use
this post to express certain misgivings about the application process.
As for myself, I certainly learned a lot while applying to tenure-track
positions, and I intend to improve the system as much as I can.&lt;/p&gt;
&lt;h1 id="applications"&gt;Applications&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s start with the raw stats. Like Derek Sivers, I assume that &lt;a href="https://sive.rs/below-average"&gt;I am
below average&lt;/a&gt;, so your numbers will
hopefully look better. Over the course of 2020, I applied to 20 different
institutions&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; with 23 individual applications.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; Here&amp;rsquo;s a summary
of all the feedback I received, shown as a &lt;a href="https://en.wikipedia.org/wiki/Sankey_diagram"&gt;Sankey
diagram&lt;/a&gt; that was built
using the awesome &lt;a href="https://sankeymatic.com"&gt;SankeyMATIC&lt;/a&gt; service:&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="A Saneky diagram of all my applications" src="https://bastian.rieck.me/images/tenure_track_sankey.svg" width="500" /&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;In the following, I will briefly discuss a few aspects of this diagram.&lt;/p&gt;
&lt;h2 id="getting-an-interview-or-not"&gt;Getting an interview (or not)&lt;/h2&gt;
&lt;p&gt;About 30% of my applications led to an interview. I count such an
invitation as a roaring success because it means that my application
materials made the cut! If you are at this point, I would suggest that
you briefly celebrate this achievement because it means that you are
being noticed by potential employers.&lt;/p&gt;
&lt;p&gt;At the same time, 30% of my applications led to a direct rejection.
There are many potential reasons for a direct rejection:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I might have been too inexperienced for some positions. If the
administrative bar for applying to a job is sufficiently low,
I tend to send in an application anyway even though it might be
a long shot. This is a delicate balance, though: I am strongly
opposed to wasting time by applying to positions that are probably
not attainable for me. For instance, if a position is looking for
an established professor with more than 5 years experience leading
their group, I am &lt;em&gt;not&lt;/em&gt; (yet) the target demographic. This
attitude sometimes surprises people, but I can only recommend to
&amp;lsquo;know thyself and thy market value&amp;rsquo; before investing too much
time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My research output might be considered insufficient for certain
places. Some places required me to enter my bibliometric
information, i.e. the number of papers, the number of citations,
and my h-index, using a service called &lt;a href="https://www.webofknowledge.com/"&gt;Web of
Knowledge&lt;/a&gt;. This service is
&lt;strong&gt;utterly useless&lt;/strong&gt; and should be avoided at all costs. It does
not even index most machine learning conferences, leading to an
extremely skewed profile of anyone&amp;rsquo;s research (at least in
the field of machine learning; I can only hope that it is better
for other disciplines).&lt;/p&gt;
&lt;p&gt;If you as a department feel you have to
rely on such statistics, consider using &lt;a href="https://scholar.google.com"&gt;Google
Scholar&lt;/a&gt;. It is infinitely better at
keeping track of publications.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Competition might have been too fierce (which is somehow related
to my first point) with many excellent people applying to the same
job, forcing the hiring committee to weed out applications rather
strictly. I did my homework for all positions and only applied
when there was a non-zero chance of success without divine
intervention but I do not know who my competitors were.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My research agenda might not have been aligned with what
a department was looking for. With many departments implementing
a strategy for cutting-edge machine learning methods nowadays, in
a diverse field such as ours, it is never clear whether your research
goals are a perfect fit for a certain place. As someone with
a rather unorthodox research vision, some departments might be
reluctant to invest time in reviewing my application further.
That&amp;rsquo;s okay!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Almost 40% of my applications, however, resulted in &lt;em&gt;no reaction&lt;/em&gt; at all.
Not even a rejection (but at least an acknowledgement of the receipt of
my application materials, so I guess, in some weird quantum fashion,
I might still be in the race for certain positions) letter. Of course,
I believe that I am sufficiently smart to know that after six months, it
is unlikely that I will hear back, but I must say that it is disappointing to
jump through a lot of administrative hoops in preparing your application
materials just to be ghosted in the end.&lt;/p&gt;
&lt;p&gt;If you are responsible for the hiring in your department, please heed
these words: every candidate, in my opinion, deserves at least a rejection letter (even a canned response is fine).&lt;/p&gt;
&lt;h3 id="lessons-learned"&gt;Lessons learned&lt;/h3&gt;
&lt;p&gt;For most of us, multiple applications are needed in
order to get a positive response. Try to treat every invitation to an
interview as a &amp;lsquo;win,&amp;rsquo; because fundamentally, this means that your
application materials were &lt;em&gt;sufficiently good&lt;/em&gt;. Plan for rejection letters to be
largely devoid of content. Due to legal reasons, no place will be
willing or able to tell you why they rejected you. You might inquire about this
through the grapevine, but you will never (&lt;a href="https://www.youtube.com/watch?v=c548RjB8jzQ"&gt;well, hardly ever&amp;hellip;&lt;/a&gt;)
get an official justification.&lt;/p&gt;
&lt;p&gt;Nevertheless, I always send a polite message thanking the organisers for
considering my application at least. For some places, I asked about the
number of candidates they invited and how many applications they
received. This gave me a way to update my perception of my own chances
at these jobs. In fact, I treated any such rejection as a &amp;rsquo;near miss.&amp;rsquo;
For instance, one position received over 60 applicants, but only 6 were
invited for an interview. I therefore&amp;mdash;probably incorrectly, but full
of vim and vigour&amp;mdash;concluded that I was &amp;lsquo;obviously&amp;rsquo; placed at rank 7,
rather than rank 60.&lt;/p&gt;
&lt;p&gt;I would suggest that everyone includes such statistics in their
rejection letters. That way, we few, we happy few, at least get a rough
feeling about our chances.&lt;/p&gt;
&lt;h2 id="during-the-interview"&gt;During the interview&lt;/h2&gt;
&lt;p&gt;The interview process itself was relatively standardised: a few slides
are prepared in advance and later on, you have one-on-one interviews
with faculty members. I enjoyed this part of the process the most
because it forced me to sharpen my skills when it comes to discussing
the merits of my own work. Since faculty members might not all be
familiar with the particulars of what you are doing, this is a great
opportunity to improve your pedagogical skills!&lt;/p&gt;
&lt;p&gt;All in all, interviews were really enjoyable. I particularly appreciated
the fact that &lt;em&gt;all&lt;/em&gt; the places I interviewed at accommodated my time
zone requirements when having virtual interviews. This really made the
process less stressful.&lt;/p&gt;
&lt;p&gt;When it comes to one-on-one interviews,
I enjoy tough questions the most&amp;mdash;this demonstrates that a person is
genuinely interested in your research and wants you to convince them!&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget that these interviews are two-sided: you are applying at
the department, but the department is also trying to convince you to
join &lt;em&gt;them&lt;/em&gt;, so make use of this time to ask all the questions you had
in mind. My most favourite question to ask faculty members is &amp;lsquo;Given
that you also started out on a tenure-track position at some point, what
advice do you wish you would have been given?&amp;rsquo; This question was
universally appreciated by all, and I like it because it is rather open
to interpretation, making it easy to segue into other subjects.&lt;/p&gt;
&lt;h3 id="lessons-learned-1"&gt;Lessons learned&lt;/h3&gt;
&lt;p&gt;Prepare your materials well in advance, of course, and be sure to know
how the respective presentation app works. I would also suggest to
invest in &lt;a href="https://bastian.rieck.me/blog/2021/recording_setup/"&gt;good lighting and a good microphone&lt;/a&gt;, but this is more for the convenience of your
listeners. I doubt that it makes a large difference in the outcome but
a good microphone at least makes it a joy to listen to someone.&lt;/p&gt;
&lt;p&gt;If you are not the interviewee but part of the department, please make
sure to follow common virtual conference etiquette and &lt;em&gt;keep your
microphone muted&lt;/em&gt; when not talking. I had few instances in which I could
hear faculty members doing something else in the background while I was
talking (such as listening to a soccer game at full volume). I do
not expect people to listen to my talk or engage with my ideas, but at
least be a decent &amp;rsquo;listener&amp;rsquo; and mute yourself, please!&lt;/p&gt;
&lt;p&gt;When participating in such a virtual meeting, I always appreciated the
places that had dedicated persons for making sure that the candidate is
in the right meeting, their audio works, etc.  This can make all the
difference between an average interviewing experience and a stellar one!&lt;/p&gt;
&lt;h2 id="after-the-interview"&gt;After the interview&lt;/h2&gt;
&lt;p&gt;As you can see from my own stats above, I received three offers in
total, but also two rejections. A rejection at this stage usually means
that they found someone whose research agenda is an even better fit than
yours. For instance, one place was looking for someone with a better
track record of industry collaborations; my profile was slightly leaning
towards &amp;lsquo;blue skies research,&amp;rsquo; so I was not the perfect fit. It is part
of the interviewing experience that you learn about your own fit only
during the interview, so rejections are also par for the course.&lt;/p&gt;
&lt;p&gt;I always make sure to write honest &amp;rsquo;thank you&amp;rsquo; notes because everyone
invests a considerable amount of time into interviewing you. In some
cases, I received additional feedback along the lines of &amp;lsquo;Your
profile is great, but it is not what we are looking for right now.&amp;rsquo;
I appreciate this type of feedback a lot because it proves that I did
a good and convincing job.&lt;/p&gt;
&lt;p&gt;To reiterate what I wrote above: given the diversity of research
directions in our field, it is often unclear what a particular candidate
brings to the table. The interview stage can be very helpful in that
regard, and I certainly do not want to be a part of a department where
I would not be a good fit. The idea is to build something splendid here,
and if my goals and those of the other department members are
fundamentally misaligned, life is bound to become rather tedious.&lt;/p&gt;
&lt;p&gt;Concerning the offers that I received, I was of course over the moon for each
and every one! It is so great to see that my work paid off. Notice that
I did not get all the offers at the same time&amp;mdash;I therefore had to think
and deliberate quite a bit before sending in my final answer.&lt;/p&gt;
&lt;p&gt;If you are part of a departmental search committee, it might seem like a foolish or
ungrateful move for a candidate to &lt;em&gt;reject&lt;/em&gt; an offer. However, I can
honestly stress that I had valid reasons for each rejection. I can only
speak for myself here, of course, but I wanted to ensure that the
position would be an excellent fit for me, enabling me to pursue my
research agenda and build a group that does marvellous science. If
I felt that it would be impossible for me to fully commit&amp;mdash;for whatever
reason&amp;mdash;I turned an offer down (in the spirit of open and honest
communication). I rather want to commit to &lt;em&gt;one thing&lt;/em&gt; that makes me say
&lt;a href="https://sive.rs/hyn"&gt;‘Hell yeah!’&lt;/a&gt; rather than being lukewarm about
something.&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="parting-thoughts"&gt;Parting thoughts&lt;/h1&gt;
&lt;p&gt;As you can see from my own case, it takes a certain amount of resilience&amp;mdash;to stomach all the rejections&amp;mdash;before
you may finally rejoice. Make sure that you apply to positions that
entice you even if you do not think you are the &amp;lsquo;perfect&amp;rsquo; fit.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;
However, also be realistic in your aspirations; every application
requires you to invest some time, which could also be spent doing other
things.&lt;/p&gt;
&lt;p&gt;Be prepared for rejections, but also be prepared for some weird
requirements (looking at you, &lt;a href="https://www.webofknowledge.com/"&gt;Web of Knowledge&lt;/a&gt;&amp;hellip;)
or some weird conversations about your CV (at one of the places, I got criticised
quite harshly for having &amp;lsquo;only&amp;rsquo; top-tier ML &lt;em&gt;conference&lt;/em&gt; publications, but
not a lot of ML &lt;em&gt;journal&lt;/em&gt; publications; I still don&amp;rsquo;t know what that was
all about).&lt;/p&gt;
&lt;p&gt;Finally, be prepared to have amazing in-depth discussion about your
research. Even for the places that ultimately rejected my application,
it was fantastic to be able to discuss &lt;em&gt;my&lt;/em&gt; research agenda with such
eminent scholars! A big &amp;rsquo;thank you&amp;rsquo; to everyone that had to deal with my
applications.  I am grateful for having come this far, and I look
forward to an exciting future.&lt;/p&gt;
&lt;p&gt;Until next time, dear reader, I wish you all the best for your own
job search. &amp;lsquo;Screw your courage to the sticking place and you shall not
fail!&amp;rsquo;&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I shall refrain from naming all of them because this post should
remain generic. The job market is in constant flux, and your profile
for potential positions is probably a lot different from mine.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Certain institutions had multiple positions at multiple
departments, necessitating, you guessed it, multiple applications.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I will maybe discuss the reasons for my own decision in another
post. For now,  let me just say that I am very excited about the new
opportunities my upcoming position will entail.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Remember that &amp;rsquo;the perfect is the enemy of the good.&amp;rsquo;&amp;#160;&lt;a class="footnote-backref" href="#fnref:4"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Sun, 19 Sep 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/tenure_track/</guid></item><item><title>Can Apple Watch Measure Your Body Weight? [Updated for 2022]</title><link>https://ayewo.com/can-the-apple-watch-measure-your-body-weight/</link><description>How does my apple watch know my weight?</description><author>Misleading Metaphors</author><pubDate>Sun, 19 Sep 2021 16:12:32 GMT</pubDate><guid isPermaLink="true">https://ayewo.com/can-the-apple-watch-measure-your-body-weight/</guid></item><item><title>Longboard Setup</title><link>https://mfashby.net/posts/2021-09-19-longboard-setup/</link><description/><author>Home on mfashby.net</author><pubDate>Sun, 19 Sep 2021 12:20:49 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-19-longboard-setup/</guid></item><item><title>Fuzzing Zig Code Using AFL++</title><link>https://www.ryanliptak.com/blog/fuzzing-zig-code/</link><description>A stop-gap solution for fuzzing Zig code</description><author>ryanliptak.com</author><pubDate>Sun, 19 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.ryanliptak.com/blog/fuzzing-zig-code/</guid></item><item><title>Plain Org for iOS (a month later)</title><link>https://xenodium.com/plain-org-for-ios-a-month-later</link><description>&lt;p&gt;A month ago, I posted about my desire to &lt;a href="https://xenodium.com/org-habits-on-ios-check-tasks-youre-next/"&gt;bring org tasks/TODOs to iOS&lt;/a&gt; and make them quickly available from my iPhone.&lt;/p&gt;
&lt;p&gt;Since then, I've received some great feedback, which I've been slowly chipping away at. My intent isn't so much to move my org workflow over to iOS, but to supplement Emacs while away from my laptop.&lt;/p&gt;
&lt;p&gt;As of now, this is what the inline edit experience looks like:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-for-ios-a-month-later/inline_keyword_toolbar.gif" /&gt;&lt;/p&gt;
&lt;p&gt;If, like me, you prefer dark mode. The app's got ya covered:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/plain-org-for-ios-a-month-later/dark.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Plain Org&lt;/em&gt; is not yet available on the App Store, but you can get a TestFlight invite if you send me an email address. Ping me on &lt;a href="https://www.reddit.com/user/xenodium"&gt;reddit&lt;/a&gt;, &lt;a href="https://twitter.com/xenodium"&gt;twitter&lt;/a&gt;, or email me at &amp;quot;plainorg&amp;quot; + &amp;quot;@&amp;quot; + &amp;quot;xenodium.com&amp;quot;.&lt;/p&gt;
&lt;p&gt;You can also check out progress over at the &lt;a href="https://www.reddit.com/r/plainorg/"&gt;r/plainorg&lt;/a&gt; subreddit.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 19 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/plain-org-for-ios-a-month-later</guid></item><item><title>Against the Flood</title><link>https://www.marginalia.nu/log/22-against-the-flood/</link><description>&lt;p&gt;So hacker news apparently discovered my search engine, and really took a liking to the idea. Actually that&amp;rsquo;s a bit of an understatement, the thread has gotten 3.3k points and lingered on the front page for half a week. And I wasn&amp;rsquo;t planning for it to go quite that public yet. It has quietly been online for a while, but it was only very recently it started to feel like it was really coming together. It wasn&amp;rsquo;t perfect, there was still a lot of jankiness and limitations that could have been fixed with more time. The index was half the size it should have been. Someone discovered it and shared it. It took off like a rocket, and I&amp;rsquo;m still at a loss for words at the reception it&amp;rsquo;s gotten. I have received so many encouraging comments, emails, offers of collaboration, a few have even joined the patreon. I&amp;rsquo;ve been working through all the messages and I aim to reply to them all, but it takes time. I&amp;rsquo;m very grateful for all of this, since I half thought I was alone in this.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sun, 19 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/22-against-the-flood/</guid></item><item><title>Catching the blur event on an element and its children</title><link>https://muffinman.io/blog/catching-the-blur-event-on-an-element-and-its-children/</link><description>&lt;article class="article"&gt;&lt;p&gt;Recently I implemented a fly out menu in React, and stumbled on the following problem - I had to catch a blur event on the menu, but it had multiple focusable children. When user is tabbing between these menu items, blur event is triggered every time on the parent, followed by the focus event on the next item. As I wanted to close the menu on blur, this would close it before user was able to get to the next menu item.&lt;/p&gt;
&lt;p&gt;Solution is fairly simple and it is not React exclusive - it can be used with any other framework or vanilla JavaScript. The flow goes something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listen to a blur event on the parent element (in my case it was the menu). This will catch blur event on all of its children too.&lt;/li&gt;
&lt;li&gt;In the handler, give browser time to focus the next element (by using &lt;code&gt;requestAnimationFrame&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Check if the newly focused element is in our parent element.&lt;/li&gt;
&lt;li&gt;If it is, do nothing, as focus hasn’t exited the parent yet.&lt;/li&gt;
&lt;li&gt;If it is not, we left the parent element completely and it is safe to do our blur logic (in my case, it was closing the menu).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Code looks like this:&lt;/p&gt;
&lt;pre class="language-js"&gt;&lt;code class="language-js code-highlight"&gt;&lt;span class="code-line"&gt;&lt;span class="token keyword"&gt;const&lt;/span&gt; &lt;span class="token function-variable function"&gt;handleBlur&lt;/span&gt; &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token parameter"&gt;e&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token arrow operator"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token keyword"&gt;const&lt;/span&gt; currentTarget &lt;span class="token operator"&gt;=&lt;/span&gt; e&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token property-access"&gt;currentTarget&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token comment"&gt;// Give browser time to focus the next element&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token function"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token arrow operator"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token comment"&gt;// Check if the new focused element is a child of the original container&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token keyword control-flow"&gt;if&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token operator"&gt;!&lt;/span&gt;currentTarget&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token method function property-access"&gt;contains&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token dom variable"&gt;document&lt;/span&gt;&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token property-access"&gt;activeElement&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;      &lt;span class="token comment"&gt;// Do blur logic here!&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token punctuation"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="react-component"&gt;React component &lt;a class="anchor-link" href="#react-component"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I pulled out the logic and created a small React component:&lt;/p&gt;
&lt;pre class="language-tsx"&gt;&lt;code class="language-tsx code-highlight"&gt;&lt;span class="code-line"&gt;&lt;span class="token keyword"&gt;const&lt;/span&gt; &lt;span class="token function-variable function"&gt;ChildrenBlur&lt;/span&gt; &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt; children&lt;span class="token punctuation"&gt;,&lt;/span&gt; onBlur&lt;span class="token punctuation"&gt;,&lt;/span&gt; &lt;span class="token spread operator"&gt;...&lt;/span&gt;props &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token arrow operator"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token keyword"&gt;const&lt;/span&gt; handleBlur &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token function"&gt;useCallback&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token punctuation"&gt;(&lt;/span&gt;e&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token arrow operator"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;      &lt;span class="token keyword"&gt;const&lt;/span&gt; currentTarget &lt;span class="token operator"&gt;=&lt;/span&gt; e&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token property-access"&gt;currentTarget&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;      &lt;span class="token comment"&gt;// Give browser time to focus the next element&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;      &lt;span class="token function"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token arrow operator"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;        &lt;span class="token comment"&gt;// Check if the new focused element is a child of the original container&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;        &lt;span class="token keyword"&gt;if&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token operator"&gt;!&lt;/span&gt;currentTarget&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token method function property-access"&gt;contains&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token dom variable"&gt;document&lt;/span&gt;&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token property-access"&gt;activeElement&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;          &lt;span class="token function"&gt;onBlur&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;        &lt;span class="token punctuation"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;      &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token punctuation"&gt;[&lt;/span&gt;onBlur&lt;span class="token punctuation"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token keyword"&gt;return&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;    &lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class="token spread"&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;span class="token spread operator"&gt;...&lt;/span&gt;props&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class="token attr-name"&gt;onBlur&lt;/span&gt;&lt;span class="token script language-javascript"&gt;&lt;span class="token script-punctuation punctuation"&gt;=&lt;/span&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt;handleBlur&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token plain-text"&gt;      &lt;/span&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt;children&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token plain-text"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token plain-text"&gt;    &lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;  &lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and usage is pretty straight forward:&lt;/p&gt;
&lt;pre class="language-tsx"&gt;&lt;code class="language-tsx code-highlight"&gt;&lt;span class="code-line"&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;&lt;/span&gt;&lt;span class="token class-name"&gt;ChildrenBlur&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token tag"&gt;  &lt;span class="token attr-name"&gt;onBlur&lt;/span&gt;&lt;span class="token script language-javascript"&gt;&lt;span class="token script-punctuation punctuation"&gt;=&lt;/span&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token arrow operator"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token tag"&gt;&lt;span class="token script language-javascript"&gt;    &lt;span class="token function"&gt;doSomethingCoolOnBlur&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token tag"&gt;&lt;span class="token script language-javascript"&gt;  &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token plain-text"&gt;  &lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;Button 1&lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token plain-text"&gt;  &lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;Button 2&lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token plain-text"&gt;  &lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;Button 3&lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="token plain-text"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="code-line"&gt;&lt;span class="token plain-text"&gt;&lt;/span&gt;&lt;span class="token tag"&gt;&lt;span class="token tag"&gt;&lt;span class="token punctuation"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="token class-name"&gt;ChildrenBlur&lt;/span&gt;&lt;/span&gt;&lt;span class="token punctuation"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="demo"&gt;Demo &lt;a class="anchor-link" href="#demo"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see it live check the demo on Codepen:&lt;/p&gt;

&lt;p&gt;I really like this technique, and I found it useful in multiple places like tooltips and dropdowns, where it can replace “outside click” listeners.&lt;/p&gt;&lt;/article&gt;</description><author>Muffin Man</author><pubDate>Sun, 19 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://muffinman.io/blog/catching-the-blur-event-on-an-element-and-its-children/</guid></item><item><title>Rolling upgrades and three links I disagree with - 123dev #14</title><link>https://justingarrison.com/123dev/2021-09-19-123dev-14/</link><description>&lt;img alt="" src="https://justingarrison.com/img/123dev/14-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="rolling-upgrades"&gt;Rolling upgrades&lt;/h2&gt;
&lt;p&gt;The gif this week is the first of many I’ve made with a focus on teaching engineering concepts. It is part of an article I wrote for The New Stack explaining different patterns for Kubernetes cluster upgrades. The full article be available later this month.&lt;/p&gt;
&lt;p&gt;With a little bit of shared knowledge you know exactly what is happening in that gif with &lt;em&gt;almost&lt;/em&gt; no words. I’ll still be sharing gifs I find funny or intriguing in the newsletter, but creating and sharing my own gifs was a desire from day 0.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 19 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-19-123dev-14/</guid></item><item><title>Reluctance to Bear a Symbol</title><link>https://lambdaland.org/posts/2021-09-19_symbols/</link><description>&lt;p&gt;I feel uncomfortable with many symbols. I might have opinions about a subject, but there’s rarely a camp that has some symbol, flag, slogan, etc. that I’m comfortable with adopting because that camp does not accurately reflect my opinion. All too often, a slogan takes on more than its surface meaning, and that can make using that slogan tricky.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sun, 19 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-09-19_symbols/</guid></item><item><title>Art Sunday: Omnispheric</title><link>http://michaelhoney.com/writing/2021/9/19/art-sunday-omnispheric</link><description>&lt;figure class="
              sqs-block-image-figure
              intrinsic
            "&gt;
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                &lt;img alt="" height="1994" src="https://images.squarespace-cdn.com/content/v1/5244d189e4b0136e0a44003d/1632006318177-A7356OCN3IZQONEOYL3N/omnispheric.png?format=1000w" width="1988" /&gt;

            
          
        
          
        

        
      
        &lt;/figure&gt;</description><author>Michael Honey: Writing - michaelhoney.com</author><pubDate>Sun, 19 Sep 2021 02:05:37 GMT</pubDate><guid isPermaLink="true">http://michaelhoney.com/writing/2021/9/19/art-sunday-omnispheric</guid></item><item><title>Five ways to reduce variance in A/B testing</title><link>https://bytepawn.com/five-ways-to-reduce-variance-in-ab-testing.html</link><description>&lt;p&gt;I use toy Monte Carlo simulations to demonstrate 5 ways to reduce variance in A/B testing: increase sample size, move towards a more even split, reduce variance in the metric definition, stratification and CUPED.&lt;br /&gt;&lt;br /&gt; &lt;img alt="Historic lift" src="/images/cuped2.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 19 Sep 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/five-ways-to-reduce-variance-in-ab-testing.html</guid></item><item><title>Maddy</title><link>https://mfashby.net/posts/2021-09-18-maddy/</link><description/><author>Home on mfashby.net</author><pubDate>Sun, 19 Sep 2021 00:05:32 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-18-maddy/</guid></item><item><title>Negative impacts of land digitization</title><link>https://stop.zona-m.net/2021/09/negative-impacts-of-land-digitization/</link><description>&lt;p&gt;&amp;ldquo;Land is political, not technical&amp;rdquo;. Like all life, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 18 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/negative-impacts-of-land-digitization/</guid></item><item><title>The Power of xdotool in X11</title><link>https://blog.herlein.com/post/xdotool/</link><description>&lt;p&gt;X11&amp;rsquo;s xdotool cli is snazzy, and solved an annoying window placement problem.&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Sat, 18 Sep 2021 11:00:01 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/xdotool/</guid></item><item><title>Just say no ⚽😑 - 123dev #13</title><link>https://justingarrison.com/123dev/2021-09-18-123dev-13/</link><description>&lt;img alt="A man getting hit in the face with a ball in slow motion" src="https://justingarrison.com/img/123dev/13-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="do-you-know-how-to-say-no"&gt;Do you know how to say no?&lt;/h2&gt;
&lt;p&gt;People often feel guilty for saying no to opportunities, but in reality you should say no when you really care about something but know you are not the best person. It doesn’t matter if you’re not interested, if you don’t have enough time, or you don’t have the right skills. If you understand saying no will make the project/event/collaboration/etc. more successful then you can better position yourself to say no and explain why.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sat, 18 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-18-123dev-13/</guid></item><item><title>Restart</title><link>https://mfashby.net/posts/2021-09-17-restart/</link><description/><author>Home on mfashby.net</author><pubDate>Sat, 18 Sep 2021 01:41:39 GMT</pubDate><guid isPermaLink="true">https://mfashby.net/posts/2021-09-17-restart/</guid></item><item><title>Wiki-Loader: Loading Wikipedia into Neo4J</title><link>https://joshuarogers.net/articles/2021-09/wiki-loader-loading-wikipedia-into-neo4j/</link><description>In April, I put together a Python script that would load an XML export from Wikipedia into a local SQLite database because, once again, you never know when you'll inadvertently become a time traveller who needs knowledge of future events to return home. (Look, it's more interesting than just saying &amp;ldquo;because I find it amusing&amp;rdquo;, so just roll with me here.)
This script satisfied the typical time traveler's use-case of &amp;ldquo;finding a bit of topical information quickly&amp;rdquo;, yes, but that is only one of the many use-cases that chronological explorers must concern themselves with before traipsing around through spacetime.</description><author>Joshua Rogers</author><pubDate>Fri, 17 Sep 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://joshuarogers.net/articles/2021-09/wiki-loader-loading-wikipedia-into-neo4j/</guid></item><item><title>Of supply chains, or why global is personal these days</title><link>https://stop.zona-m.net/2021/09/of-supply-chains-or-why-global-is-personal-these-days/</link><description>&lt;p&gt;Global is personal. It really is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 17 Sep 2021 07:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/of-supply-chains-or-why-global-is-personal-these-days/</guid></item><item><title>Your code is on fire 🔥 - 123dev #12</title><link>https://justingarrison.com/123dev/2021-09-17-123dev-12/</link><description>&lt;img alt="A van rolling down a hill. The van is on fire" src="https://justingarrison.com/img/123dev/12-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="shipping-code-that-doesnt-catch-fire"&gt;Shipping code that doesn’t catch fire&lt;/h2&gt;
&lt;p&gt;Sometimes I push code into production that I’m certain will work well. Not because of test coverage or PR reviews, but usually just because the change was so simple and obvious there couldn’t possibly be another way to do it.&lt;/p&gt;
&lt;p&gt;Many times those pushes work fine and my false confidence is reinforced to do it again another day. Often times I end up paying for an undocumented quick fix later. Sometimes those guaranteed-not-to-fail code pushes catch fire and roll production down a hill. Those are good learning experiences.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 17 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-17-123dev-12/</guid></item><item><title>Create your first NFT Marketplace for selling cars in PHP Laravel and web3.js</title><link>https://blog.adnansiddiqi.me/create-your-first-nft-market-place-for-selling-cars-in-php-laravel-and-web3-js/</link><description>&lt;p&gt;If you are into Crypto then chances are you have heard about NFT, especially after the incidents like Beeple sold his art for $69 million dollars or this friendship meme. NFT is usually considered is suitable for arts like pictures only but this is not true. NFT is all about uniqueness, authority, and the transfer of ownership. We are going to use NFT for selling cars. The video demo of this project can be seen below: &amp;#160; In this post, I will be discussing how to mint ERC721 NFT tokens and then integrate them with new or existing websites. The user who buys a car will pay the amount of the car being bought and the minting fee. I came across many tutorials regarding NFT or ERC721 tokens but none of them actually provided end-end solutions. It was quite frustrating because NFT itself is nothing unless it is integrated with an app. In this post, I am coming up with a fictitious car-selling portal site developed in PHP Laravel and web3.js. As I said in a previous post, that the backend framework or language does not matter. I will be writing this tutorial in a way that will highlight the NFT functionality and its integration. In case you wonder why I am using PHP not Python, well, it is just an attempt to break the taboo that blockchain programming is all about Javascript-based technologies. What is NFT? According to Wikipedia: A non-fungible token (NFT) is a unit of data stored on a digital ledger, called a blockchain, that certifies a digital asset to be unique and therefore not interchangeable.[1] NFTs can be used to represent items such as photos, videos, audio, and other types of digital files. Access to any copy of the original file, however, is not restricted to the buyer of the NFT. While copies of these digital items are available for anyone to obtain, NFTs are tracked on blockchains to provide the owner with a proof of ownership that is separate from copyright. NFT is quite a in demand these days. Though the majority of NFTs are built on Ethereum based blockchains but not limited to it. Blockchains like Solana and many others also offer NFT on their respective platforms. I am not going to cover the Laravel code as such since this is not the scope of this post neither I am going to cover Truffle/Ganache and smart contract integration via web3.js. You can read about it here. Website Development The backend of the website is developed in Laravel. MySQL has been used for storing all data and IPFS is a decentralized CDN for images. Pinata Cloud which provides the pinning service was actually used to upload all images on IPFS. The flow of the website is pretty simple. Currently, dApps are considered those who are hybrid in nature that is, a few features are on blockchain, especially the payment one, and the rest of the info is in centralized systems. A user signs up and uploads the details of the car he wants to sell. The image is uploaded via Pinata API for pinning. The API returns a hash which along with other information is stored in the MySQL database. The cars table structure looks like the below: The function that uploads the inventory looks like the below: Don&amp;#8217;t be happy as these keys do not exist anymore 😉 The car once uploaded appears like the below image: As you see I have used VIN here. The reason to use it for to bring uniqueness to the product since NFT is all about uniqueness. NFT Smart Contract Currently, Ethereum offers two types of smart contracts standards: ERC721 and ERC1155. I am using the very basic ERC721 one since it serves the purpose well. I am using the one provided by OpenZeppelin. Using existing templates spare you to come up with your own faulty implementations while on the other hand smart contracts by OZ guys have been thoroughly audited by security companies. dCars NFT smart contract looks like the below: Notice that dCars is inheriting ERC721 contract provided by OZ guys. I am also inheriting Ownable for ownership and authorization purposes thus make it secure. You can learn more about it here. I am only implementing the minting feature because I am changing the default implementation by introducing Counters, rest of the functions that are already available to us. Thanks to our friend, Inheritence. The mintNFT function accepts a tokenURI parameter which is actually directing to the Metadata of the individual NFT. The Metadata looks like the below. This is actually the metadata that helps to discover your NFT on NFT marketplaces like Openseas. Web3js Integration Here web3.js is being used for two reasons: First, asking the user to pay the price of the car itself, and second the gas fee for minting purposes. I am not going into detail about charging ETH from your user&amp;#8217;s Metamask since I discussed it here and minting the NFT which is actually calling mintNFT function which I have discussed here. payEther function looks like the below. And createNFT JS function which is actually calling smart contract function looks like the below: Both of these functions are actually invoked via the click event of the BUY button which after successful transaction stores info in relevant DB tables. After this the detail page will look like the below: &amp;#160; Visitors can see who is the current owner of this car and its history. It is quite possible that the owner makes it to resell to others, for that transferFrom smart contract function will be called which is not implemented here. Conclusion So in this post, you learn what is an NFT and how as a developer you can implement it in existing or new web apps. This is not a complete project, features like reselling have not been implemented in it. Like always, the code is available on Github. &amp;#160; &amp;#160; If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/create-your-first-nft-market-place-for-selling-cars-in-php-laravel-and-web3-js/"&gt;Create your first NFT Marketplace for selling cars in PHP Laravel and web3.js&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Thu, 16 Sep 2021 21:45:41 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/create-your-first-nft-market-place-for-selling-cars-in-php-laravel-and-web3-js/</guid></item><item><title>The American Style of quotation mark punctuation makes no sense</title><link>https://nicolaiarocci.com/the-american-style-of-quotation-mark-punctuation-makes-no-sense/</link><description>&lt;p&gt;Years ago, I &lt;a href="http://www.endore.it/Arretrati/9/Articoli/SuTolkienELeFiabe.pdf"&gt;translated&lt;/a&gt; an essay by Terry Windling, &lt;a href="https://accademia.tolkieniana.net/tesi/endicott/tlkefiabeng.html"&gt;On Tolkien and
Fairie-Stories&lt;/a&gt;, from American English to Italian. I remember arguing with
the author about her use of periods in quotations. Each quotation would end
with a period before the closing mark. I was puzzled. We don&amp;rsquo;t do that in
Italy. More importantly, I read many English texts where the period was left
outside the quotation itself. She insisted that her style was correct&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 16 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/the-american-style-of-quotation-mark-punctuation-makes-no-sense/</guid></item><item><title>Why and how Amazon Counter could be good for the world</title><link>https://stop.zona-m.net/2021/09/why-and-how-amazon-counter-could-be-good-for-the-world/</link><description>If it re-done right, that is.  Back in early 2020 (*), a friend of mine asked online what people thought about Amazon Counter. My first reply greatly surprised him, because he knows my opinions about Amazon, and that reply started with:
&amp;ldquo;This really sounds like a consumer&amp;rsquo;s dream, and a green one too.&amp;quot;
The conversation continued as follows (edited for clarity).
Friend: Did you call it green because it would enable &amp;lsquo;cosmo-local&amp;rsquo; production, that is small-scale manufacturing of products designed once and then reused globally?</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 16 Sep 2021 08:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/why-and-how-amazon-counter-could-be-good-for-the-world/</guid></item><item><title>Practice, handling errors, and some python 🐍 - 123dev #11</title><link>https://justingarrison.com/123dev/2021-09-16-123dev-11/</link><description>&lt;img alt="a cat licking itself at a gym" src="https://justingarrison.com/img/123dev/11-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="practice"&gt;Practice&lt;/h2&gt;
&lt;p&gt;I’ve been on a diet and I’m pretty sure this cat has better sit-up form than I have. Just like anything we do it takes practice and if we don’t do it for a while we fall out of form. As much as I exercised in high school I have to relearn a lot after not doing much for the past 20 years.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 16 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-16-123dev-11/</guid></item><item><title>Top 5 areas where senior architects can make the biggest difference</title><link>https://billylo.medium.com/top-5-areas-where-senior-architects-can-make-the-biggest-difference-7a3fd0b54955?source=rss-5717c3e67e86------2</link><description>&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iL8MeTos_IMhzrCrAp_lkg.jpeg" /&gt;&lt;figcaption&gt;Photo by &lt;a href="https://www.pexels.com/@lexovertoom?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels"&gt;Lex Photography&lt;/a&gt; from &lt;a href="https://www.pexels.com/photo/person-holding-black-pen-1109541/?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels"&gt;Pexels&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Think of your technology systems as a car: a tool that your company’s leaders use to get to their destination.&lt;/p&gt;&lt;p&gt;When designing and building this car, critical elements must receive sufficient focus. Here is my Top 5 list.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;&lt;em&gt;The engine and the drivetrain:&lt;/em&gt;&lt;/strong&gt; The &lt;strong&gt;operating platform&lt;/strong&gt; must be reliable and adaptive so we can drive on highways, city roads &lt;em&gt;and&lt;/em&gt; suburb trails. Cloud native is definitely the prominent trend here.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;S&lt;em&gt;peedometer and brakes: &lt;/em&gt;&lt;/strong&gt;To be able to drive fast without going out of control, we need to have good sensors and control systems. In the context of software systems, these are your &lt;strong&gt;application performance &amp;amp; management&lt;/strong&gt; (APM) components.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;em&gt;Drivers’ manuals and training:&lt;/em&gt;&lt;/strong&gt; Drivers come from different backgrounds and we cannot assume they understand how the car can be used optimally. Systems are the same. &lt;strong&gt;Education, communication and coaching&lt;/strong&gt; are high value areas. Architects can make a big difference because of their strong understanding on how systems support business processes.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;em&gt;GPS and on-dash displays:&lt;/em&gt;&lt;/strong&gt; Road conditions are dynamic and we cannot stick to the same route every time. &lt;strong&gt;Analytics and reporting components&lt;/strong&gt; is always worth your senior architects’ time. We need to get to the right place after all.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Driver’s comfort: &lt;/strong&gt;The journey is measured in years not minutes. If you want your system to be successful, it has to feel comfortable. But how do you architect systems that feel right? There is no magic formula here. Ensure you have a good feedback gathering mechanism: something that you can use to make adjustments. The ability to observe how applications are used (e.g. tools such as HotJar, Google Analytics) is a good start.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You may notice that security is not on this list because it really is a given. If you have a Ferrari with no locks, you are not going to find it again for your 2nd trip. :-)&lt;/p&gt;&lt;p&gt;Focus means saying no to good, but less important things. I hope this can help you prioritize projects that need to be worked on.&lt;/p&gt;&lt;p&gt;Safe travels! 🚗 🚗 🚗&lt;/p&gt;&lt;img alt="" height="1" src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=7a3fd0b54955" width="1" /&gt;</description><author>Stories by Billy Lo on Medium</author><pubDate>Wed, 15 Sep 2021 22:46:30 GMT</pubDate><guid isPermaLink="true">https://billylo.medium.com/top-5-areas-where-senior-architects-can-make-the-biggest-difference-7a3fd0b54955?source=rss-5717c3e67e86------2</guid></item><item><title>This is not the toilet I was looking for</title><link>https://stop.zona-m.net/2021/09/this-is-not-the-toilet-i-was-looking-for/</link><description>&lt;p&gt;Never mind FACE recognition. Ass recognition is where it&amp;rsquo;s at.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 15 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/this-is-not-the-toilet-i-was-looking-for/</guid></item><item><title>Shang-Chi and the Legend of the Ten Rings</title><link>https://olshansky.info/movie/shang-chi_and_the_legend_of_the_ten_rings/</link><description>Olshansky's review of Shang-Chi and the Legend of the Ten Rings</description><author>🦉 olshansky 🦁</author><pubDate>Wed, 15 Sep 2021 09:47:52 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/shang-chi_and_the_legend_of_the_ten_rings/</guid></item><item><title>advanced shell packaging: resholve YADM's nixpkg</title><link>https://t-ravis.com/post/nix/advanced_shell_packaging_resholve_yadm/</link><description>I've been working on resholve to improve Nix packaging for shell projects since early 2020, but the new v0.6.0 release is a decent leap forward in the fraction of Shell scripts and programs resholve can handle.</description><author>travis a. everett</author><pubDate>Wed, 15 Sep 2021 03:40:00 GMT</pubDate><guid isPermaLink="true">https://t-ravis.com/post/nix/advanced_shell_packaging_resholve_yadm/</guid></item><item><title>Adding Desktop Icons to sdkman managed packages</title><link>https://b.agilob.net/linux/sdk-man-desktop-icon/</link><description>When you install package like jmc or VisualVM from sdkman, the installation is for your current user only, so it doesn&amp;rsquo;t create desktop shortcuts allowing you to start program from Gnome or KDE app launchers. Unfortunately this by design forces users to start application from command line, even when it&amp;rsquo;s a desktop application.
To fix it, and be able to run jmc from Alt+F2 launcher you need to create your own desktop file like this:</description><author>Random Notes by agilob</author><pubDate>Wed, 15 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/linux/sdk-man-desktop-icon/</guid></item><item><title>Hidden work, systems design, compensation, and faster learning - 123dev #10</title><link>https://justingarrison.com/123dev/2021-09-15-123dev-10/</link><description>&lt;img alt="" src="https://justingarrison.com/img/123dev/10-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="hidden-work"&gt;Hidden work&lt;/h2&gt;
&lt;p&gt;I never thought about how donuts were filled. I should have guessed it was someone’s job, but I assumed it was automated. It makes me realize there’s a lot of work I never think about.&lt;/p&gt;
&lt;p&gt;There is lots of work I do that no one else thinks about. This isn’t a bad thing. It’s important to view my work as a delightful filling to someone’s day. Hopefully, like this email is for you.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 15 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-15-123dev-10/</guid></item><item><title>Facebook does opposite of what I said</title><link>https://stop.zona-m.net/2021/09/facebook-does-opposite-of-what-i-said/</link><description>&lt;p&gt;But hey, why should you bother? It works, does it not?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 15 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/facebook-does-opposite-of-what-i-said/</guid></item><item><title>Max Size of Django message Data in Cookie Storage</title><link>https://banagale.com/max-size-of-django-message-data-in-cookie-storage.htm</link><description>I responded to a little challenge from Matt Layman of the Django Riffs podcast Episode 15, User Session Data. Toward the end of the episode, Matt asked what might happen if over 4k worth of text was passed into messages if the backend storage method was cookies? What I found was unexpected. First, you can&amp;#8230; &lt;a class="more-link" href="https://banagale.com/max-size-of-django-message-data-in-cookie-storage.htm"&gt;Continue reading &lt;span class="screen-reader-text"&gt;Max Size of Django message Data in Cookie Storage&lt;/span&gt;&lt;/a&gt;</description><author>Rob</author><pubDate>Wed, 15 Sep 2021 00:46:14 GMT</pubDate><guid isPermaLink="true">https://banagale.com/max-size-of-django-message-data-in-cookie-storage.htm</guid></item><item><title>Test models thoroughly, test everything else less</title><link>https://adamcraven.com/writing/test-models-thoroughly-test-everything-else-less/</link><description/><author>Writing on Adam Craven</author><pubDate>Tue, 14 Sep 2021 19:21:06 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/test-models-thoroughly-test-everything-else-less/</guid></item><item><title>Model all state in an application</title><link>https://adamcraven.com/writing/model-all-state-in-an-application/</link><description/><author>Writing on Adam Craven</author><pubDate>Tue, 14 Sep 2021 19:04:58 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/model-all-state-in-an-application/</guid></item><item><title>Ensure UI components can be loaded separately from an application</title><link>https://adamcraven.com/writing/ensure-ui-components-can-be-loaded-separately-from-an-application/</link><description/><author>Writing on Adam Craven</author><pubDate>Tue, 14 Sep 2021 16:49:53 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/ensure-ui-components-can-be-loaded-separately-from-an-application/</guid></item><item><title>MediaWiki hotkeys (the useful ones)</title><link>https://river.me/blog/mediawiki-hotkeys-useful/</link><description>A list of just the useful hotkeys/access keys in MediaWiki</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 14 Sep 2021 10:15:21 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/mediawiki-hotkeys-useful/</guid></item><item><title>New Solutions Creating Old Problems</title><link>https://www.marginalia.nu/log/21-new-solutions-old-problems/</link><description>&lt;p&gt;I&amp;rsquo;ve spent some time the last week optimizing how the search engine identifies appropriate search results, putting far more consideration into where and how the search terms appear in the page when determining the order they are presented.&lt;/p&gt;
&lt;p&gt;Search-result relevance is a pretty difficult problem, but I do think the changes has brought the search engine in a very good direction.&lt;/p&gt;
&lt;p&gt;A bit simplified, I&amp;rsquo;m building tiered indices, ranging from&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Tue, 14 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/21-new-solutions-old-problems/</guid></item><item><title>Going back to the gym</title><link>https://blog.imraniqbal.org/gym/</link><description>&lt;p&gt;The province that I am in lifted covid restrictions for the gym.
I used to work out a lot before covid, during the pandemic I have co-opted more of a sedentary lifestyle.&lt;/p&gt;
&lt;p&gt;I signed up for a new membership as soon as possible to the closest gym.
The first workout (per body part) always sucks.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The weights that I am able to lift is far below what I used to be able to do&lt;/li&gt;
&lt;li&gt;I am going to be sore for days after the workout and the worked out body parts will be quite useless&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Despite the pain, it's a pleasant experience.
It has a huge improvement on my mood, though going outside might also contribute to that.
I am super hoping that the government does not lock things down now that vaccine administration is nearing 80ish percent.&lt;/p&gt;
&lt;p&gt;My old gym goal used to be a 600 lb deadlift before I was 30.
I think it might be doable to achieve by 35 (sitting pretty at 135 lbs at the moment).&lt;/p&gt;
&lt;p&gt;Oh and in other happier news, the vertigo has gone (more like I just got used to it)!
At the least it has not affected any of my workouts and I hope it remains that way.&lt;/p&gt;</description><author>Imran's Blog</author><pubDate>Tue, 14 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.imraniqbal.org/gym/</guid></item><item><title>Balance, free resources, lock-in, and whitepapers - 123dev #9</title><link>https://justingarrison.com/123dev/2021-09-14-123dev-9/</link><description>&lt;img alt="woman rides a bike on a high wire over a forest" src="https://justingarrison.com/img/123dev/9-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="balance"&gt;Balance&lt;/h2&gt;
&lt;p&gt;Learning how to ride a bike requires you to balance in new ways. Riding a bike on a tight rope requires similar balance, and I can only image the perceived risks of failure makes it exponentially harder to focus.&lt;/p&gt;
&lt;p&gt;Trying anything new will require some sort of balance. Physical, mental, time management, relationships all create a balance in our lives that can easily topple due to unexpected illnesses, changes in priority, or adding new plates to juggle. Balance is only achievable when we understand how we fall and reacting to the signals that indicate we are falling.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 14 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-14-123dev-9/</guid></item><item><title>Abusing AWS Lambda to make an Aussie Search Engine</title><link>https://boyter.org/posts/abusing-aws-to-make-a-search-engine/</link><description>&lt;h1 id="update-2025-05-19"&gt;UPDATE 2025-05-19&lt;/h1&gt;
&lt;p&gt;Somewhat un-suprisingly AWS has closed the loophole that I was exploiting for this &lt;a href="https://aws.amazon.com/service-terms/#30._AWS_Lambda"&gt;https://aws.amazon.com/service-terms/#30._AWS_Lambda&lt;/a&gt; and as such compiling data into your lambda for &amp;ldquo;free&amp;rdquo; storage is no longer allowed. As such bonzamate.com.au in its current iteration has been shut down.&lt;/p&gt;
&lt;p&gt;Watch this space however! I have some ideas on how to bring it back, albeit in a different form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL/DR&lt;/strong&gt; I wrote an Australian search engine. You can view it at &lt;a href="https://bonzamate.com.au"&gt;bonzamate.com.au&lt;/a&gt;. It&amp;rsquo;s interesting because it runs its own index, only indexes Australian websites, is written by an Australian for Australians and hosted in Australia. It&amp;rsquo;s interesting technically because it runs almost entirely serverless using AWS Lambda, and uses bit slice signatures or bloom filters for the index similar to Bing. I also found out the most successful code I have ever written is PHP, despite never being a professional PHP developer.&lt;/p&gt;</description><author>Ben E. C. Boyter</author><pubDate>Tue, 14 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/abusing-aws-to-make-a-search-engine/</guid></item><item><title>Something meaningful about driverless, for a change</title><link>https://stop.zona-m.net/2021/09/something-meaningful-about-driverless-for-a-change/</link><description>&lt;p&gt;This is refreshing, really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 13 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/something-meaningful-about-driverless-for-a-change/</guid></item><item><title>Pico Ada Libraries Release 0.6.0</title><link>https://synack.me/blog/rp2040-hal-release-0-6-0.html</link><description>I’ve tagged release 0.6.0 of the rp2040_hal, pico_bsp, and pico_examples libraries for the Raspberry Pi Pico. Release notes are at the end of this page.</description><author>Jeremy Grosser</author><pubDate>Mon, 13 Sep 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://synack.me/blog/rp2040-hal-release-0-6-0.html</guid></item><item><title>Things that are real</title><link>https://tiltingatwindmills.dev/things-that-are-real/</link><description>I just watched the documentary "Behind the Curve" on Netflix. It explores the
Flat Earth movement and their efforts to prove that there is a…</description><author>Tilting at Windmills</author><pubDate>Mon, 13 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://tiltingatwindmills.dev/things-that-are-real/</guid></item><item><title>Vertical auto scaling Daft Punk elliptic curves - 123dev #8</title><link>https://justingarrison.com/123dev/2021-09-13-123dev-8/</link><description>&lt;img alt="Two dots on a graph form a line until they hit a 3rd dot" src="https://justingarrison.com/img/123dev/8-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="one-more-feature"&gt;One more feature&lt;/h2&gt;
&lt;p&gt;It’s difficult with software to know when to stop and release what you have. There’s always one more feature to create and one more bug to squash. Putting out software you know has bugs or doesn’t do everything you want is anxiety inducing but also freeing because it can only get better from here.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 13 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-13-123dev-8/</guid></item><item><title>The Dig</title><link>https://olshansky.info/movie/the_dig/</link><description>Olshansky's review of The Dig</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 12 Sep 2021 12:04:36 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/the_dig/</guid></item><item><title>Demystifying AWS IAM Policies: Unraveling De Morgan’s Laws and S3 Buckets Policy</title><link>https://blog.cetinich.net/content/2021/s3-de-morgans-laws-and-iam/</link><description>Before we get into it we need to review how Condition evaluation works:</description><author>Brent's Blog</author><pubDate>Sun, 12 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.cetinich.net/content/2021/s3-de-morgans-laws-and-iam/</guid></item><item><title>How to learn and three projects to try - 123dev #7</title><link>https://justingarrison.com/123dev/2021-09-12-123dev-7/</link><description>&lt;img alt="a frame of a car is replaced in commercials with real looking production cars" src="https://justingarrison.com/img/123dev/7-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="company-presentations-and-movie-magic"&gt;Company presentations and movie magic&lt;/h2&gt;
&lt;p&gt;I often watch conference talks from companies that appear to be complete internal products, but behind the scenes are more like the blackbird car in the gif above. Companies have high performance, purpose built solutions they create and then present them as the answer to everyone’s problem. This isn’t always a negative thing. You should be aware that their Ferrari may not have a windshield or AC.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 12 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-12-123dev-7/</guid></item><item><title>No, THESE 9/11 losses are not Adobe's fault</title><link>https://stop.zona-m.net/2021/09/no-these-9/11-losses-are-not-adobes-fault/</link><description>&lt;p&gt;No way they are. They are YOUR fault.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 12 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/no-these-9/11-losses-are-not-adobes-fault/</guid></item><item><title>September 11th from Singapore</title><link>https://www.swyx.io/sept-11-from-singapore</link><description>just some memories 20 years after 9/11.</description><author>swyx's site RSS Feed</author><pubDate>Sun, 12 Sep 2021 01:26:53 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/sept-11-from-singapore</guid></item><item><title>Fun with speckle patterns</title><link>https://www.anfractuosity.com/projects/fun-with-speckle-patterns/</link><description>I recently came across a really fascinating video &amp;#8211; https://www.youtube.com/watch?v=tYFLze9VwB0 where they make use of a micro laser projector to generate a speckle pattern on a surface and then photograph it with a DSLR. They then simply touch a surface with their hand/finger and then take a photo of the speckle pattern again after the [&amp;#8230;]</description><author>Anfractuosity</author><pubDate>Sun, 12 Sep 2021 01:04:43 GMT</pubDate><guid isPermaLink="true">https://www.anfractuosity.com/projects/fun-with-speckle-patterns/</guid></item><item><title>Not Just Bikes</title><link>https://voussoir.net/writing/not_just_bikes</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a class="tag_link" href="/writing/tags/life"&gt;[life]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Not Just Bikes&lt;/h1&gt;
&lt;p&gt;In May of 2021 I discovered the youtube channel &lt;a href="https://www.youtube.com/channel/UC0intLFzLaudFG-xAvUEO-A/videos"&gt;Not Just Bikes&lt;/a&gt;, and immediately watched the whole thing. The experience was revelatory and painful.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/not_just_bikes/matrix.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;NJB has shown me that American urban and road design took a nosedive during the postwar period, where the &lt;a href="https://www.youtube.com/watch?v=VVUeqxXwCA0&amp;amp;t=1m28s" title="How Suburban Development Makes American Cities Poorer"&gt;American Dream&lt;/a&gt; was to own a house with a yard, drive a car everywhere you go, and, for some reason, have nothing of utility around you. It's very easy to hop around a map of the US and find houses where the nearest grocery store is literally an hour's walk away.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/not_just_bikes/walkingdistance.png" /&gt;&lt;/p&gt;
&lt;p&gt;So you drive, and you park in the parking lots. The parking lots are enormous because everybody's driving instead of walking. They're so enormous, they push all the actual destinations farther and farther apart from each other, so that walking becomes even more useless and you have to do even more driving. Then the parking lots have to get bigger.&lt;/p&gt;
&lt;p&gt;It would be no big deal to visit several small stores for a few items each on foot, but nobody likes starting their car multiple times to visit multiple stores across an asphalt ocean, and finding a new parking space each time. So, shopping trips are usually &lt;a href="https://www.youtube.com/watch?v=YCeQLeQiRP4" title="you could stop at five or six stores"&gt;single-destination&lt;/a&gt; only, preferring the Walmarts and Targets over the small, specialized, personalized, and/or more informed grocers and boutiques.&lt;/p&gt;
&lt;p&gt;Our &lt;a href="https://www.youtube.com/watch?v=ORzNZUeUHAM&amp;amp;t=13s" title="The Ugly, Dangerous, and Inefficient Stroads found all over the US &amp;amp; Canada"&gt;stroads&lt;/a&gt; are wide, straight, and multilaned, so that it's easy for us to go really really fast and &lt;a href="https://www.youtube.com/watch?v=Ra_0DgnJ1uQ&amp;amp;t=58s" title="Why Cars Rarely Crash into Buildings in the Netherlands"&gt;crash into buildings&lt;/a&gt;, because those are our priorities. All other forms of transit infrastructure are designed first and foremost to avoid disrupting the &lt;a href="https://www.youtube.com/watch?v=vMed1qceJ_Q&amp;amp;t=8m33s" title="I am not a Cyclist"&gt;fragile egos&lt;/a&gt; of the motorists:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=9OfBpQgLXUc&amp;amp;t=34s" title="The Dutch Solution for Safer Sidewalks"&gt;Sidewalk curbs step down into the road instead of the road coming up to the sidewalk&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=M8F5hXqS-Ac&amp;amp;t=21m00s" title="Safe Cycling Showdown"&gt;Pedestrian bridges go over the stroad so drivers don't have to slow down&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=bglWCuCMSWc&amp;amp;t=7s" title="The Wrong Way to Set Speed Limits - The 85th Percentile"&gt;Speed limits are discovered as emergent properties instead of, like, designed&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=bAxRYrpbnuA&amp;amp;t=34s" title="Traffic Calming is Everywhere in the Netherlands"&gt;Traffic calming is only added as an absolute last resort after enough people die&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/notjustbikes/status/1431260478303518724" title="If the bike stencil won't fit, that should tell you something."&gt;Cyclists are sent down the rain gutters to make space for cars&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=RQY6WGOoYis&amp;amp;t=1m43s" title="Do Your Buses Get Stuck in Traffic?"&gt;Buses get stuck in the same traffic as everyone else, ensuring they remain useless to all but the carless poor&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/_ByEBjf9ktY?t=10m38s" title="Crossing the Street Shouldn't be Deadly (but it is)"&gt;Traffic signals are placed at the far end of the intersection so that motorists can comfortably encroach into and through the crosswalk without affecting their own line of sight&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because heaven forbid we impose upon the helpless motorists inside their cushy, air conditioned, noise-proof boxes, with radio and navigation and heated seats, that require no physical effort to launch at speeds higher than humanity could have dreamed of just two hundred years ago. Don't make them wait at a crossing. Life is already so hard for them.&lt;/p&gt;
&lt;p&gt;Here's my favorite NJB quote:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;For centuries, cities were designed such that everybody had what they needed within walking distance. Once you got where you were going, you could access everything you needed to on foot. These are the kinds of places Americans go to on vacation.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=VVUeqxXwCA0&amp;amp;t=23s"&gt;How Suburban Development Makes American Cities Poorer&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He says "centuries", but I think he means "since the dawn of civilization". Automobiles are less than 150 years old. You might recognize a number of beautiful, comfortable, enviable cities which are older than that. And you'll never find yourself thinking "commuting to work in ancient Rome must have been so difficult without a car!", because it wasn't. The car didn't become a necessity until we intentionally stamped out all of the alternatives.&lt;/p&gt;
&lt;p&gt;In America, young people love to say that the DMV needs to be more aggressive with re-testing elderly people and revoking their drivers license because they cause more danger in traffic. And while that's true and probably a good idea, it doesn't get implemented because license revocation is a prison sentence and absolutely everyone knows it. In the cities of respectable nations, &lt;a href="https://www.youtube.com/watch?v=IkVvXVDs5aI" title="Japan encourages parents to let kids walk to school solo - CBS"&gt;children&lt;/a&gt; and the elderly don't have any problems going to the shop or cafe on their own because it's only five minutes away from their front door. In the States, a car is compulsory for all travel unless you live in the very densest of areas. Without it, you're stuck. Taking away your grandmother's license is taking away her independence. A whole lifetime of stars-and-stripes 🦅 American 🦅 freedom and independence comes toppling down with the removal of that one stone. Then Grandma spends the rest of her life trapped in the house because there's nowhere to walk and not enough bus service and everything's too far for even an electric mobility scooter to be useful, and she whithers away into mental and physical atrophy and dies.&lt;/p&gt;
&lt;p&gt;One of the first excuses that springs to peoples' minds as to why America must be car dependent is that it is so &lt;a href="https://www.youtube.com/watch?v=REni8Oi1QJQ" title="The Dumbest Excuse for Bad Cities - iT's tOo biG!"&gt;big&lt;/a&gt;. Yes, America is geographically big, and if you go looking you can find &lt;a href="https://old.reddit.com/r/dataisbeautiful/comments/tkfpfb/oc_the_most_isolated_city_or_town_in_each_us_state/"&gt;very isolated cities&lt;/a&gt; that would take a long time to travel between. But the width of the continent as a whole has absolutely nothing to do with the placement of essentials &lt;em&gt;within&lt;/em&gt; a single city. Even people who own very large houses still keep the toilet paper within arm's reach of the commode.&lt;/p&gt;
&lt;p&gt;The people who built the oldest parts of our country must not have known how big the continent was, because they put living quarters right on top of shops back then! If today's cities would suck in their gut and bring more mixed use back into the center of downtown, people could get all of their daily business done by walking instead of driving, regardless of the center-to-center distance between them and the next city over. It's a non-sequitur.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://commons.wikimedia.org/wiki/File:Apartments_and_businesses,_12th_Ave_E_and_E_Pike_St,_ca_1912_(SEATTLE_2441).jpg"&gt;&lt;img alt="" src="/writing/not_just_bikes/mixed_use_building.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The phrase that's been in my mind for a while is "too stupid to live". It is something of a wonder that we Americans haven't already extinguished ourselves by forgetting how to breathe or eat. You may have heard that sloths can starve to death on a full stomach because their only source of food is practically indigestible. You'd think they'd figure it out and try something else.&lt;/p&gt;
&lt;p&gt;When my family gathers around the dinner table, one topic that always comes up for conversation is the vehicular idiocy that was seen throughout the week. Red light runs, excessive speeding, drivers in the shoulder lane, crashes, and near misses. A majority of people are not to be trusted with the task of driving, but we force them to do it anyway because we, collectively, are too stupid to put together better systems that don't necessitate it. It is easier to concentrate our moments of frustration towards the individuals who make short-term mistakes like running red lights than it is to realize the frustration that should be directed at the makers of long-term, diffuse, and pervasive mistakes like building suburban sprawl and bulldozing transit options.&lt;/p&gt;
&lt;p&gt;Another part of the problem is that it feels good to punish people. We are a sadistic species and we like to coerce people into crossing our boundaries so that we can feel justified in retaliating. It is not as fun to design boundaries that don't get crossed in the first place. We design roadways that encourage unsafe driving and then we put up signs justifying punishment. It's dissonance. Here's a big fat 50mph five lane road that runs straight for miles, but you'd better slow down to 25mph now because there's an elementary school here. Nevermind that the 25mph stretch of road is cut from the same cloth as the 50mph road — it's the posted sign that makes the difference. Here's a series of traffic lights that don't coordinate with each other, and will stop you over and over again just as soon as you get going, but you'd better not run any reds and you sure as hell better not get stuck in the intersection during a light change because boy I'm gonna, um, honk at you, yeah that's what I'm gonna do, and you're gonna deserve it. It is difficult to convince people that car crashes are a symptom of deep, systemic design flaws affecting average people of average driving skill, rather than the fault of uniquely stupid individuals. Which is not to say the average skill level is very high, but even the people who'd like to opt out do not have a choice.&lt;/p&gt;
&lt;p&gt;There is a freeway near me that is currently undergoing expansion, which is proven to &lt;a href="https://en.wikipedia.org/wiki/Induced_demand"&gt;induce more demand&lt;/a&gt;. Intelligent species have known this for decades. The &lt;del&gt;m&lt;/del&gt;&lt;a href="https://www.youtube.com/watch?v=CHZwOAIect4" title="More Lands are (Still) a Bad Thing"&gt;billions of dollars&lt;/a&gt; being used to put more cars on the road are not being used to improve walkability, bikeability, or reduce the need for driving in the first place by fixing our &lt;a href="https://www.youtube.com/watch?v=VVUeqxXwCA0&amp;amp;t=1m43s" title="How Suburban Development Makes American Cities Poorer"&gt;wack zoning&lt;/a&gt; and just allowing a grocery to open on more corners.&lt;/p&gt;
&lt;p&gt;Our zoning is the first thing that must change before anything else can happen. American cities are designed with huge contiguous swaths of land zoned as residential, especially R1 which denotes single-family housing. This is the zoning that prevents you from walking to the shop because there are no shops. This is the zoning that requires you to exodus by car to do absolutely anything except admire your neighbors' yards. This is the zoning for which every American deserves &lt;a href="https://www.youtube.com/watch?v=8JeLPUDj4uk" title="SpongeBob SquarePants - 40 lashes"&gt;forty lashes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With mixed-use zoning we can allow destinations to exist among the living space instead of being wholly separate. We should start with small shops, cafes, and restaurants. The kinds of things that are needed often by the residents and won't draw excessive traffic from far away because the people who live far away have their own. This will give the NIMBYs less room to complain, though they will try anyway.&lt;/p&gt;
&lt;p&gt;In the study of occupational safety, there is something called the &lt;a href="https://www.cdc.gov/niosh/topics/hierarchy/default.html"&gt;hierarchy of controls&lt;/a&gt;. Commonly represented as an inverted pyramid, it suggests you should do everything you can to eliminate a hazard using the most effective methods available to you, and only proceed to less effective methods when absolutely necessary. In between each of these levels, you could insert the phrase "&lt;em&gt;but if you can't, then, ...&lt;/em&gt;".&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/not_just_bikes/hierarchy_of_controls.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;For example, an electrician wearing rubber gloves (PPE) could touch a live wire without too much risk, but it makes a lot more sense to turn the circuit off in the first place (Elimination) unless there are substantial justifications for working hot.&lt;/p&gt;
&lt;p&gt;Applying this to suburban motorsports, we might find these levels correspond to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Elimination&lt;/strong&gt;: Do not ride any dangerous vehicle, just walk. But if you can't, then,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Substitution&lt;/strong&gt;: Get ordinary motorists off the road because they have proven themselves untrustworthy;  substitute them with buses operated by drivers with higher training standards, and trains which run on rails and are backed by substantial safety measures. But if you can't, then,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engineering controls&lt;/strong&gt;: Increase separation of motorways from other modes of transit. Add comprehensive networks of bicycle paths. But if you can't, then,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Administrative controls&lt;/strong&gt;: Design roadways to be less conducive to speeding and reckless driving by making them less convenient to motorists. Raise pedestrian sidewalks so motorists must slow down, lest they wreck their suspension by speeding over it. Move traffic lights to the near side of the intersection. Bring destinations closer together with better zoning. But if you can't, then,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PPE&lt;/strong&gt;: Mandate airbags and seatbelts.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fail, fail, fail, fail, pass. Great job America, you got seatbelts in your cars. The only measure you've managed to accomplish so far is the least effective.&lt;/p&gt;
&lt;p&gt;We are the sloths. We are too stupid to live. We'd rather challenge ourselves with the invention of self-driving cars and the manufacture of unspeakable quantities of batteries for EVs &lt;a class="footnote_link" href="#footnote_text_1" id="footnote_link_1" title="[1] I look forward to EVs replacing ICEs, but they are still not as good as just using fewer cars."&gt;[1]&lt;/a&gt; than consider the possibility that maybe spending thousands of hours in traffic isn't an inherent part of life around which all else must bow down. You'd think they'd figure it out and try something else.&lt;/p&gt;
&lt;p&gt;NJB has made me ashamed to be American. I am ashamed that a country with this much flagrant disregard for sense touts itself as the best nation in the world. I am ashamed that the closest we can get to eco-friendliness is to make our cars run on massive batteries and steer themselves, because simply getting out of the car was never considered an option. I am ashamed that these are my surroundings and that I did not wake up from this &lt;a href="https://www.youtube.com/watch?v=OQE_5MFCekg" title="The Orange Pill"&gt;Matrix&lt;/a&gt; sooner.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/not_just_bikes/scissorhands.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;One of the other big problems with cars, which I don't think NJB has mentioned yet, is that they turn everyone into anonymous metal bubbles. Even without tinted windows, cars provide very little person-to-person visibility, communication, and most importantly, empathy. It is easy to become hateful towards a driver on the road in a way you'd never be towards a person you bumped into on foot, because they are nothing more than a big hotwheel that's sitting in your way. Also because bumping into someone on foot doesn't normally incur thousands of dollars of property damage.&lt;/p&gt;
&lt;p&gt;Tom Scott made a video about &lt;a href="https://www.youtube.com/watch?v=pcVGqtmd2wM"&gt;Peachtree City, Georgia&lt;/a&gt; &lt;a class="footnote_link" href="#footnote_text_2" id="footnote_link_2" title="[2] not to be confused with..."&gt;[2]&lt;/a&gt;. They have a network of asphalt paths designed specifically for travel by golf cart, which I think is really cool and sets a good example for the rest of the country to follow. Small vehicles provide an intermediate option between bicycles and 3,000 pound automobiles, ideal for many of the around-town trips we have to make where a bike might not be enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Travelling with a child or multiple children, or a person with limited mobility.&lt;/li&gt;
&lt;li&gt;Travelling moderate distances in unpleasant weather.&lt;/li&gt;
&lt;li&gt;Shopping for things that are too big or awkward to carry on a bike.&lt;/li&gt;
&lt;li&gt;More things that I currently don't know about because I don't ride bikes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NJB has talked about &lt;a href="https://www.youtube.com/watch?v=B9ly7JjqEb0" title="The Miniature Microcars of Amsterdam"&gt;microcars&lt;/a&gt;, and while the video is still excellent, these don't strike my fancy the same way the golf carts did. Most of the microcars shown in his video follow too much in the design language of regular cars, with their high pillars and rectangulish porthole windows.&lt;/p&gt;
&lt;p&gt;If we were to adopt small vehicles in more areas, I'd like them to remain mostly open-air, or wrapped in clear glass only. Slower modes of travel allow everyone to be closer to each other, and this coupled with high person-to-person visibility will ease traffic interactions and lessen the visceral savage hate that leads to so many of our stupid, pointless crashes. A switch to small vehicles is an opportunity to solve multiple traffic problems at once — let's not squander it by following the same old metal-bubble design. I don't expect actual golf carts to be what sweeps the nation, I just want small vehicles about that size, plus some comforts like weatherproofing and some speakers.&lt;/p&gt;
&lt;p&gt;(See also Micah Toll's glowing review of a 25 mph electric mini-truck from China: &lt;a href="/writing/not_just_bikes/electrek_1.html"&gt;part 1&lt;/a&gt;, &lt;a href="/writing/not_just_bikes/electrek_2.html"&gt;part 2&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Finally, we need more public transit and it needs to provide benefits that cars can't. Sometimes I'll pick a destination on Google maps and ask it to route by transit. For destinations that are within "day trip" distance, the results are usually three to five times as long as travel by car, blowing all possibility of making a day trip out of it. I tried sampling a few destinations from here to the other coast and transit never seems to break even, always 1.5× to 2.5× the travel time of the car.&lt;/p&gt;
&lt;p&gt;I have very little experience riding transit, so I'm not sure how much timing overhead is to be expected, or if these numbers are Americanly bad. Naturally, some time overhead is worth it in exchange for not having to do the driving, and transit routes can never be as direct as driving there yourself. But I really was expecting the trains to go just a little bit faster to make up for it. Notwithstanding that at these distances you're better off enjoying stops along the way rather than gunning for the finish.&lt;/p&gt;
&lt;p&gt;As it stands, it is undeniable that transit is always significantly slower and less practical for day-to-day travel than driving except in the very densest of cities, and only exists for those who have no other options. Improved transit could be a great benefit to our society and atmosphere &lt;a class="footnote_link" href="#footnote_text_3" id="footnote_link_3" title="[3] That's the place we keep our breathable air."&gt;[3]&lt;/a&gt;, but only if it provides enough advantages to be desirable to a person who otherwise could drive a car.&lt;/p&gt;
&lt;p&gt;I can also recommend &lt;a href="https://www.youtube.com/channel/UCGc8ZVCsrR3dAuhvUbkbToQ/videos"&gt;City Beautiful&lt;/a&gt;. In the meantime, try &lt;a href="https://www.youtube.com/watch?v=iGFqfTCL2fs"&gt;breaking traffic waves&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's an exchange from &lt;a href="https://en.wikipedia.org/wiki/How_to_Get_Ahead_in_Advertising"&gt;How to Get Ahead in Advertising&lt;/a&gt;, a movie I'm surprised I hadn't heard of earlier:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;- [Dennis, to his boil] I'd like to see you suffer.&lt;/p&gt;
&lt;p&gt;- [Boil] A typically communist statement.&lt;/p&gt;
&lt;p&gt;- I'm not a communist!&lt;/p&gt;
&lt;p&gt;- Yes, you are. You want to take everyone's car away.&lt;/p&gt;
&lt;p&gt;- I do not want to take anything from anyone. I want to give them the choice of something better.&lt;/p&gt;
&lt;p&gt;- Oh, yes? What?&lt;/p&gt;
&lt;p&gt;- Trains.&lt;/p&gt;
&lt;p&gt;- Trains? Trains are no good. They're old-fashioned. I hate trains. They're rotten.&lt;/p&gt;
&lt;p&gt;- Only because they don't consume. Only because they're already there and don't eat up more and more and more. That's why you hate them. That's why government hates them. That's why they're old-fashioned and rotten.&lt;/p&gt;
&lt;p&gt;- You commies don't half talk a lot of shit.&lt;/p&gt;
&lt;p&gt;- Shut up! I'm not a communist!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And here's a fun, angry comment from Hacker News user brailsafe:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The suburbs are ironically, but not so surprisingly filled with crazy assholes that feel like the world belongs to them, or otherwise have some absurd levels of entitlement. They feel like they own the sidewalks, or if there's no sidewalks, they own the street and everything on it or not on it in front of their house, they feel like they should control whether or not a marginally higher density building should be built down the street or whether a neighbor can paint their house a different colour. When there's no land left, they want more cul-de-sacs built for them because it's the suburban dream and fuck wetlands anyway, and when there's no social services left, they want new ones built for them at everyone else's expense and they don't really want to be part of a community; they want to be by themselves, on their fat asses, in front of their fucking TV, the only quality of which they understand is how big it is.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://news.ycombinator.com/item?id=35106661"&gt;https://news.ycombinator.com/item?id=35106661&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="footnote_text" href="#footnote_link_1" id="footnote_text_1"&gt;[1]&lt;/a&gt; I look forward to EVs replacing ICEs, but they are still not as good as just &lt;a href="https://www.youtube.com/watch?v=OObwqreAJ48&amp;amp;t=6" title="You don't need to own a care if you don't drive to work - carsharing"&gt;using fewer cars&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="footnote_text" href="#footnote_link_2" id="footnote_text_2"&gt;[2]&lt;/a&gt; &lt;a href="/writing/not_just_bikes/peachtrees.jpg"&gt;not to be confused with...&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="footnote_text" href="#footnote_link_3" id="footnote_text_3"&gt;[3]&lt;/a&gt; That's the place we keep our breathable air.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/not_just_bikes/not_just_bikes.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/a8a7281e7f6f2f4a490d39bcb9953712de26aa35"&gt;&lt;time datetime="2024-02-05T19:31:06-08:00"&gt;2024-02-05&lt;/time&gt; Add link to induced demand video.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/b3727336ddb9562c686a4672f4b2070c79f872f7"&gt;&lt;time datetime="2024-01-17T20:19:29-08:00"&gt;2024-01-17&lt;/time&gt; Improve and rearrange some prose; add hierarchy of controls.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/b2687ba8501c014a62100c663323cba8b8547428"&gt;&lt;time datetime="2023-10-08T10:52:00-07:00"&gt;2023-10-08&lt;/time&gt; Fix typo "invention of".&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/f03441cfc6a2670206f45e3c20d69f62074a9fa5"&gt;&lt;time datetime="2023-08-29T21:54:50-07:00"&gt;2023-08-29&lt;/time&gt; Add brailsafe comment.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/96c6abb8cc113fca5887d5464794b6ad1584de13"&gt;&lt;time datetime="2023-01-01T14:07:36-08:00"&gt;2023-01-01&lt;/time&gt; Add How to get Ahead in Advertising quote.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/795031fc24694fa1f6f1443d37a1618a277120a0"&gt;&lt;time datetime="2022-09-27T18:33:36-07:00"&gt;2022-09-27&lt;/time&gt; Add link to newest pedestrian crossing video.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/19046fbe2cd10ecd9d03991e9104f6dc7d02c704"&gt;&lt;time datetime="2022-08-19T17:35:55-07:00"&gt;2022-08-19&lt;/time&gt; Update not_just_bikes.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/99e194ff43df3e26a64d5ca29d4fdc50d8f658f7"&gt;&lt;time datetime="2022-06-02T18:37:24-07:00"&gt;2022-06-02&lt;/time&gt; Link to NJB's Orange Pill video.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/1486c482b617619de918ecf079f40c48c9e10701"&gt;&lt;time datetime="2022-01-07T22:45:34-08:00"&gt;2022-01-07&lt;/time&gt; Add tag life to several articles.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/d6ce0a1acf309ec9b9c0d9e648e3c50e85cdadfd"&gt;&lt;time datetime="2021-10-10T12:42:26-07:00"&gt;2021-10-10&lt;/time&gt; Clarify wording near asphalt ocean.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/16318a53b0baf6885c6c98609b5bc498f047e0a7"&gt;&lt;time datetime="2021-09-17T23:01:59-07:00"&gt;2021-09-17&lt;/time&gt; Add more of my own thoughts regarding solutions.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/02beda73fa846b115f6dc266bea489ef0c13da1e"&gt;&lt;time datetime="2021-09-10T21:56:04-07:00"&gt;2021-09-10&lt;/time&gt; Add not_just_bikes.md.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Sat, 11 Sep 2021 07:56:04 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/not_just_bikes</guid></item><item><title>Snow Lake</title><link>https://sam.hooke.me/trip/2021/09/snow-lake/</link><description>&lt;figure class="single"&gt;
 &lt;div class="single-inset"&gt;
 










 
 
 
 &lt;a href="https://sam.hooke.me/images/trip/2021/snow_lake/w1200/03_lake.jpg"&gt;
 
 &lt;img alt="A deep blue mountain lake, surrounded by forested mountainsides, with a misty cloud rolling onto the surface through the distant trees." src="https://sam.hooke.me/images/trip/2021/snow_lake/w1200/03_lake_hu2111443708171132399.jpg" /&gt;
 
 &lt;/a&gt;
 


 &lt;figcaption style="margin: auto;"&gt;
 
 View of Snow Lake as you descend the ridge.
 
 &lt;/figcaption&gt;
 &lt;/div&gt;
&lt;/figure&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Snow Lake is incredibly popular, and for good reason. The hike is beautiful from start to finish, especially with the low lying blankets of mist that roll through the trees and across the lake. It&amp;rsquo;s barely an hour&amp;rsquo;s drive from Seattle, yet feels like remote, unspoilt wilderness.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Sat, 11 Sep 2021 04:30:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/09/snow-lake/</guid></item><item><title>Code Coverage for Zig with Callgrind</title><link>https://www.ryanliptak.com/blog/code-coverage-zig-callgrind/</link><description>Generating code coverage information for Zig tests&amp;#x2f;binaries using Callgrind</description><author>ryanliptak.com</author><pubDate>Sat, 11 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.ryanliptak.com/blog/code-coverage-zig-callgrind/</guid></item><item><title>🔒 Creating IAM and handling exceptions - 123dev #6</title><link>https://justingarrison.com/123dev/2021-09-11-123dev-6/</link><description>&lt;img alt="a tree bends and breaks under the weight of snow" src="https://justingarrison.com/img/123dev/6-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="the-weight-of-nothing"&gt;The weight of nothing&lt;/h2&gt;
&lt;p&gt;Sometimes we make decisions in software and design that appear to have no impact on what we’re building. After many years of unchecked decisions we may find our systems breaking from a million — seemingly weightless — snowflakes.&lt;/p&gt;
&lt;h2 id="future-of-open-source"&gt;Future of open source&lt;/h2&gt;
&lt;p&gt;Open source is changing and so are the people and communities. What started as free software stored in a drawer back when people used to share computers has evolved into big business and companies trying to protect the type of open source that benefits them. A new generation of users and contributors are getting involved and have their own motives.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Sat, 11 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-11-123dev-6/</guid></item><item><title>Graphene is the new plastic</title><link>https://stop.zona-m.net/2021/09/graphene-is-the-new-plastic/</link><description>&lt;p&gt;Not necessarily in the good ways.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 11 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/graphene-is-the-new-plastic/</guid></item><item><title>Implicit Hiring Criteria</title><link>https://3059274a.danpalmer-me.pages.dev/2021-09-11-implicit-hiring-criteria/</link><description>&lt;p&gt;At Thread I&amp;rsquo;m involved in hiring engineers for frontend, backend and iOS roles.
One of the things I have become more aware of as I have gained experience in
hiring and interviewing is how my biases affect the outcomes of interviews. This
is something I&amp;rsquo;m always trying to improve – to understand what biases I have, to
mitigate their effects – and in the process I have found a mental model that has
helped me.&lt;/p&gt;</description><author>Dan Palmer</author><pubDate>Sat, 11 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://3059274a.danpalmer-me.pages.dev/2021-09-11-implicit-hiring-criteria/</guid></item><item><title>How I Modified a Thule Bike Trailer Hitch for an E-Bike with a 12mm Axle</title><link>https://www.mikekasberg.com/blog/2021/09/10/how-i-modified-a-thule-bike-trailer-hitch-for-an-ebike-with-a-12mm-axle.html</link><description>&lt;p&gt;Thule makes several popular bike trailers, including the
&lt;a href="https://www.thule.com/en-us/bike-trailers/multisport-trailers/thule-cadence-_-1684635"&gt;Cadence&lt;/a&gt;
and
&lt;a href="https://www.thule.com/en-us/strollers/double-jogging-strollers/thule-chariot-lite-2-_-10203022"&gt;Chariot&lt;/a&gt;.
The trailers come with the &lt;a href="https://www.thule.com/en-us/accessories/thule-axle-mount-ezhitcht-cup-with-quick-release-skewer-_-20100796"&gt;Thule
ezHitch&lt;/a&gt;
to attach easily to most quick-release or solid axle wheels, and Thule also
sells an adapter to make the ezHitch fit on thru-axle wheels. This covers most
common bicycle setups, but it leaves out many e-bikes, which often use 12mm
solid rear axles. And that’s really unfortunate because towing a trailer is a
great way to use an e-bike!  I figured out how to modify a Thule ezHitch to fit
on my e-bike with a 12mm solid axle, and I’ll show you how I did it below.&lt;/p&gt;

&lt;p&gt;The Thule ezHitch is compatible with most solid axle bicycles, but most solid
axle bicycles use a 3/8” (~10mm) axle. Larger rear axles are common on e-bikes
because many e-bike designs apply torque against the rear axle and therefore
rely on a sturdy solid axle, often with additional measures (such as anti-spin
washers) to prevent the axle from rotating against the frame. For example, my
&lt;a href="https://www.juicedbikes.com/products/crosscurrentx"&gt;Juiced CrossCurrent&lt;/a&gt; e-bike
has an &lt;a href="https://support.juicedbikes.com/hc/en-us/articles/360056450892-Rear-Wheel-Removal-and-Installation-CC-Service-Guide"&gt;M12x1.25 rear
axle&lt;/a&gt;
and some Rad Power Bikes also have a 12mm rear axle.  Unfortunately, Thule
doesn’t offer any adapter that will fit a 12mm axle, so Thule trailers are
incompatible with many e-bikes – unless you modify the hitch to make it work!&lt;/p&gt;

&lt;div class="warning"&gt;
&lt;p&gt;&lt;b&gt;Do this at your own risk!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;The following is not officially supported by Thule. It will almost certainly
void your warranty, and could result in serious injury or death. I'm not
recommending that you do this or that it's safe to do; I'm just showing you what
I did.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Well, if the hole on the Thule ezHitch is too small, the obvious solution is to
make the hole a little bigger – and that’s actually pretty easy to do. You can
drill through stainless steel using a cobalt drill bit and an ordinary drill. I
picked up a &lt;a href="https://www.homedepot.com/p/Milwaukee-1-2-in-Cobalt-Red-Helix-Twist-Drill-Bit-48-89-2329/203115299"&gt;1/2” cobalt drill
bit&lt;/a&gt;
for about $25 at Home Depot. 1/2” is 12.7mm, so this is slightly larger than the
axle – but 1/2” is a common drill bit size and the extra 0.7mm gives just a
little extra room for error. When researching this idea, I found a YouTube
video on the &lt;a href="https://www.youtube.com/watch?v=ExNQ1ICba8M"&gt;RadCity eBike Vlog&lt;/a&gt;
that shows a similar approach for a different trailer brand – you could watch
that if you want to see what to expect.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Drilling a larger hole in a Thule ezHitch" src="https://www.mikekasberg.com/images/how-i-modified-a-thule/drilling.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;When drilling steel, you should go slower than you would if you were drilling
wood. Running the drill slowly will reduce the amount of heat that builds up,
which will reduce damage to the drill bit. It took several minutes of drilling
for me to get through the layer of steel. While you &lt;em&gt;might&lt;/em&gt; be able to use a
drill bit that isn’t cobalt, it will probably damage the drill bit. My cobalt
drill bit seems to have held up great!&lt;/p&gt;

&lt;p&gt;&lt;img alt="A close up of the ezHitch on an e-bike" src="https://www.mikekasberg.com/images/how-i-modified-a-thule/finished.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;When you’re done drilling, the project is finished. The whole thing only takes
about 5 minutes and you’re ready to install the hitch on your e-bike! The axle
on my Juiced CrossCurrent was long enough that the extra space for the hitch
wasn’t a problem. My hitch now fits perfectly on my e-bike, and pulling my son
around in the trailer is a blast! It’s really nice to have the extra power from
the e-bike when pulling the trailer up a hill or travelling longer distances –
the e-bike with a trailer is a great combo and I’m glad I found a solution to
make my e-bike work with a Thule trailer!&lt;/p&gt;

&lt;div class="message"&gt;
&lt;p&gt;&lt;b&gt;2024 Update&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Some people are curious to know how this has held up for me
over time. Well, it's been more than two years (probably 40+ rides), and the
trailer hitch on the e-bike continues to work great for me!&lt;/p&gt;
&lt;/div&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Fri, 10 Sep 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://www.mikekasberg.com/blog/2021/09/10/how-i-modified-a-thule-bike-trailer-hitch-for-an-ebike-with-a-12mm-axle.html</guid></item><item><title>Error Handling Links</title><link>https://blog.gnoack.org/post/error-handling-links</link><description>&lt;p&gt;This is a collection of interesting literature on the subject of error
handling which I had collected to research &lt;a href="https://blog.gnoack.org/post/error_handling/"&gt;my own blog post on the
subject&lt;/a&gt; a while ago.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;ldquo;&lt;a href="https://www.amazon.com/Philosophy-Software-Design-John-Ousterhout/dp/1732102201"&gt;A Philosophy of Software
Design&lt;/a&gt;&amp;rdquo;
by John Ousterhout has a significant section on error handling that
I found worth a read. It takes a slightly different angle and gives
some good examples on how to design programs in a way so that errors
cannot happen, or only happen in the right places.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href="https://www.amazon.com/Smalltalk-80-Language-Implementation-Adele-Goldberg/dp/0201113716/"&gt;Smalltalk-80 Blue
book&lt;/a&gt;
had the weirdest error handling mechanism: You&amp;rsquo;d call the &lt;code&gt;error:&lt;/code&gt;
method on your own object, and the interactive system pops up a
dialog. Some Smalltalk errors are recoverable in unusual ways
through the interactivity of the system - you can define methods on
the go if your program calls a method that doesn&amp;rsquo;t exist&amp;hellip; :)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Common Lisp has a &amp;ldquo;Conditions&amp;rdquo; system which is similar to exceptions
in that it propagates up the stack, but it inserts a layer in the
middle between handling (&amp;ldquo;catching&amp;rdquo;) and signaling (&amp;ldquo;raising&amp;rdquo;)
exceptions. The middle layer can define recovery strategies which
the handling layer can then choose from.&lt;/p&gt;
&lt;p&gt;I found the Common Lisp stuff in the &amp;ldquo;&lt;a href="https://gigamonkeys.com/book/"&gt;Practical Common
Lisp&lt;/a&gt;&amp;rdquo; book. I cannot claim that I
managed to wrap my head around that one. The chapter is online at
&lt;a href="https://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html"&gt;https://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html&lt;/a&gt;
. Apparently someone wrote a full book on the Common Lisp condition
system recently, but I haven&amp;rsquo;t read that.
&lt;a href="https://amazon.com/Common-Lisp-Condition-System-Mechanisms/dp/148426133X"&gt;https://amazon.com/Common-Lisp-Condition-System-Mechanisms/dp/148426133X&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from that, the books I looked through were not very helpful. I
feel that the Ousterhout approach of explaining this is a useful one,
talking about ways to design programs so that the complications of
error handling are reduced.&lt;/p&gt;
&lt;p&gt;Other notable &amp;ldquo;error handling philosophies&amp;rdquo; I have only some links to&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://python.org/dev/peps/pep-0020/"&gt;https://python.org/dev/peps/pep-0020/&lt;/a&gt; The Zen of Python (&amp;ldquo;errors
should never pass silently&amp;rdquo;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Midori error model
&lt;a href="http://joeduffyblog.com/2016/02/07/the-error-model/"&gt;http://joeduffyblog.com/2016/02/07/the-error-model/&lt;/a&gt; (Midori was an
experimental OS developed at Microsoft)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My own research culminated in this article:
&lt;a href="https://blog.gnoack.org/post/error_handling/"&gt;https://blog.gnoack.org/post/error_handling/&lt;/a&gt;. In hindsight, I took a
much too academic and prescriptive approach there, and so it doesn&amp;rsquo;t
get read much. The article might be biased towards the &amp;ldquo;deployed and
monitored&amp;rdquo; kind of software.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another thing I heard people discuss is the rule to &amp;ldquo;act on an error
in only one place&amp;rdquo;, but I failed to find a canonical source for it.
(It&amp;rsquo;s the rule that is most commonly violated if someone is logging
the same error at multiple layers in the stack, leading to log spam.)&lt;/p&gt;</description><author>Blog on blog.gnoack.org</author><pubDate>Fri, 10 Sep 2021 17:54:54 GMT</pubDate><guid isPermaLink="true">https://blog.gnoack.org/post/error-handling-links</guid></item><item><title>Abstract Space</title><link>https://adamcraven.com/writing/abstract-space/</link><description/><author>Writing on Adam Craven</author><pubDate>Fri, 10 Sep 2021 13:53:24 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/abstract-space/</guid></item><item><title>The quest for a humidifier</title><link>https://liza.io/the-quest-for-a-humidifier/</link><description>&lt;p&gt;Humidifiers and I have a several-year-long history, in which I&amp;rsquo;ve considered countless humidifiers and tried&amp;hellip;a couple. My problem with humidifiers is that I&amp;rsquo;m &lt;em&gt;lazy&lt;/em&gt;. I do not want to scrub them clean. I don&amp;rsquo;t want to even wipe them after each refill. I don&amp;rsquo;t want to find yellow goo under the tank that I have to clean by hand.&lt;/p&gt;</description><author>Liza Shulyayeva</author><pubDate>Fri, 10 Sep 2021 12:20:00 GMT</pubDate><guid isPermaLink="true">https://liza.io/the-quest-for-a-humidifier/</guid></item><item><title>Django Project Initialisation Process</title><link>https://nutcroft.mataroa.blog/blog/django-init-process/</link><description>&lt;p&gt;When I start a new Django project I can never remember what are the steps to arrive at a functional boilerplate with one Django app, the simplest view to get started, and all dependencies set.&lt;/p&gt;
&lt;p&gt;This is a documentation of this process.&lt;/p&gt;
&lt;h2 id="preparation"&gt;Preparation&lt;/h2&gt;
&lt;p&gt;We assume our project’s name is &lt;code&gt;earth&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We assume we have these installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bash&lt;/li&gt;
&lt;li&gt;git&lt;/li&gt;
&lt;li&gt;python3&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="setup-project"&gt;Setup project&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mkdir&lt;span style="color: #657B83;"&gt; &lt;/span&gt;earth&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #268BD2;"&gt;cd&lt;/span&gt;&lt;span style="color: #657B83;"&gt; &lt;/span&gt;earth
git&lt;span style="color: #657B83;"&gt; &lt;/span&gt;init
python3&lt;span style="color: #657B83;"&gt; &lt;/span&gt;-m&lt;span style="color: #657B83;"&gt; &lt;/span&gt;venv&lt;span style="color: #657B83;"&gt; &lt;/span&gt;venv
&lt;span style="color: #268BD2;"&gt;source&lt;/span&gt;&lt;span style="color: #657B83;"&gt; &lt;/span&gt;venv/bin/activate
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="create-requirementsin"&gt;Create &lt;code&gt;requirements.in&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;lt;&amp;lt; EOF &amp;gt; requirements.in&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;django&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="create-requirements_devtxt"&gt;Create &lt;code&gt;requirements_dev.txt&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;lt;&amp;lt; EOF &amp;gt; requirements_dev.txt&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;pip-tools&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;ipdb&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;ipython&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;isort&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;flake8&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;black&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;coverage&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="create-makefile"&gt;Create &lt;code&gt;Makefile&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;lt;&amp;lt; EOF &amp;gt; Makefile&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;.PHONY: format lint cov&lt;/span&gt;

&lt;span style="color: #2AA198;"&gt;lint:&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   flake8 --exclude=.git/,venv/ --ignore=E203,E501,W503&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   isort --check-only --profile black .&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   black --check --exclude '/(\.git|venv)/' .&lt;/span&gt;

&lt;span style="color: #2AA198;"&gt;format:&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   black --exclude '/(\.git|venv)/' .&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   isort --profile black .&lt;/span&gt;

&lt;span style="color: #2AA198;"&gt;cov:&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   coverage run --source='.' --omit 'venv/*' manage.py test&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;   coverage report -m&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="setup-dependencies"&gt;Setup dependencies&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span style="color: #657B83;"&gt; &lt;/span&gt;install&lt;span style="color: #657B83;"&gt; &lt;/span&gt;-r&lt;span style="color: #657B83;"&gt; &lt;/span&gt;requirements_dev.txt
pip&lt;span style="color: #657B83;"&gt; &lt;/span&gt;install&lt;span style="color: #657B83;"&gt; &lt;/span&gt;--upgrade&lt;span style="color: #657B83;"&gt; &lt;/span&gt;pip
pip-compile
pip&lt;span style="color: #657B83;"&gt; &lt;/span&gt;install&lt;span style="color: #657B83;"&gt; &lt;/span&gt;-r&lt;span style="color: #657B83;"&gt; &lt;/span&gt;requirements.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="start-django-project"&gt;Start django project&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;django-admin&lt;span style="color: #657B83;"&gt; &lt;/span&gt;startproject&lt;span style="color: #657B83;"&gt; &lt;/span&gt;earth&lt;span style="color: #657B83;"&gt; &lt;/span&gt;.
django-admin&lt;span style="color: #657B83;"&gt; &lt;/span&gt;startapp&lt;span style="color: #657B83;"&gt; &lt;/span&gt;main
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="reformat-based-on-black"&gt;Reformat based on black&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;make&lt;span style="color: #657B83;"&gt; &lt;/span&gt;format
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="create-gitignore"&gt;Create &lt;code&gt;.gitignore&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;lt;&amp;lt; EOF &amp;gt; .gitignore&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;*.pyc&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;venv/&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="initial-commit"&gt;Initial commit&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git&lt;span style="color: #657B83;"&gt; &lt;/span&gt;add&lt;span style="color: #657B83;"&gt; &lt;/span&gt;.
git&lt;span style="color: #657B83;"&gt; &lt;/span&gt;commit&lt;span style="color: #657B83;"&gt; &lt;/span&gt;-m&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;quot;init django project&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="create-mainviewspy"&gt;Create &lt;code&gt;main/views.py&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;lt;&amp;lt; EOF &amp;gt; main/views.py&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;from django.http import HttpResponse&lt;/span&gt;


&lt;span style="color: #2AA198;"&gt;def index(request):&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;    return HttpResponse(&amp;quot;ok&amp;quot;)&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="create-mainurlspy"&gt;Create &lt;code&gt;main/urls.py&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cat&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;lt;&amp;lt; EOF &amp;gt; main/urls.py&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;from django.urls import path&lt;/span&gt;

&lt;span style="color: #2AA198;"&gt;from main import views&lt;/span&gt;

&lt;span style="color: #2AA198;"&gt;urlpatterns = [&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;    path(&amp;quot;&amp;quot;, views.index, name=&amp;quot;index&amp;quot;),&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;]&lt;/span&gt;
&lt;span style="color: #2AA198;"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="edit-earthurlspy"&gt;Edit &lt;code&gt;earth/urls.py&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Add the &lt;code&gt;include&lt;/code&gt; import:&lt;/p&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #CB4B16;"&gt;from&lt;/span&gt;&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #268BD2;"&gt;django.urls&lt;/span&gt;&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #CB4B16;"&gt;import&lt;/span&gt; &lt;span style="color: #657B83;"&gt;path,&lt;/span&gt; &lt;span style="color: #657B83;"&gt;include&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the index path to include all main app urls:&lt;/p&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;path(&lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt; &lt;span style="color: #657B83;"&gt;include(&lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;quot;main.urls&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;)),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="edit-earthsettingspy"&gt;Edit &lt;code&gt;earth/settings.py&lt;/code&gt;&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #CB4B16;"&gt;import&lt;/span&gt;&lt;span style="color: #657B83;"&gt; &lt;/span&gt;&lt;span style="color: #268BD2;"&gt;os&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;SECRET_KEY&lt;/span&gt; &lt;span style="color: #93A1A1;"&gt;=&lt;/span&gt; &lt;span style="color: #657B83;"&gt;os&lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;.&lt;/span&gt;&lt;span style="color: #657B83;"&gt;environ&lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;.&lt;/span&gt;&lt;span style="color: #657B83;"&gt;get(&lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;quot;SECRET_KEY&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt; &lt;span style="color: #2AA198;"&gt;&amp;quot;nonrandom_secret&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;DEBUG&lt;/span&gt; &lt;span style="color: #93A1A1;"&gt;=&lt;/span&gt; &lt;span style="color: #2AA198;"&gt;True&lt;/span&gt; &lt;span style="color: #859900;"&gt;if&lt;/span&gt; &lt;span style="color: #657B83;"&gt;os&lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;.&lt;/span&gt;&lt;span style="color: #657B83;"&gt;environ&lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;.&lt;/span&gt;&lt;span style="color: #657B83;"&gt;get(&lt;/span&gt;&lt;span style="color: #2AA198;"&gt;&amp;quot;NODEBUG&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;)&lt;/span&gt; &lt;span style="color: #859900;"&gt;is&lt;/span&gt; &lt;span style="color: #2AA198;"&gt;None&lt;/span&gt; &lt;span style="color: #859900;"&gt;else&lt;/span&gt; &lt;span style="color: #2AA198;"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;ALLOWED_HOSTS&lt;/span&gt; &lt;span style="color: #93A1A1;"&gt;=&lt;/span&gt; &lt;span style="color: #657B83;"&gt;[&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;127.0.0.1&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
&lt;span style="color: #657B83;"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span style="color: #93A1A1;"&gt;=&lt;/span&gt; &lt;span style="color: #657B83;"&gt;[&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;main.apps.MainConfig&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;django.contrib.admin&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;django.contrib.auth&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;django.contrib.contenttypes&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;django.contrib.sessions&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;django.contrib.messages&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
    &lt;span style="color: #2AA198;"&gt;&amp;quot;django.contrib.staticfiles&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;,&lt;/span&gt;
&lt;span style="color: #657B83;"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;STATIC_URL&lt;/span&gt; &lt;span style="color: #93A1A1;"&gt;=&lt;/span&gt; &lt;span style="color: #2AA198;"&gt;&amp;quot;/static/&amp;quot;&lt;/span&gt;
&lt;span style="color: #657B83;"&gt;STATIC_ROOT&lt;/span&gt; &lt;span style="color: #93A1A1;"&gt;=&lt;/span&gt; &lt;span style="color: #657B83;"&gt;os&lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;.&lt;/span&gt;&lt;span style="color: #657B83;"&gt;path&lt;/span&gt;&lt;span style="color: #93A1A1;"&gt;.&lt;/span&gt;&lt;span style="color: #657B83;"&gt;join(BASE_DIR,&lt;/span&gt; &lt;span style="color: #2AA198;"&gt;&amp;quot;static&amp;quot;&lt;/span&gt;&lt;span style="color: #657B83;"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="migrate-and-run"&gt;Migrate and run&lt;/h2&gt;
&lt;div style="background: #fdf6e3;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;python&lt;span style="color: #657B83;"&gt; &lt;/span&gt;manage.py&lt;span style="color: #657B83;"&gt; &lt;/span&gt;migrate
python&lt;span style="color: #657B83;"&gt; &lt;/span&gt;manage.py&lt;span style="color: #657B83;"&gt; &lt;/span&gt;runserver
&lt;/pre&gt;&lt;/div&gt;</description><author>nutcroft</author><pubDate>Fri, 10 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nutcroft.mataroa.blog/blog/django-init-process/</guid></item><item><title>HTML, queues, and whiteboarding - 123dev #5</title><link>https://justingarrison.com/123dev/2021-09-10-123dev-5/</link><description>&lt;img alt="Two ventilation towers collapse in sync" src="https://justingarrison.com/img/123dev/5-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="html"&gt;HTML&lt;/h2&gt;
&lt;p&gt;There were some conversations on Twitter this past weekend that HTML is not a programming language. I don’t care about semantics, but I &lt;strong&gt;DO&lt;/strong&gt; care to recognize people that write HTML as developers.&lt;/p&gt;
&lt;p&gt;My first full-time job I wrote code to solve problems even though I didn’t have “developer” in my title. I automated a job that used to take 3 weeks down to 3 hours with the code I wrote. It just so happened that my IDE was Excel.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 10 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-10-123dev-5/</guid></item><item><title>Prove them right! Not wrong.</title><link>https://www.unsungnovelty.org/posts/09/2021/prove-them-right-not-wrong./</link><description>&lt;blockquote&gt;
&lt;p&gt;License: &lt;a href="https://creativecommons.org/licenses/by-nc-nd/4.0/"&gt;Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
_International Public License (CC BY-NC-ND
_4.0)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Conversations are important to learn and understand things better. It is one of the fundamental ways we learn and grow. It helps us to reach conclusions and validates our thoughts/ideas on what is &amp;ldquo;right&amp;rdquo; and what is &amp;ldquo;wrong&amp;rdquo;. But if you have observed, we end up having conversations which are pretty much one sided most times. Or it ends up being a debate where the last man standing wins. A productive conversation is getting hard to come by these days. Cos we have our own knowledge that defines what is right and what is wrong. That is not the problem though. The problem is that we are not ready to open up to the idea that we could be wrong about what is &amp;ldquo;right&amp;rdquo; and what is &amp;ldquo;wrong&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;So how could we understand our biases better? How can we open up to the idea that we could be wrong? How can we have more productive conversations? An easy way to test your biases is to try to talk to the person you are having conversations and prove them right! Thereby trying to prove yourself &amp;ldquo;wrong&amp;rdquo;. This puts your thoughts and ideas into testing and helps you listen. So instead of noding along until the person stops talking so that you can prove them wrong, you tolerate and hopefullly listen better about what the other person has to say.&lt;/p&gt;
&lt;p&gt;This also makes it easy for you to callout your own biases easily in a conversation. To be very clear, this doesn&amp;rsquo;t mean you should defend everything the other person says though. It is just a simple test to check how biased you are when going into a conversation. Where the byproduct could be better listening and tolerance to opposite views.&lt;/p&gt;
&lt;p&gt;This only works when you are open to the possibility of thinking that you could be wrong. So be open next time you are having a conversation. Try to prove them &amp;ldquo;right&amp;rdquo; when you are trying to have a conversation with someone. And maybe you would be able to uncover your biases. Or validate your thoughts. So, prove them right. Not wrong!&lt;/p&gt;
&lt;h2 id="references--links"&gt;References / links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Principle_of_charity"&gt;https://en.wikipedia.org/wiki/Principle_of_charity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Steelmanning"&gt;https://en.wikipedia.org/wiki/Steelmanning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have thoughts or feedback regarding my post/writing? DM me on Twitter &lt;a href="https://twitter.com/unsungNovelty"&gt;@unsungNovelty&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description><author>unsungNovelty</author><pubDate>Thu, 09 Sep 2021 14:36:37 GMT</pubDate><guid isPermaLink="true">https://www.unsungnovelty.org/posts/09/2021/prove-them-right-not-wrong./</guid></item><item><title>One more proof that cyberspace does not exist? Great!</title><link>https://stop.zona-m.net/2021/09/one-more-proof-that-cyberspace-does-not-exist-great/</link><description>&lt;p&gt;So we can solve REAL problems, instead.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 09 Sep 2021 11:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/one-more-proof-that-cyberspace-does-not-exist-great/</guid></item><item><title>Learn Crystal with Practice Problems - Part 1: Variables Math and Strings</title><link>https://bartlomiejmika.com/posts/2021/learn-crystal-lang-with-practice-problems-part-1-variables-math-and-strings/</link><description>&lt;p&gt;Are you interested in learning &lt;a href="https://bartlomiejmika.com/tags/crystal-lang/"&gt;#Crystal-Lang&lt;/a&gt; but having difficulty because you are a &lt;a href="https://en.wikipedia.org/wiki/Kinesthetic_learning"&gt;tactile learner&lt;/a&gt;? The purpose of this tutorial is to provide you practice problems you can try out at your leisure.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Thu, 09 Sep 2021 06:38:40 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/learn-crystal-lang-with-practice-problems-part-1-variables-math-and-strings/</guid></item><item><title>Error Handling in Go REST APIs</title><link>https://alexrichey.com/articles/2021-09-09-error-handling-in-go-rest-apis.html</link><description>A pattern for distinguishing and handling client and internal errors in Go that leverages the power of Go’s interfaces.</description><author>Alex Richey</author><pubDate>Thu, 09 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://alexrichey.com/articles/2021-09-09-error-handling-in-go-rest-apis.html</guid></item><item><title>Docker Gripes</title><link>https://blog.janissary.xyz/posts/docker-gripes</link><author>janissary</author><pubDate>Thu, 09 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.janissary.xyz/posts/docker-gripes</guid></item><item><title>The Curious Case of the Dot-Com Link Farms</title><link>https://www.marginalia.nu/log/20-dot-com-link-farms/</link><description>&lt;p&gt;I spent some time today weeding out yet more link-farms from my search engine&amp;rsquo;s index.&lt;/p&gt;
&lt;p&gt;Typically what I would do is just block the subnet assigned to the VPS provider they&amp;rsquo;re on, and that does seem to work rather well. The cloud providers that don&amp;rsquo;t police what they host is almost always home to quite a lot of this stuff, so I don&amp;rsquo;t particularly mind scorching some earth in the name of a clean index.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Thu, 09 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/20-dot-com-link-farms/</guid></item><item><title>📃 Make your resume stand out and write your docs with semantic line breaks - 123dev #4</title><link>https://justingarrison.com/123dev/2021-09-09-123dev-4/</link><description>&lt;img alt="a cat waving at something" src="https://justingarrison.com/img/123dev/4-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="open-source"&gt;Open Source&lt;/h2&gt;
&lt;p&gt;There’s been a lot of open source news this week between my current employer and Elastic. I have nothing to do with it directly, but it reminds me that open source is people which means there will always be drama.&lt;/p&gt;
&lt;p&gt;Even when it’s billion dollar companies it often comes down to a small number of people making decisions. I think this weeks drama hurt the image of open source more than it helped.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 09 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-09-123dev-4/</guid></item><item><title>Promising news for right to repair from Germany</title><link>https://stop.zona-m.net/2021/09/promising-news-for-right-to-repair-from-germany/</link><description>&lt;p&gt;New rules say something obvious, but never said enough.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 08 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/promising-news-for-right-to-repair-from-germany/</guid></item><item><title>Daft Punk's legendary Alive 2007 concert with remastered audio and 4K video</title><link>https://nicolaiarocci.com/daft-punks-legendary-alive-2007-concert-with-remastered-audio-and-4k-video/</link><description>&lt;p&gt;Someone did it &amp;ndash;the perfect Daft Punk tribute after their break up. Also ideal
for those hard-coding sessions.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For the first time in 14 years, experience Daft Punk like never before! The
first professionally recorded live show; revived meticulously by remastering
and restoring the audio and video. The Alive 2007 show is considered a staple
in live performances featuring the signature &amp;lsquo;Pyramid&amp;rsquo; and groundbreaking
visuals, which is now complemented by a fully remastered audio track which
aims to maximize the experience. Presenting the ultimate authentic Daft Punk
experience.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 08 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/daft-punks-legendary-alive-2007-concert-with-remastered-audio-and-4k-video/</guid></item><item><title>The Small Website Discoverability Crisis</title><link>https://www.marginalia.nu/log/19-website-discoverability-crisis/</link><description>&lt;p&gt;There are a lot of small websites on the Internet: Interesting websites, beautiful websites, unique websites.&lt;/p&gt;
&lt;p&gt;Unfortunately they are incredibly hard to find. You cannot find them on Google or Reddit, and while you can stumble onto them with my search engine, it is not in a very directed fashion.&lt;/p&gt;
&lt;p&gt;It is an unfortunate state of affairs. Even if you do not particularly care for becoming the next big thing, it&amp;rsquo;s still discouraging to put work into a website and get next to no traffic beyond the usual bots.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 08 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/19-website-discoverability-crisis/</guid></item><item><title>Gradle run two tasks of the same type</title><link>https://b.agilob.net/programming/java/gradle-two-tasks-openapi/</link><description>I have this setup in a single project which handles backend and frontend generation of server and client code. This requires to run openapi-generator twice, once for backend with spring generator and once for frontend with typescript-angular generator. I need backend code to be generated to build directory - so it is not committed to version control. TypeScript code needs to be reformatted and committed to git.
TypeScript code also requires additional mapping due to non-standard structure of my specification.</description><author>Random Notes by agilob</author><pubDate>Wed, 08 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/programming/java/gradle-two-tasks-openapi/</guid></item><item><title>AWS as a framework, pair terminals, and bash script template - 123dev #3</title><link>https://justingarrison.com/123dev/2021-09-08-123dev-3/</link><description>&lt;img alt="A man hits his head on a table and turns into snow" src="https://justingarrison.com/img/123dev/3-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;h2 id="learning-to-code"&gt;Learning to code&lt;/h2&gt;
&lt;p&gt;The more programming languages I learn the more it resembles human language for me. It takes a lot of practice to write content that humans want to read. It takes similar practice to write code that computers want to execute.&lt;/p&gt;
&lt;p&gt;If I start writing in another language the meaning of the words might be the same, but how they will be interpreted by the readers could be completely different.&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Wed, 08 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-08-123dev-3/</guid></item><item><title>Book review &amp;amp; notes -  Looking for Information</title><link>https://river.me/blog/book-review-looking-for-information/</link><description>A review of, including chapter-by-chapter notes on, &amp;ldquo;Looking for Information: A Survey of Research on Information Seeking, Needs, and Behavior&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 07 Sep 2021 23:33:43 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-looking-for-information/</guid></item><item><title>If reskilling is not quick enough, it's not COVID's fault</title><link>https://stop.zona-m.net/2021/09/if-reskilling-is-not-quick-enough-its-not-covids-fault/</link><description>&lt;p&gt;It cannot be. It just can&amp;rsquo;t.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 07 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/if-reskilling-is-not-quick-enough-its-not-covids-fault/</guid></item><item><title>Learning loops</title><link>https://minireference.com/blog/learning-loops/</link><description>I was talking with friends recently about an interesting phenomenon that all self-taught programmers have observed, which we ended up calling &amp;#8220;learning loops.&amp;#8221; A learning loop is a process in which learners are motivated to advance their knowledge thanks to the positive feedback on their performance. In this blog post, I want to look at [&amp;#8230;]</description><author>Minireference blog</author><pubDate>Tue, 07 Sep 2021 17:34:11 GMT</pubDate><guid isPermaLink="true">https://minireference.com/blog/learning-loops/</guid></item><item><title>Connect Jupyter to Remote</title><link>https://blog.lukesalamone.com/posts/connect-jupyter-to-remote/</link><description>&lt;p&gt;Here&amp;rsquo;s how to connect to a remote Jupyter notebook.&lt;/p&gt;
&lt;p&gt;Create an ssh tunnel to your remote machine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -L 8080:localhost:8080 user@12.34.56.78

# or use a .pem file to connect to ec2
ssh -L 8080:localhost:8080 -i &amp;quot;aws.pem&amp;quot; ec2-user@ec2-12-34-56-78.compute-1.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start Jupyter on that machine in headless mode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jupyter notebook --no-browser --port=8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use a browser to open one of the urls that Jupyter presents:&lt;br /&gt;
http://localhost:8080/?token=xyz&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Tue, 07 Sep 2021 17:10:56 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/connect-jupyter-to-remote/</guid></item><item><title>DIY cloud gaming setup with VFIO, Parsec and AMD</title><link>https://ounapuu.ee/posts/2021/09/07/diy-cloud-gaming-vfio-parsec-amd/</link><description>A build log of how I set up my own 'cloud gaming' setup, including all the issues I faced and attempts to fix those.</description><author>./techtipsy</author><pubDate>Tue, 07 Sep 2021 06:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/09/07/diy-cloud-gaming-vfio-parsec-amd/</guid></item><item><title>The Toyota Way</title><link>https://june.kim/the-toyota-way/</link><author>june.kim</author><pubDate>Tue, 07 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/the-toyota-way/</guid></item><item><title>Comparing SIMD on x86-64 and arm64</title><link>https://blog.yiningkarlli.com/2021/09/neon-vs-sse.html</link><description>&lt;p&gt;&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;

&lt;div class="tableofcontents"&gt;
    &lt;div class="tableofcontents-row"&gt;
        &lt;div class="tableofcontents-column3"&gt;
            &lt;div class="tableofcontents-content"&gt;
                1. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-introduction"&gt;Introduction&lt;/a&gt;&lt;br /&gt;
                2. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-4-wide-ray-aabb-intersection"&gt;4-Wide Ray Bounding Box Intersection&lt;/a&gt;&lt;br /&gt;
                3. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-test-program-setup"&gt;Test Program Setup&lt;/a&gt;&lt;br /&gt;
                4. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-introduction-sse-neon-structs"&gt;Shared SSE/Neon Structs&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column3"&gt;
            &lt;div class="tableofcontents-content"&gt;
                5. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-scalar-implementations"&gt;Scalar Implementations&lt;/a&gt;&lt;br /&gt;
                6. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-sse-implementation"&gt;SSE Implementation&lt;/a&gt;&lt;br /&gt;
                7. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-neon-implementation"&gt;Neon Implementation&lt;/a&gt;&lt;br /&gt;
                8. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-autovector-implementation"&gt;Auto-Vectorized Implementation&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column3"&gt;
            &lt;div class="tableofcontents-content"&gt;
                9. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-ispc-implementation"&gt;ISPC Implementation&lt;/a&gt;&lt;br /&gt;
                10. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-results"&gt;Final Results and Conclusions &lt;/a&gt;&lt;br /&gt;
                11. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-addenda"&gt;Addenda&lt;/a&gt;&lt;br /&gt;
                12. &lt;a href="/2021/09/neon-vs-sse.html#2021-09-07-references"&gt;References&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div id="2021-09-07-introduction"&gt;&lt;/div&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;I recently wrote a big two-part series about a ton of things that I learned throughout the process of porting my hobby renderer, Takua Renderer, to 64-bit ARM.
In the &lt;a href="https://blog.yiningkarlli.com/2021/07/porting-takua-to-arm-pt2.html"&gt;second part&lt;/a&gt;, one of the topics I covered was how the Embree ray tracing kernels library gained arm64 support by utilizing the sse2neon project to emulate x86-64 SSE2 SIMD instructions using arm64’s Neon SIMD instructions.
In the second part of the series, I had originally planned on diving much deeper into comparing writing vectorized code using SSE intrinsics versus using Neon intrinsics versus other approaches, but the comparison write-up became so large that I wound up leaving it out of the original post with the intention of making the comparison into its own standalone post.
This post is that standalone comparison!&lt;/p&gt;

&lt;p&gt;As discussed in my porting to arm64 series, a huge proportion of the raw compute power in modern CPUs is located in vector &lt;a href="https://en.wikipedia.org/wiki/SIMD"&gt;SIMD instruction set extensions&lt;/a&gt;, and lots of things in computer graphics happen to be be workload types that fit vectorization very well.
Long-time readers of this blog probably already know what SIMD instructions do, but for the unfamiliar, here’s a very brief summary.
SIMD stands for &lt;em&gt;single instruction, multiple data&lt;/em&gt;, and is a type of parallel programming that exploits &lt;em&gt;data level parallelism&lt;/em&gt; instead of concurrency.
What the above means is that, unlike multithreading, in which multiple different streams of instructions simultaneously execute on different cores over different pieces of data, in a SIMD program, a single instruction stream executes simultaneously over different pieces of data.
For example, a 4-wide SIMD multiplication instruction would simultaneously execute a single multiply instruction over four pairs of numbers; each pair is multiplied together at the same time as the other pairs.
SIMD processing makes processors more powerful by allowing the processor to process more data within the same clock cycle; many modern CPUs implement SIMD extensions to their base scalar instruction sets, and modern GPUs are at a very high level broadly similar to huge ultra-wide SIMD processors.&lt;/p&gt;

&lt;p&gt;Multiple approaches exist today for writing vectorized code.
The four main ways available today are: directly write code using SIMD assembly instructions, write code using compiler-provided vector intrinsics, write normal scalar code and rely on compiler auto-vectorization to emit vectorized assembly, or write code using ISPC: the Intel SPMD Program Compiler.
Choosing which approach to use for a given project requires considering many different tradeoffs and factors, such as ease of programming, performance, and portability.
Since this post is looking at comparing SSE2 and Neon, portability is especially interesting here.
Auto-vectorization and ISPC are the most easily portable approaches, while vector intrinsics can be made portable using sse2neon, but each of these approaches requires different trade-offs in other areas.&lt;/p&gt;

&lt;p&gt;In this post, I’ll compare vectorizing the same snippet of code using several different approaches.
On x86-64, I’ll compare implementations using SSE intrinsics, using auto-vectorization, and using ISPC emitting SSE assembly.
On arm64, I’ll compare implementations using Neon intrinsics, using SSE intrinsics emulated on arm64 using sse2neon, using auto-vectorization, and using ISPC emitting Neon assembly.
I’ll also evaluate how each approach does in balancing portability, ease-of-use, and performance.&lt;/p&gt;

&lt;div id="2021-09-07-4-wide-ray-aabb-intersection"&gt;&lt;/div&gt;
&lt;h2 id="4-wide-ray-bounding-box-intersection"&gt;4-wide Ray Bounding Box Intersection&lt;/h2&gt;

&lt;p&gt;For my comparisons, I wanted to use a small but practical real-world example.
I wanted something small since I wanted to be able to look at the assembly output directly, and keeping things smaller makes the assembly output easier to read all at once.
However, I also wanted something real-world to make sure that whatever I learned wasn’t just the result of a contrived artificial example.
The comparison that I picked is a common operation inside of ray tracing: 4-wide ray bounding box intersection.
By 4-wide, I mean intersecting the same ray against four bounding boxes at the same time.
Ray bounding box intersection tests are a fundamental operation in BVH traversal, and typically account for a large proportion (often a majority) of the computational cost in ray intersection against the scene.
Before we dive into code, here’s some background on BVH traversal and the role that 4-wide ray bounding box intersection plays in modern ray tracing implementations.&lt;/p&gt;

&lt;p&gt;Acceleration structures are a critical component of ray tracing; tree-based acceleration structures convert tracing a ray against a scene from being a O(&lt;em&gt;N&lt;/em&gt;) problem into a O(&lt;em&gt;log(N)&lt;/em&gt;) problem, where &lt;em&gt;N&lt;/em&gt; is the number of objects that are in the scene.
For scenes with lots of objects and for objects made up of lots of primitives, lowering the worst-case complexity of ray intersection from linear to logarithmic is what makes the difference between ray tracing being impractical and practical.
From roughly the late 90s through to the early 2010s, a number of different groups across the graphics field put an enormous amount of research and effort into establishing the best possible acceleration structures.
Early on, the broad general consensus was that KD-trees were the most efficient acceleration structure for ray intersection performance, while BVHs were known to be faster to build than KD-trees but less performant at actual ray intersection.
However, advancements over time improved BVH ray intersection performance &lt;a href="https://doi.org/10.1145/1572769.1572771"&gt;[Stich et al. 2009]&lt;/a&gt; to the point where today, BVHs are now the dominant acceleration structure used in pretty much every production ray tracing solution.
For a history and detailed survey of BVH research over the past twenty-odd years, please refer to Meister et al. &lt;a href="https://doi.org/10.1111/cgf.142662"&gt;[2021]&lt;/a&gt;.
One interesting thing to note when looking through the past twenty years of ray tracing acceleration research are the author names; many of these authors are the same people that went on to create the modern underpinnings of Embree, Optix, and the ray acceleration hardware found in NVIDIA’s RTX GPUs.&lt;/p&gt;

&lt;p&gt;A BVH is a tree structure where bounding boxes are placed over all of the objects that need to be intersected, and then these bounding boxes are grouped into (hopefully) spatially local groups.
Each group is then enclosed in another bounding box, and these boxes are grouped again, and so on and so forth until a top-level bounding box is reached that contains everything below.
In university courses, BVHs are traditionally taught as being binary trees, meaning that each node within the tree structure bounds two children nodes.
Binary BVHs are the simplest possible BVH to build and implement, hence why they’re usually the standard version taught in schools.
However, the actual branching factor at each BVH node doesn’t have to be binary; the branching factor can be any integer number greater than 2.
BVHs with 4 and even 8 wide branching factors have largely come to dominate production usage today.&lt;/p&gt;

&lt;p&gt;The reason production BVHs today tend to have wide branching factors originates in the need to vectorize BVH traversal in order to utilize the maximum possible performance of SIMD-enabled CPUs.
Early attempts at vectorizing BVH traversal centered around tracing groups, or packets, of multiple rays through a BVH together; packet tracing allows for simultaneously intersecting N rays against a single bounding box at each node in the hierarchy &lt;a href="https://doi.org/10.1111/1467-8659.00508"&gt;[Wald et al. 2001]&lt;/a&gt;, where N is the vector width.
However, packet tracing only really works well for groups of rays that are all going in largely the same direction from largely the same origin; for incoherent rays, divergence in the traversal path each incoherent ray needs to take through the BVH destroys the efficacy of vectorized packet traversal.
To solve this problem, several papers concurrently proposed a different solution to vectorizing BVH traversal &lt;a href="https://doi.org/10.1109/RT.2008.4634620"&gt;[Wald et al. 2008&lt;/a&gt;, &lt;a href="https://doi.org/10.1109/RT.2008.4634618"&gt;Ernst and Greiner 2008&lt;/a&gt;, &lt;a href="https://doi.org/10.1111/j.1467-8659.2008.01261.x"&gt;Dammertz et al. 2008]&lt;/a&gt;: instead of simultaneously intersecting N rays against a single bounding box, this new solution simultaneously intersects a single ray against N bounding boxes.
Since the most common SIMD implementations are at least 4 lanes wide, BVH implementations that want to take maximum advantage of SIMD hardware also need to be able to present four bounding boxes at a time for vectorized ray intersection, hence the move from a splitting factor of 2 to a splitting factor of 4 or even wider.
In addition to being more performant when vectorized, a 4-wide splitting factor also tends to reduce the depth and therefore memory footprint of BVHs, and 4-wide BVHs have also been demonstrated to be able to outperform 2-wide BVHs even without vectorization &lt;a href="https://psychopath.io/post/2017_08_03_bvh4_without_simd"&gt;[Vegdahl 2017]&lt;/a&gt;.
Vectorized 4-wide BVH traversal can also be combined with the previous packet approach to yield even better performance for coherent rays &lt;a href="https://doi.org/10.1145/1572769.1572793"&gt;[Tsakok 2009]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All of the above factors combined are why BVHs with wider branching factors are more popularly used today on the CPU; for example, the widely used Embree library &lt;a href="https://doi.org/10.1145/2601097.2601199"&gt;[Wald et al. 2014]&lt;/a&gt; offers 4-wide as the &lt;em&gt;minimum&lt;/em&gt; supported split factor, and supports even wider split factors when vectorizing using wider AVX instructions.
On the GPU, the story is similar, although a little bit more complex since the GPU’s SIMT (as opposed to SIMD) parallelism model changes the relative importance of being able to simultaneously intersect one ray against multiple boxes.
GPU ray tracing systems today use a variety of different split factors; AMD’s RDNA2-based GPUs implement hardware acceleration for a 4-wide BVH &lt;a href="https://gpuopen.com/rdna2-isa-available/"&gt;[AMD 2020]&lt;/a&gt;.
NVIDIA does not publicly disclose what split factor their RTX GPUs assume in hardware, since their various APIs for accessing the ray tracing hardware are designed to allow for changing out for different, better future techniques under the hood without modification to client applications.
However, we can guess that support for multiple different splitting factors seems likely given that Optix 7 uses different splitting factors depending on whether an application wants to prioritize BVH construction speed or BVH traversal speed &lt;a href="https://raytracing-docs.nvidia.com/optix7/guide/index.html"&gt;[NVIDIA 2021]&lt;/a&gt;.
While not explicitly disclosed, as of writing, we can reasonable guess based off of what Optix 6.x implemented that Optix 7’s fast construction mode implements a TRBVH &lt;a href="https://doi.org/10.1145/2492045.2492055"&gt;[Karras and Aila 2013]&lt;/a&gt;, which is a binary BVH, and that Optix 7’s performance-optimized mode implements a 8-wide BVH with compression &lt;a href="https://doi.org/10.1145/3105762.3105773"&gt;[Ylitie et al. 2017]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since the most common splitting factor in production CPU cases in a 4-wide split, and since SSE and Neon are both 4-wide vector instruction sets, I think the core simultaneous single-ray-4-box intersection test is a perfect example case to look at!
To start off, we need an efficient intersection test between a single ray and a single axis-aligned bounding box.
I’ll be using the commonly utilized solution by Williams et al. &lt;a href="https://doi.org/10.1080/2151237X.2005.10129188"&gt;[2005]&lt;/a&gt;; improved techniques with better precision &lt;a href="http://jcgt.org/published/0002/02/02/"&gt;[Ize 2013]&lt;/a&gt; and more generalized flexibility &lt;a href="http://jcgt.org/published/0007/03/04/"&gt;[Majercik 2018]&lt;/a&gt; do exist, but I’ll stick with the original Williams approach in this post to keep things simple.&lt;/p&gt;

&lt;div id="2021-09-07-test-program-setup"&gt;&lt;/div&gt;
&lt;h2 id="test-program-setup"&gt;Test Program Setup&lt;/h2&gt;

&lt;p&gt;Everything in this post is implemented in a small test program that I have &lt;a href="https://github.com/betajippity/sseneoncompare"&gt;put in an open Github repository&lt;/a&gt;, licensed under the Apache-2.0 License.
Feel free to clone the repository for yourself to follow along using or to play with!
To build and run the test program yourself, you will need a version of &lt;a href="https://cmake.org"&gt;CMake&lt;/a&gt; that has ISPC support (so, CMake 3.19 or newer), a modern C++ compiler with support for C++17, and a version of &lt;a href="https://ispc.github.io"&gt;ISPC&lt;/a&gt; that supports Neon output for arm64 (so, ISPC v1.16.1 or newer); further instructions for building and running the test program is included in the repository’s README.md file.
The test program compiles and runs on both x86-64 and arm64; on each processor architecture, the appropriate implementations for each processor architecture are automatically chosen for compilation.&lt;/p&gt;

&lt;p&gt;The test program runs each single-ray-4-box intersection test implementation N times, where N is an integer that can be set by the user as the first input argument to the program.
By default, and for all results in this post, N is set to 100000 runs.
The four bounding boxes that the intersection tests run against are hardcoded into the test program’s main function and are reused for all N runs.
Since the bounding boxes are hardcoded, I had to take some care to make sure that the compiler wasn’t going to pull any optimization shenanigans and not actually run all N runs.
To make sure of the above, the test program is compiled in two separate pieces: all of the actual ray-bounding-box intersection functions are compiled into a static library using &lt;code class="language-plaintext highlighter-rouge"&gt;-O3&lt;/code&gt; optimization, and then the test program’s main function is compiled separately with all optimizations disabled, and then the intersection functions static library is linked in.&lt;/p&gt;

&lt;p&gt;Ideally I would have liked to set up the project to compile directly to a Universal Binary on macOS, but unfortunately CMake’s built-in infrastructure for compiling multi-architecture binaries doesn’t really work with ISPC at the moment, and I was too lazy to manually set up custom CMake scripts to invoke ISPC multiple times (once for each target architecture) and call the macOS &lt;code class="language-plaintext highlighter-rouge"&gt;lipo&lt;/code&gt; tool; I just compiled and ran the test program separately on an x86-64 Mac and on an arm64 Mac.
However, on both the x86-64 and arm64 systems, I used the same operating system and compilers.
For all of the results in this post, I’m running on macOS 11.5.2 and I’m compiling using Apple Clang v12.0.5 (which comes with Xcode 12.5.1) for C++ code and ISPC v1.16.1 for ISPC code.&lt;/p&gt;

&lt;p&gt;For the rest of the post, I’ll include results for each implementation in the section discussing that implementation, and then include all results together in a &lt;a href="#2021-09-07-results"&gt;results section&lt;/a&gt; at the end.
All results were generated by running on a 2019 16 inch MacBook Pro with a Intel Core i7-9750H CPU for x86-64, and on a 2020 M1 Mac Mini for arm64 and Rosetta 2.
All results were generated by running the test program with 100000 runs per implementation, and I averaged results across 5 runs of the test program after throwing out the highest and lowest result for each implementation to discard outliers.
The timings reported for each implementation are the average across 100000 runs.&lt;/p&gt;

&lt;div id="2021-09-07-introduction-sse-neon-structs"&gt;&lt;/div&gt;
&lt;h2 id="defining-structs-usable-with-both-sse-and-neon"&gt;Defining Structs Usable with Both SSE and Neon&lt;/h2&gt;

&lt;p&gt;Before we dive into the ray-box intersection implementations, I need to introduce and describe the handful of simple structs that the test program uses.
The most widely used struct in the test program is &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;, which defines a 4-dimensional float vector by simply wrapping around four floats.
&lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; has one important trick: &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; uses a union to accomplish type punning, which allows us to access the four floats in &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; either as separate individual floats, or as a single &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; when using SSE or a single &lt;code class="language-plaintext highlighter-rouge"&gt;float32x4_t&lt;/code&gt; when using Neon.
&lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; on SSE and &lt;code class="language-plaintext highlighter-rouge"&gt;float32x4_t&lt;/code&gt; on Neon serve the same purpose; since SSE and Neon use 128-bit wide registers with four 32-bit “lanes” per register, intrinsics implementations for SSE and Neon need a 128-bit data type that maps directly to the vector register when compiled.
The SSE intrinsics implementation defined in &lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;xmmintrin.h&amp;gt;&lt;/code&gt; uses &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; as its single generic 128-bit data type, whereas the Neon intrinsics implementation defined in &lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;arm_neon.h&amp;gt;&lt;/code&gt; defines separate 128-bit types depending on what is being stored.
For example, Neon intrinsics use &lt;code class="language-plaintext highlighter-rouge"&gt;float32x4&lt;/code&gt; as its 128-bit data type for four 32-bit floats, and uses &lt;code class="language-plaintext highlighter-rouge"&gt;uint32x4&lt;/code&gt; as its 128-bit data type for four 32-bit unsigned integers, and so on.
Each 32-bit component in a 128-bit vector register is often known as a &lt;em&gt;lane&lt;/em&gt;.
The process of populating each of the lanes in a 128-bit vector type is sometimes referred to as a &lt;em&gt;gather&lt;/em&gt; operation, and the process of pulling 32-bit values out of the 128-bit vector type is sometimes referred to as a &lt;em&gt;scatter&lt;/em&gt; operation; the &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; struct’s type punning makes gather and scatter operations nice and easy to do.&lt;/p&gt;

&lt;p&gt;One of the comparisons that the test program does on arm64 machines is between an implementation using native Neon intrinsics, and an implementation written using SSE intrinsics that are emulated with Neon intrinsics under the hood on arm64 via the sse2neon project.
Since for this test program, SSE intrinsics are available on both x86-64 (natively) and on arm64 (through sse2neon), we don’t need to wrap the &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; member of the union in any &lt;code class="language-plaintext highlighter-rouge"&gt;#ifdefs&lt;/code&gt;.
We do need to &lt;code class="language-plaintext highlighter-rouge"&gt;#ifdef&lt;/code&gt; out the Neon implementation on x86-64 though, hence the check for &lt;code class="language-plaintext highlighter-rouge"&gt;#if defined(__aarch64__)&lt;/code&gt;.
Putting everything above all together, we can get a nice, convenient 4-dimensional float vector in which we can access each component individually and access the entire contents of the vector as a single intrinsics-friendly 128-bit data type on both SSE and Neon:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;struct FVec4 {
    union {  // Use union for type punning __m128 and float32x4_t
        __m128 m128;
#if defined(__aarch64__)
        float32x4_t f32x4;
#endif
        struct {
            float x;
            float y;
            float z;
            float w;
        };
        float data[4];
    };

    FVec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {}
#if defined(__x86_64__)
    FVec4(__m128 f4) : m128(f4) {}
#elif defined(__aarch64__)
    FVec4(float32x4_t f4) : f32x4(f4) {}
#endif

    FVec4(float x_, float y_, float z_, float w_) : x(x_), y(y_), z(z_), w(w_) {}
    FVec4(float x_, float y_, float z_) : x(x_), y(y_), z(z_), w(0.0f) {}

    float operator[](int i) const { return data[i]; }
    float&amp;amp; operator[](int i) { return data[i]; }
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 1: &lt;code class="language-plaintext highligher-rouge"&gt;FVec4&lt;/code&gt; definition, which defines a 4-dimensional float vector that can be accessed as either a single 128-bit vector value or as individual 32-bit floats.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The actual implementation in the test project has a few more functions defined as part of &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; to provide basic arithmetic operators.
In the test project, I also define &lt;code class="language-plaintext highlighter-rouge"&gt;IVec4&lt;/code&gt;, which is a simple 4-dimensional integer vector type that is useful for storing multiple indices together.
Rays are represented as a simple struct containing just two &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;s and two floats; the two &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;s store the ray’s direction and origin, and the two floats store the ray’s tMin and tMax values.&lt;/p&gt;

&lt;p&gt;For representing bounding boxes, the test project has two different structs.
The first is &lt;code class="language-plaintext highlighter-rouge"&gt;BBox&lt;/code&gt;, which defines a single axis-aligned bounding box for purely scalar use.
Since &lt;code class="language-plaintext highlighter-rouge"&gt;BBox&lt;/code&gt; is only used for scalar code, it just contains normal floats and doesn’t have any vector data types at all inside:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;struct BBox {
    union {
        float corners[6];        // indexed as [minX minY minZ maxX maxY maxZ]
        float cornersAlt[2][3];  // indexed as corner[minOrMax][XYZ]
    };

    BBox(const FVec4&amp;amp; minCorner, const FVec4&amp;amp; maxCorner) {
        cornersAlt[0][0] = fmin(minCorner.x, maxCorner.x);
        cornersAlt[0][1] = fmin(minCorner.y, maxCorner.y);
        cornersAlt[0][2] = fmin(minCorner.z, maxCorner.z);
        cornersAlt[1][0] = fmax(minCorner.x, maxCorner.x);
        cornersAlt[1][1] = fmax(minCorner.y, maxCorner.y);
        cornersAlt[1][2] = fmax(minCorner.x, maxCorner.x);
    }

    FVec4 minCorner() const { return FVec4(corners[0], corners[1], corners[2]); }

    FVec4 maxCorner() const { return FVec4(corners[3], corners[4], corners[5]); }
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 2: Struct holding a single bounding-box.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The second bounding box struct is &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt;, which stores four axis-aligned bounding boxes together.
&lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; internally uses &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;s in a union with two different arrays of regular floats to allow for vectorized operation and individual access to each component of each corner of each box.
The internal layout of &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; is not as simple as just storing four &lt;code class="language-plaintext highlighter-rouge"&gt;BBox&lt;/code&gt; structs; I’ll discuss how the internal layout of &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; works a little bit later in this post.&lt;/p&gt;

&lt;div id="2021-09-07-scalar-implementations"&gt;&lt;/div&gt;
&lt;h2 id="williams-et-al-2005-ray-box-intersection-test-scalar-implementations"&gt;Williams et al. 2005 Ray-Box Intersection Test: Scalar Implementations&lt;/h2&gt;

&lt;p&gt;Now that we have all of the data structures that we’ll need, we can dive into the actual implementations.
The first implementation is the reference scalar version of ray-box intersection.
The implementation below is pretty close to being copy-pasted straight out of the Williams et al. 2005 paper, albeit with some minor changes to use our previously defined data structures:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;bool rayBBoxIntersectScalar(const Ray&amp;amp; ray, const BBox&amp;amp; bbox, float&amp;amp; tMin, float&amp;amp; tMax) {
    FVec4 rdir = 1.0f / ray.direction;
    int sign[3];
    sign[0] = (rdir.x &amp;lt; 0);
    sign[1] = (rdir.y &amp;lt; 0);
    sign[2] = (rdir.z &amp;lt; 0);

    float tyMin, tyMax, tzMin, tzMax;
    tMin = (bbox.cornersAlt[sign[0]][0] - ray.origin.x) * rdir.x;
    tMax = (bbox.cornersAlt[1 - sign[0]][0] - ray.origin.x) * rdir.x;
    tyMin = (bbox.cornersAlt[sign[1]][1] - ray.origin.y) * rdir.y;
    tyMax = (bbox.cornersAlt[1 - sign[1]][1] - ray.origin.y) * rdir.y;
    if ((tMin &amp;gt; tyMax) || (tyMin &amp;gt; tMax)) {
        return false;
    }
    if (tyMin &amp;gt; tMin) {
        tMin = tyMin;
    }
    if (tyMax &amp;lt; tMax) {
        tMax = tyMax;
    }
    tzMin = (bbox.cornersAlt[sign[2]][2] - ray.origin.z) * rdir.z;
    tzMax = (bbox.cornersAlt[1 - sign[2]][2] - ray.origin.z) * rdir.z;
    if ((tMin &amp;gt; tzMax) || (tzMin &amp;gt; tMax)) {
        return false;
    }
    if (tzMin &amp;gt; tMin) {
        tMin = tzMin;
    }
    if (tzMax &amp;lt; tMax) {
        tMax = tzMax;
    }
    return ((tMin &amp;lt; ray.tMax) &amp;amp;&amp;amp; (tMax &amp;gt; ray.tMin));
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 3: A direct implementation of &lt;a href="https://doi.org/10.1080/2151237X.2005.10129188"&gt;"An Efficient and Robust Ray-Box Intersection Algorithm" by Amy Williams et al. 2005.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;For our test, we want to intersect a ray against four boxes, so we just write a wrapper function that calls &lt;code class="language-plaintext highlighter-rouge"&gt;rayBBoxIntersectScalar()&lt;/code&gt; four times in sequence.
In the wrapper function, &lt;code class="language-plaintext highlighter-rouge"&gt;hits&lt;/code&gt; is a reference to a &lt;code class="language-plaintext highlighter-rouge"&gt;IVec4&lt;/code&gt; where each component of the &lt;code class="language-plaintext highlighter-rouge"&gt;IVec4&lt;/code&gt; is used to store either &lt;code class="language-plaintext highlighter-rouge"&gt;0&lt;/code&gt; to indicate no intersection, or &lt;code class="language-plaintext highlighter-rouge"&gt;1&lt;/code&gt; to indicate an intersection:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;void rayBBoxIntersect4Scalar(const Ray&amp;amp; ray,
                            const BBox&amp;amp; bbox0,
                            const BBox&amp;amp; bbox1,
                            const BBox&amp;amp; bbox2,
                            const BBox&amp;amp; bbox3,
                            IVec4&amp;amp; hits,
                            FVec4&amp;amp; tMins,
                            FVec4&amp;amp; tMaxs) {
    hits[0] = (int)rayBBoxIntersectScalar(ray, bbox0, tMins[0], tMaxs[0]);
    hits[1] = (int)rayBBoxIntersectScalar(ray, bbox1, tMins[1], tMaxs[1]);
    hits[2] = (int)rayBBoxIntersectScalar(ray, bbox2, tMins[2], tMaxs[2]);
    hits[3] = (int)rayBBoxIntersectScalar(ray, bbox3, tMins[3], tMaxs[3]);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 4: Wrap and call &lt;code class="language-plaintext highligher-rouge"&gt;rayBBoxIntersectScalar()&lt;/code&gt; four times sequentially to implement scalar 4-way ray-box intersection.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The implementation provided in the original paper is easy to understand, but unfortunately is not in a form that we can easily vectorize.
Note the six branching if statements; branching statements do not bode well for good vectorized code.
The reason branching doesn’t go well with SIMD code is because with SIMD code, the same instruction has to be executed in lockstep across all four SIMD lanes; the only way for different lanes to execute different branches is to run all branches across all lanes sequentially, and for each branch mask out the lanes that the branch shouldn’t apply to.
Contrast with normal scalar sequential execution where we process one ray-box intersection at a time; each ray-box test can independently choose what codepath to execute at each branch and completely bypass executing branches that never get taken.
Scalar code can also do fancy things like advanced branch prediction to further speed things up.&lt;/p&gt;

&lt;p&gt;In order to get to a point where we can more easily write vectorized SSE and Neon implementations of the ray-box test, we first need to refactor the original implementation into an intermediate scalar form that is more amenable to vectorization.
In other words, we need to rewrite the code in Listing 3 to be as branchless as possible.
We can see that each of the if statements in Listing 3 is comparing two values and, depending on which value is greater, assigning one value to be the same as the other value.
Fortunately, this type of compare-and-assign with floats can easily be replicated in a branchless fashion by just using a &lt;code class="language-plaintext highlighter-rouge"&gt;min&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;max&lt;/code&gt; operation.
For example, the branching statement &lt;code class="language-plaintext highlighter-rouge"&gt;if (tyMin &amp;gt; tMin) { tMin = tyMin; }&lt;/code&gt; can be easily replaced with the branchless statement &lt;code class="language-plaintext highlighter-rouge"&gt;tMin = fmax(tMin, tyMin);&lt;/code&gt;.
I chose to use &lt;code class="language-plaintext highlighter-rouge"&gt;fmax()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;fmin()&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;std::max()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;std::min()&lt;/code&gt; because I found &lt;code class="language-plaintext highlighter-rouge"&gt;fmax()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;fmin()&lt;/code&gt; to be slightly faster in this example.
The good thing about replacing our branches with &lt;code class="language-plaintext highlighter-rouge"&gt;min&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;max&lt;/code&gt; operations is that SSE and Neon both have intrinsics to do vectorized &lt;code class="language-plaintext highlighter-rouge"&gt;min&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;max&lt;/code&gt; operations in the form of &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_min_ps&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_max_ps&lt;/code&gt; for SSE and &lt;code class="language-plaintext highlighter-rouge"&gt;vminq_f32&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;vmaxq_f32&lt;/code&gt; for Neon.&lt;/p&gt;

&lt;p&gt;Also note how in Listing 3, the index of each corner is calculated while looking up the corner; for example: &lt;code class="language-plaintext highlighter-rouge"&gt;bbox.cornersAlt[1 - sign[0]]&lt;/code&gt;.
To make the code easier to vectorize, we don’t want to be computing indices in the lookup; instead, we want to precompute all of the indices that we will want to look up.
In Listing 5, the &lt;code class="language-plaintext highlighter-rouge"&gt;IVec4&lt;/code&gt; values named &lt;code class="language-plaintext highlighter-rouge"&gt;near&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;far&lt;/code&gt; are used to store precomputed lookup indices.
Finally, one more shortcut we can make with an eye towards easier vectorization is that we don’t actually care what the values of &lt;code class="language-plaintext highlighter-rouge"&gt;tMin&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;tMax&lt;/code&gt; are in the event that the ray misses the box; if the values that come out of a missed hit in our vectorized implementation don’t exactly match the values that come out of a missed hit in the scalar implementation, that’s okay!
We just need to check for the missed hit case and instead return whether or not a hit has occurred as a bool.&lt;/p&gt;

&lt;p&gt;Putting all of the above together, we can rewrite Listing 3 into the following much more compact, more more SIMD friendly scalar implementation:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;bool rayBBoxIntersectScalarCompact(const Ray&amp;amp; ray, const BBox&amp;amp; bbox, float&amp;amp; tMin, float&amp;amp; tMax) {
    FVec4 rdir = 1.0f / ray.direction;
    IVec4 near(int(rdir.x &amp;gt;= 0.0f ? 0 : 3), int(rdir.y &amp;gt;= 0.0f ? 1 : 4),
            int(rdir.z &amp;gt;= 0.0f ? 2 : 5));
    IVec4 far(int(rdir.x &amp;gt;= 0.0f ? 3 : 0), int(rdir.y &amp;gt;= 0.0f ? 4 : 1),
            int(rdir.z &amp;gt;= 0.0f ? 5 : 2));

    tMin = fmax(fmax(ray.tMin, (bbox.corners[near.x] - ray.origin.x) * rdir.x),
                fmax((bbox.corners[near.y] - ray.origin.y) * rdir.y,
                    (bbox.corners[near.z] - ray.origin.z) * rdir.z));
    tMax = fmin(fmin(ray.tMax, (bbox.corners[far.x] - ray.origin.x) * rdir.x),
                fmin((bbox.corners[far.y] - ray.origin.y) * rdir.y,
                    (bbox.corners[far.z] - ray.origin.z) * rdir.z));
                    
    return tMin &amp;lt;= tMax;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 5: A much more compact implementation of Williams et al. 2005; this implementation does not calculate a negative tMin if the ray origin is inside of the box.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The wrapper around &lt;code class="language-plaintext highlighter-rouge"&gt;rayBBoxIntersectScalarCompact()&lt;/code&gt; to make a function that intersects one ray against four boxes is exactly the same as in Listing 4, just with a call to the new function, so I won’t bother going into it.&lt;/p&gt;

&lt;p&gt;Here is how the scalar compact implementation (Listing 5) compares to the original scalar implementation (Listing 3).
The “speedup” columns use the scalar compact implementation as the baseline:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2 Speedup:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Compact:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.5159 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;41.8187 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;81.0942 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Original:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.1004 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0117x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;78.4001 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.5334x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;90.7649 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8935x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar No Early-Out:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;55.6770 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8014x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;85.3562 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.4899x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;102.763 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.7891x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The original scalar implementation is actually ever-so-slightly faster than our scalar compact implementation on x86-64!
This result actually doesn’t surprise me; note that the original scalar implementation has early-outs when checking the values of &lt;code class="language-plaintext highlighter-rouge"&gt;tyMin&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;tzMin&lt;/code&gt;, whereas the early-outs have to be removed in order to restructure the original scalar implementation into the vectorization-friendly compact scalar implementation.
To confirm that the original scalar implementation is faster because of the early-outs, in the test program I also include a version of the original scalar implementation that has the early-outs removed.
Instead of returning when the checks on &lt;code class="language-plaintext highlighter-rouge"&gt;tyMin&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;tzMin&lt;/code&gt; fail, I modified the original scalar implementation to store the result of the checks in a bool that is stored until the end of the function and then checked at the end of the function.
In the results, this modified version of the original scalar implementation is labeled as “Scalar No Early-Out”; this modified version is considerably slower than the compact scalar implementation on both x86-64 and arm64.&lt;/p&gt;

&lt;p&gt;The more surprising result is that the original scalar implementation is &lt;em&gt;slower&lt;/em&gt; than the compact scalar implementation on arm64, and by a considerable amount!
Even more interesting is that the original scalar implementation and the modified “no early-out” version perform relatively similarly on arm64; this result strongly hints to me that for whatever reason, the version of Clang I used just wasn’t able to optimize for arm64 as well as it was able to for x86-64.
Looking at the &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGe1wAyeAyYAHI%2BAEaYxBKS0gAOqAqETgwe3r56icmOAkEh4SxRMVLSdpgOqUIETMQE6T5%2BXLaY9rkM1bUE%2BWGR0bFlNXUNmc0KQ93BvUX9pQCUtqhexMjsHOYAzHhUANRYNCHoEAD6x6oAHABsx5eSp3MmGgCCm8HI3lg7JhtuqiwswQIxGCADoEN9sI8XmYNq1tntMAdMEdTkxasgELd7lDXgx3l5Pt83LUWMcQgIwRCcTDXNtqVs8R9MF8fiwmAQEJSNtgdl9nlDxsQvA4dgAxABqFUkXwA7FZnrzeV4GKlZRZeQB6DU7RTM5Wq/jEHYEACe8WZ8WVKqMO1OLC4ZnOO0M6B2VFoqHZGzMqjuBHp8P2UxRZyuNzuxweCsVMbtDqd9sd33l0NhtEDiODJ2OaJWmIjUaeMZj7s9BG9vuOBDdFckyfptKoUOLisFwurJjlzZbxdL7J2qnr0Z7ir71ZNQ6LI9HHv7AC9J9OZ2WdgB3Rc9zsAEQ3LbHe3ZTBMAFYLHXjzuNimY9vJ93eRKpRA5jsQAOIBoQRoqAsdiaP1%2BP6kDsc4Ad%2Bv6rmBP5qre/IwhmSIhhc1xYpG95ipKyCSNmiZOlQkgvm%2BuHUARMEyjucFpghWaoui%2BbYsOGFPmOtZVm6pFvlQtYkS%2BnZWORDYMPgTb8oxj5YdQs7VqoxzAfuJqyW6UkgYp%2B6rpGr7vjJv7/gpv6gXOkbAZB6m8V2AliZh2H7jJcnKQpdkroZhFaUZf4QHpwEGW5kGfuBZEUS8jH7qg5rEOyJAnlYF4QICOx4C%2BaAMOMao7MQmAEMsDAHjUUV4Cel7qrBU68mO5iXDsoXRBFxBRQVsWCPFZnqulmXENl6CHnlBXJrKgXoeJ0pVeFRC1ZYEBJSlg3lTsETNWlGVZUxEmqF8lizSCqjASaa3qhEIIml5u0bXOxnHftq4PFefUDVZlVhTVAC0E0CFNVkzXNqWtUtg0QKtj0bVtf47AD%2B2HSBIMnWdoMgpdvXFTGg33dVo0AFQvcl1ZlWYFWfXxC1tdlv2rajs3bTspMREdlNnZTV1FRZJXLUND2jRqGMpdjuPzd97XM39OzalTwNC0dotroLs30zd/ICVeOLPME6YhMzyMjSQ7OTVjUkzUwwFa8zH3NehvNE1ZEBMJL%2B1A5bQsHcBttQ86Vuw/TOKMwKQLtjsACSd18eh%2BoCDBjExm2IoB6HPZxYO8tRy2cUTnHTMjnFC7J0uvJxeuGebnL17R41nW5ae56Feht7J%2BhftPi5qgfjpDdeU3a4NwF1fmzHimJ93jWGcB2caW%2B9fadtHlud5EEQKZ7eMTXEldwPjUOfFfdD65jeeSBEDOcZbcBx7jFxcNNV1TFcUJTsBv46bOVHqe%2BUXvDjMxoCM0n6NZ9bg11aXzfi182LvfCwj9CoyxePnBWTxw7VgsBYVAq1I5MyDtlJBI59xoHatEBQUVLg9Wuj2LUq8sCqGRM6BQXxTwAgYAADR2NQgAmvQ4IAAtehTBVB0LZKoJh3CWE9XjkpFcmCQjEAUE8Wg/pTxmB6qeDY%2BD1SC21MEEhZCmAUJEdEKK1CADyxAACyHDZEWBoQw/hT9GKVxTOhOBCCObVmmjjZhaQSCiP1q9Bx70nHcI8Fg4gxtBGaLERIqRFgNDGPCReFkW43TUIgNQ3xojNrAR8a46Im03aBLScEyRUVIk7lPFwAq0TYnBHicERJ6TwapL8QdTJKdFRBPEbk08%2BSooyKid8GJVA4kJOySCU67DVCVOIAM%2BpI4mkhKikUp%2BrTildNiRw8pLjalAxqUk1Q4yeyTJaRYGZBS9nzI2N07hyyRn2yGeck0WyWw7NCfs9pRyTlLL6aslJHDzmbN3OA26UpnEjOfFfDxX0AFmyfE0vJBV3F%2BJwYUqFQKYWPIvNLYqvysKXOyYC6%2BcoCY/XNhCuR8KCVniJdk2FFhjwFRRR7SBoloFexFAAJSYDtNBiokb4HSpUAQ3ykYkDwMAYI3z9wEH0UK3OpVlKiqMVXRizL/wG0cRVTlFR2iKUVV4iq/LBUMFUlKsVurHL9mldpdCMY3wqu5QwCAlq1W/m1WUh1urfyirKa651wETUQBNRpA%2B/VaVBSeBEVAng0ostsaoH2ghsGqpEGIWo9idjypmuFcGBsI0fWDUDLmxoDVGv9E4k1ASmZI2IJykpXBAKS1TSCW1qRvlxWSMABgUV5FPwlTsJtLa5mdOOTsCAZa8CjMQT8HYGgbmdoFd2w5vaYkDs5QdFkbgx0Tq7UimJCz51DoGUuldd5gpSpNHm40R6OGernMeggF6ZUF15O6kpEAIhZpBHcqKa6e0FIOfkyGNanUZIpmlBdsdb25o4Q%2Bp9CCX1kqmYUyG76wmyK/cUgGv7gQ6v/aTQdw7vmmgNeB59r7TzwYebMmdMSUMspBH%2B65AGsMHRw6ekdc6IOqCgzCmDey4NTumYhnjUSKMmio2h0ENHMMLqTiB%2BEUB70QhPYYzZso3CdmXd6o9wQWQ8iLcWkct8qBiCUN8hGiopO4fU7J912mewyb7aZltHajNZ12Kp%2BTu6i0hwaXelzCzcM3ori/RUV68ObpY2x0RzTQnwY6Z%2B9dP7KN/rnC%2BMT2704gcC2B4LBHoO7K4Fx5t67jFRdi4J%2BLiXAPJYbU571QXuTGmvQp5Tyn%2B2BbMzVrT7mdOgrdPpzAhn/OOaaxelrmmDWWZbNZmJzW7MgYc/FJzaWR3Lrc2ysbXmbN1d64FGMt9pPVeXTWpbOMZpVfSzV/bI36moueAAN1QHgV0qaI1RoIDGhwkg40GGIIm5NTjU2kDNZnAHQLMY7AzU4ljGg/uCMBxM4FoPcZZuaP96H0503wMHGDrNZhIceeR9s2HaPM0II2Nj3HgP551icQgQgCgSek8zkq3NwQadI7p4jTVoHVAKFGzsKnBByXfs3YCOYD20dPZe8IUQH2B0suAuDz1Br%2BfwpNYr5F3zefkv2Q%2BoXIuEFi7EbGyXCbfuzQR/LpnfGtzy44RrqlavqcxcF4IYX4bRfRv1w4d7RuZcm4QVjxnyV11W85%2Buid6vW1PP7drl3uu3dKA94bz7xuWPE/9%2BSttluOdp9t1XRmHAFi0E4MeXgfgOBaFIKgTgSnLDWE7UsFYzJNg8FIAQTQeeFgAGsQDHghwXjgkhi%2Bt/L5wXgCgQAQ5b6XvPpA4CwCQGgFg8Q6DRHIJQefi/6AxHeIYYAXAuAaGaDQSR2DKAREHxEYItQTScCb/PtgggdEMFoFfyfpAsBsiMOIF/daruYFHy/zApCyAXgz21%2BvAgIrQg%2B6YEQ4UxAJoHgWAg%2BQIeALAoBCw7oTAwACg4oeAmAq4Oi5oJeTe/Agg727AUgMggguoagg%2BugzQBgRgKA1g1g%2BgeAEQo%2BkACwoU7Qf%2Bj0OiGwI%2BrQqqqQLgQkIwTQpAgQUwhQxQWQSQKQAgYhshOQqQPQ0hswLQbQVQEwihYwghVqnQdQqhfQJQtg2hngjQeg4wXQRhMwJQCwCgdeqwegQImAawPA%2BeheA%2BL%2BFeHAyEj0twV89BwAOwu%2BX4IIOWEAVeVglgwEuAhAJAa0GwzQOwHgC%2BS%2BRojecwvAE%2BWgcwHeXePenA/epAJeZePhI%2BY%2BzereeR%2BgnAZgXhZRw%2BVRk%2BNRP%2BYiwhkgQAA"&gt;compiled x86-64 assembly&lt;/a&gt; and the &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGe1wAyeAyYAHI%2BAEaYxBKS0gAOqAqETgwe3r56icmOAkEh4SxRMVLSdpgOqUIETMQE6T5%2BXLaY9rkM1bUE%2BWGR0bFlNXUNmc0KQ93BvUX9pQCUtqhexMjsHOYAzHhUANRYNCHoEAD6x6oAHABsx5eSp3MmGgCCm8HI3lg7JhtuqiwswQIxGCADoEN9sI8XmYNq1tntMAdMEdTkxasgELd7lDXgx3l5Pt83LUWMcQgIwRCcTDXNtqVs8R9MF8fiwmAQEJSNtgdl9nlDxsQvA4dgAxABqFUkXwA7FZnrzeV4GKlZRZeQB6DU7RTM5Wq/jEHYEACe8WZ8WVKqMO1OLC4ZnOO0M6B2VFoqHZGzMqjuBHp8P2UxRZyuNzuxweCsVMbtDqd9sd33l0NhtEDiODJ2OaJWmIjUaeMZj7s9BG9vuOBDdFckyfptKoUOLisFwurJjlzZbxdL7J2qnr0Z7ir71ZNQ6LI9HHv7AC9J9OZ2WdgB3Rc9zsAEQ3LbHe3ZTBMAFYLHXjzuNimY9vJ93eRKpRA5jsQAOIBoQRoqAsdiaP1%2BP6kDsc4Ad%2Bv6rmBP5qre/IwhmSIhhc1xYpG95ipKyCSNmiZOlQkgvm%2BuHUARMEyjucFpghWaoui%2BbYsOGFPmOtZVm6pFvlQtYkS%2BnZWORDYMPgTb8oxj5YdQs7VqoxzAfuJqyW6UkgYp%2B6rpGr7vjJv7/gpv6gXOkbAZB6m8V2AliZh2H7jJcnKQpdkroZhFaUZf4QHpwEGW5kGfuBZEUS8jH7qg5rEOyJAnlYF4QICOx4C%2BaAMOMao7MQmAEMsDAHjUUV4Cel7qrBU68mO5iXDsoXRBFxBRQVsWCPFZnqulmXENl6CHnlBXJrKgXoeJ0pVeFRC1ZYEBJSlg3lTsETNWlGVZUxEmqF8lizSCqjASaa3qhEIIml5u0bXOxnHftq4PFefUDVZlVhTVAC0E0CFNVkzXNqWtUtg0QKtj0bVtf47AD%2B2HSBIMnWdoMgpdvXFTGg33dVo0AFQvcl1ZlWYFWfXxC1tdlv2rajs3bTspMREdlNnZTV1FRZJXLUND2jRqGMpdjuPzd97XM39OzalTwNC0dotroLs30zd/ICVeOLPME6YhMzyMjSQ7OTVjUkzUwwFa8zH3NehvNE1ZEBMJL%2B1A5bQsHcBttQ86Vuw/TOKMwKQLtjsACSd18eh%2BoCDBjExm2IoB6HPZxYO8tRy2cUTnHTMjnFC7J0uvJxeuGebnL17R41nW5ae56Feht7J%2BhftPi5qgfjpDdeU3a4NwF1fmzHimJ93jWGcB2caW%2B9fadtHlud5EEQKZ7eMTXEldwPjUOfFfdD65jeeSBEDOcZbcBx7jFxcNNV1TFcUJTsBv46bOVHqe%2BUXvDjMxoCM0n6NZ9bg11aXzfi182LvfCwj9CoyxePnBWTxw7VgsBYVAq1I5MyDtlJBI59xoHatEBQUVLg9Wuj2LUq8sCqGRM6BQXxTwAgYAADR2NQgAmvQ4IAAtehTBVB0LZKoJh3CWE9XjkpFcmCQjEAUE8Wg/pTxmB6qeDY%2BD1SC21MEEhZCmAUJEdEKK1CADyxAACyHDZEWBoQw/hT9GKVxTOhOBCCObVmmjjZhaQSCiP1q9Bx70nHcI8Fg4gxtBGaLERIqRFgNDGPCReFkW43TUIgNQ3xojNrAR8a46Im03aBLScEyRUVIk7lPFwAq0TYnBHicERJ6TwapL8QdTJKdFRBPEbk08%2BSooyKid8GJVA4kJOySCU67DVCVOIAM%2BpI4mkhKikUp%2BrTildNiRw8pLjalAxqUk1Q4yeyTJaRYGZBS9nzI2N07hyyRn2yGeck0WyWw7NCfs9pRyTlLL6aslJHDzmbN3OA26UpnEjOfFfDxX0AFmyfE0vJBV3F%2BJwYUqFQKYWPIvNLYqvysKXOyYC6%2BcoCY/XNhCuR8KCVniJdk2FFhjwFRRR7SBoloFexFAAJSYDtNBiokb4HSpUAQ3ykYkDwMAYI3z9wEH0UK3OpVlKiqMVXRizL/wG0cRVTlFR2iKUVV4iq/LBUMFUlKsVurHL9mldpdCMY3wqu5QwCAlq1W/m1WUh1urfyirKa651wETUQBNRpA%2B/VaVBSeBEVAng0ostsaoH2ghsGqpEGIWo9idjypmuFcGBsI0fWDUDLmxoDVGv9E4k1ASmZI2IJykpXBAKS1TSCW1qRvlxWSMABgUV5FPwlTsJtLa5mdOOTsCAZa8CjMQT8HYGgbmdoFd2w5vaYkDs5QdFkbgx0Tq7UimJCz51DoGUuldd5gpSpNHm40R6OGernMeggF6ZUF15O6kpEAIhZpBHcqKa6e0FIOfkyGNanUZIpmlBdsdb25o4Q%2Bp9CCX1kqmYUyG76wmyK/cUgGv7gQ6v/aTQdw7vmmgNeB59r7TzwYebMmdMSUMspBH%2B65AGsMHRw6ekdc6IOqCgzCmDey4NTumYhnjUSKMmio2h0ENHMMLqTiB%2BEUB70QhPYYzZso3CdmXd6o9wQWQ8iLcWkct8qBiCUN8hGiopO4fU7J912mewyb7aZltHajNZ12Kp%2BTu6i0hwaXelzCzcM3ori/RUV68ObpY2x0RzTQnwY6Z%2B9dP7KN/rnC%2BMT2704gcC2B4LBHoO7K4Fx5t67jFRdi4J%2BLiXAPJYbU571QXuTGmvQp5Tyn%2B2BbMzVrT7mdOgrdPpzAhn/OOaaxelrmmDWWZbNZmJzW7MgYc/FJzaWR3Lrc2ysbXmbN1d64FGMt9pPVeXTWpbOMZpVfSzV/bI36moueAAN1QHgV0qaI1RoIDGhwkg40GGIIm5NTjU2kDNZnAHQLMY7AzU4ljGg/uCMBxM4FoPcZZuaP96H0503wMHGDrNZhIceeR9s2HaPM0II2Nj3HgP551icQgQgCgSek8zkq3NwQadI7p4jTVoHVAKFGzsKnBByXfs3YCOYD20dPZe8IUQH2B0suAuDz1Br%2BfwpNYr5F3zefkv2Q%2BoXIuEFi7EbGyXCbfuzQR/LpnfGtzy44RrqlavqcxcF4IYX4bRfRv1w4d7RuZcm4QVjxnyV11W85%2Buid6vW1PP7drl3uu3dKA94bz7xuWPE/9%2BSttluOdp9t1XRmHAFi0E4MeXgfgOBaFIKgTgSnLDWE7UsFYzJNg8FIAQTQeeFgAGsQDHghwXjgkhi%2Bt/L5wXgCgQAQ5b6XvPpA4CwCQGgFg8Q6DRHIJQefi/6AxBJFd84j13iGGAFwLgGhmg0EkdgygERB8RGCLUE0nAm/z7YIIHRDBaB38n6QLAbIjDiA/3Wq7mAo%2BH%2BmApCyAXgz29%2BvAgIrQg%2B6YEQ4UxAJoHgWAg%2BQIeALAkBCw7oTAwACg4oeAmAq4Oi5oJeTe/Agg727AUgMggguoagg%2BugzQBgRgKA1g1g%2BgeAEQo%2BkACwoU7QQBj0OiGwI%2BrQqqqQLgQkIwTQpAgQUwhQxQWQSQKQAgUhihOQqQPQ8hswLQbQVQEwqhYwohVqnQdQmhfQJQtg%2BhngjQeg4wXQZhMwJQCwCgdeqwegQImAawPA%2BeheA%2BH%2BFeHAW%2BO%2Blse%2BNoh%2BX4IIOWEAVeVglgwEuAhAJAa0GwzQOwHgC%2BS%2BRojecwvAE%2BWgcwHeXePenA/epAJeZeARI%2BY%2BzereBR%2BgnAZgfhFRw%2BNRk%2BdRABYi4hkgQAA"&gt;compiled arm64 assembly&lt;/a&gt; on Godbolt Compiler Explorer for the original scalar implementation shows that the structure of the output assembly is very similar across both architectures though, so the cause of the slower performance on arm64 is not completely clear to me.&lt;/p&gt;

&lt;p&gt;For all of the results in the rest of the post, the compact scalar implementation’s timings are used as the baseline that everything else is compared against, since all of the following implementations are derived from the compact scalar implementation.&lt;/p&gt;

&lt;div id="2021-09-07-sse-implementation"&gt;&lt;/div&gt;
&lt;h2 id="sse-implementation"&gt;SSE Implementation&lt;/h2&gt;

&lt;p&gt;The first vectorized implementation we’ll look at is using SSE on x86-64 processors.
The full SSE through SSE4 instruction set today including contains 281 instructions, introduced over the past two decades-ish in a series of supplementary extensions to the original SSE instruction set.
All modern Intel and AMD x86-64 processors from at least the past decade support SSE4, and all x86-64 processors ever made support at least SSE2 since SSE2 is written into the base x86-64 specification.
As mentioned earlier, SSE uses 128-bit registers that can be split into two, four, eight, or even sixteen lanes; the most common (and original) use case is four 32-bit floats.
AVX and AVX2 later expanded the register width from 128-bit to 256-bit, and the latest AVX-512 extensions introduced 512-bit registers.
For this post though, we’ll just stick with 128-bit SSE.&lt;/p&gt;

&lt;p&gt;In order to program directly using SSE instructions, we can either write SSE assembly directly, or we can use SSE intrinsics.
Writing SSE assembly directly is not particularly ideal for all of the same reasons that writing programs in regular assembly is not particularly ideal for most cases, so we’ll want to use intrinsics instead.
Intrinsics are functions whose implementations are specially handled by the compiler; in the case of vector intrinsics, each function maps directly to a known single or small number of vector assembly instructions.
Intrinsics kind of bridge between writing directly in assembly and using full-blown standard library functions; intrinsics are &lt;em&gt;higher&lt;/em&gt; level than assembly, but &lt;em&gt;lower&lt;/em&gt; level than what you typically find in standard library functions.
The headers for vector intrinsics are defined by the compiler; almost every C++ compiler that supports SSE and AVX intrinsics follows a convention where SSE/AVX intrinsics headers are named using the pattern *mmintrin.h, where * is a letter of the alphabet corresponding to a specific subset or version of either SSE of AVX (for example, x for SSE, e for SSE2, n for SSE4.2, i for AVX, etc.). 
For example, &lt;code class="language-plaintext highlighter-rouge"&gt;xmmintrin.h&lt;/code&gt; is where the &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; type we used earlier in defining all of our structs comes from.
Intel’s searchable &lt;a href="https://software.intel.com/sites/landingpage/IntrinsicsGuide/"&gt;online Intrinsics Guide&lt;/a&gt; is an invaluable resource for looking up what SSE intrinsics there are and what each of them does.&lt;/p&gt;

&lt;p&gt;The first thing we need to do for our SSE implementation is to define a new &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; struct that holds four bounding boxes together.
How we store these four bounding boxes together is extremely important.
The easiest way to store four bounding boxes in a single struct is to just have &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; store four separate &lt;code class="language-plaintext highlighter-rouge"&gt;BBox&lt;/code&gt; structs internally, but this approach is actually really bad for vectorization.
To understand why, consider something like the following, where we perform an &lt;code class="language-plaintext highlighter-rouge"&gt;min&lt;/code&gt; operation between the ray tMin and a distance to a corner of a bounding box:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;fmax(ray.tMin, (bbox.corners[near.x] - ray.origin.x) * rdir.x);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now consider if we want to do this operation for four bounding boxes in serial:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;fmax(ray.tMin, (bbox0.corners[near.x] - ray.origin.x) * rdir.x);
fmax(ray.tMin, (bbox1.corners[near.x] - ray.origin.x) * rdir.x);
fmax(ray.tMin, (bbox2.corners[near.x] - ray.origin.x) * rdir.x);
fmax(ray.tMin, (bbox3.corners[near.x] - ray.origin.x) * rdir.x);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above serial sequence is a perfect example of what we want to fold into a single vectorized line of code.
The inputs to a vectorized version of the above should be a 128-bit four-lane value with &lt;code class="language-plaintext highlighter-rouge"&gt;ray.tMin&lt;/code&gt; in all four lanes, another 128-bit four-lane value with &lt;code class="language-plaintext highlighter-rouge"&gt;ray.origin.x&lt;/code&gt; in all four lanes, another 128-bit four-lane value with &lt;code class="language-plaintext highlighter-rouge"&gt;rdir.x&lt;/code&gt; in all four lanes, and finally a 128-bit four-lane value where the first lane is a single index of a single corner from the first bounding box, the second lane is a single index of a single corner from the second bounding box, and so on and so forth.
Instead of an array of structs, we need the bounding box values to be provided as a struct of corner value arrays where each 128-bit value stores one 32-bit value from each corner of each of the four boxes.
Alternatively, the &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; memory layout that we want can be thought of as an array of 24 floats, which is indexed as a 3D array where the first dimension is indexed by min or max corner, the second dimension is indexed by x, y, and z within each corner, and the third dimension is indexed by which bounding box the value belongs to.
Putting the above together with some accessors and setter functions yields the following definition for &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt;:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;struct BBox4 {
    union {
        FVec4 corners[6];             // order: minX, minY, minZ, maxX, maxY, maxZ
        float cornersFloat[2][3][4];  // indexed as corner[minOrMax][XYZ][bboxNumber]
        float cornersFloatAlt[6][4];
    };

    inline __m128* minCornerSSE() { return &amp;amp;corners[0].m128; }
    inline __m128* maxCornerSSE() { return &amp;amp;corners[3].m128; }

#if defined(__aarch64__)
    inline float32x4_t* minCornerNeon() { return &amp;amp;corners[0].f32x4; }
    inline float32x4_t* maxCornerNeon() { return &amp;amp;corners[3].f32x4; }
#endif

    inline void setBBox(int boxNum, const FVec4&amp;amp; minCorner, const FVec4&amp;amp; maxCorner) {
        cornersFloat[0][0][boxNum] = fmin(minCorner.x, maxCorner.x);
        cornersFloat[0][1][boxNum] = fmin(minCorner.y, maxCorner.y);
        cornersFloat[0][2][boxNum] = fmin(minCorner.z, maxCorner.z);
        cornersFloat[1][0][boxNum] = fmax(minCorner.x, maxCorner.x);
        cornersFloat[1][1][boxNum] = fmax(minCorner.y, maxCorner.y);
        cornersFloat[1][2][boxNum] = fmax(minCorner.x, maxCorner.x);
    }

    BBox4(const BBox&amp;amp; a, const BBox&amp;amp; b, const BBox&amp;amp; c, const BBox&amp;amp; d) {
        setBBox(0, a.minCorner(), a.maxCorner());
        setBBox(1, b.minCorner(), b.maxCorner());
        setBBox(2, c.minCorner(), c.maxCorner());
        setBBox(3, d.minCorner(), d.maxCorner());
    }
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 6: Struct holding four bounding boxes together with values interleaved for optimal vectorized access.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Note how the &lt;code class="language-plaintext highlighter-rouge"&gt;setBBox&lt;/code&gt; function (which the constructor calls) has a memory access pattern where a single value is written into every 128-bit &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;.
Generally scattered access like this is extremely expensive in vectorized code, and should be avoided as much as possible; setting an entire 128-bit value at once is much faster than setting four separate 32-bit segments across four different values.
However, something like the above is often inevitably necessary just to get data loaded into a layout optimal for vectorized code; in the test program, &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; structs are initialized and set up once, and then reused across all tests.
The time required to set up &lt;code class="language-plaintext highlighter-rouge"&gt;BBox&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; is not counted as part of any of the test runs; in a full BVH traversal implementation, the BVH’s bounds at each node should be pre-arranged into a vector-friendly layout before any ray traversal takes place.
In general, figuring out how to restructure an algorithm to be easily expressed using vector intrinsics is really only half of the challenge in writing good vectorized programs; the other half of the challenge is just getting the input data into a form that is amenable to vectorization.
Actually, depending on the problem domain, the data marshaling can account for far more than half of the total effort spent!&lt;/p&gt;

&lt;p&gt;Now that we have four bounding boxes structured in a way that is amenable to vectorized usage, we also need to structure our ray inputs for vectorized usage.
This step is relatively easy; we just need to expand each component of each element of the ray into a 128-bit value where the same value is replicated across every 32-bit lane.
SSE has a specific intrinsic to do exactly this: &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_set1_ps()&lt;/code&gt; takes in a single 32-bit float and replicates it to all four lanes in a 128-bit &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt;.
SSE also has a bunch of more specialized instructions, which can be used in specific scenarios to do complex operations in a single instruction.
Knowing when to use these more specialized instructions can be tricky and requires extensive knowledge of the SSE instruction set; I don’t know these very well yet!
One good trick I did figure out was that in the case of taking a &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; and creating a new &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; from each of the &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;’s components, I could use &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_shuffle_ps&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_set1_ps()&lt;/code&gt;.
The problem with using &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_set1_ps()&lt;/code&gt; in this case is that with a &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt;, which internally uses &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; on x86-64, taking a element out to store using &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_set1_ps()&lt;/code&gt; compiles down to a &lt;code class="language-plaintext highlighter-rouge"&gt;MOVSS&lt;/code&gt; instruction in addition to a shuffle.
&lt;code class="language-plaintext highlighter-rouge"&gt;_mm_shuffle_ps()&lt;/code&gt;, on the other hand, compiles down to a single &lt;code class="language-plaintext highlighter-rouge"&gt;SHUFPS&lt;/code&gt; instruction.
&lt;code class="language-plaintext highlighter-rouge"&gt;_mm_shuffle_ps()&lt;/code&gt; takes in two &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt;s as input and takes two components from the first &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; for the first two components of the output, and takes two components from the second &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; for the second two components of the output.
Which components from the inputs are taken is assignable using an input mask, which can conveniently be generated using the &lt;code class="language-plaintext highlighter-rouge"&gt;_MM_SHUFFLE()&lt;/code&gt; macro that comes with the SSE intrinsics headers.
Since our ray struct’s origin and direction elements are already backed by &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; under the hood, we can just use &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_shuffle_ps()&lt;/code&gt; with the same element from the ray as both the first and second inputs to generate a &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; containing only a single component of each element.
For example, to create a &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; containing only the x component of the ray direction, we can write: &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_shuffle_ps(rdir.m128, rdir.m128, _MM_SHUFFLE(0, 0, 0, 0))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Translating the &lt;code class="language-plaintext highlighter-rouge"&gt;fmin()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;fmax()&lt;/code&gt; functions is very straightforward with SSE; we can use SSE’s &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_min_ps()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_max_ps()&lt;/code&gt; as direct analogues.
Putting all of the above together allows us to write a fully SSE-ized version of the compact scalar ray-box intersection test that intersects a single ray against four boxes simultaneously:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;void rayBBoxIntersect4SSE(const Ray&amp;amp; ray,
                        const BBox4&amp;amp; bbox4,
                        IVec4&amp;amp; hits,
                        FVec4&amp;amp; tMins,
                        FVec4&amp;amp; tMaxs) {
    FVec4 rdir(_mm_set1_ps(1.0f) / ray.direction.m128);
    /* use _mm_shuffle_ps, which translates to a single instruction while _mm_set1_ps involves a
    MOVSS + a shuffle */
    FVec4 rdirX(_mm_shuffle_ps(rdir.m128, rdir.m128, _MM_SHUFFLE(0, 0, 0, 0)));
    FVec4 rdirY(_mm_shuffle_ps(rdir.m128, rdir.m128, _MM_SHUFFLE(1, 1, 1, 1)));
    FVec4 rdirZ(_mm_shuffle_ps(rdir.m128, rdir.m128, _MM_SHUFFLE(2, 2, 2, 2)));
    FVec4 originX(_mm_shuffle_ps(ray.origin.m128, ray.origin.m128, _MM_SHUFFLE(0, 0, 0, 0)));
    FVec4 originY(_mm_shuffle_ps(ray.origin.m128, ray.origin.m128, _MM_SHUFFLE(1, 1, 1, 1)));
    FVec4 originZ(_mm_shuffle_ps(ray.origin.m128, ray.origin.m128, _MM_SHUFFLE(2, 2, 2, 2)));

    IVec4 near(int(rdir.x &amp;gt;= 0.0f ? 0 : 3), int(rdir.y &amp;gt;= 0.0f ? 1 : 4),
            int(rdir.z &amp;gt;= 0.0f ? 2 : 5));
    IVec4 far(int(rdir.x &amp;gt;= 0.0f ? 3 : 0), int(rdir.y &amp;gt;= 0.0f ? 4 : 1),
            int(rdir.z &amp;gt;= 0.0f ? 5 : 2));

    tMins = FVec4(_mm_max_ps(
        _mm_max_ps(_mm_set1_ps(ray.tMin), 
                   (bbox4.corners[near.x].m128 - originX.m128) * rdirX.m128),
        _mm_max_ps((bbox4.corners[near.y].m128 - originY.m128) * rdirY.m128,
                   (bbox4.corners[near.z].m128 - originZ.m128) * rdirZ.m128)));
    tMaxs = FVec4(_mm_min_ps(
        _mm_min_ps(_mm_set1_ps(ray.tMax),
                   (bbox4.corners[far.x].m128 - originX.m128) * rdirX.m128),
        _mm_min_ps((bbox4.corners[far.y].m128 - originY.m128) * rdirY.m128,
                   (bbox4.corners[far.z].m128 - originZ.m128) * rdirZ.m128)));

    int hit = ((1 &amp;lt;&amp;lt; 4) - 1) &amp;amp; _mm_movemask_ps(_mm_cmple_ps(tMins.m128, tMaxs.m128));
    hits[0] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (0)));
    hits[1] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (1)));
    hits[2] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (2)));
    hits[3] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (3)));
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 7: SSE version of the compact Williams et al. 2005 implementation.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The last part of &lt;code class="language-plaintext highlighter-rouge"&gt;rayBBoxIntersect4SSE()&lt;/code&gt; where &lt;code class="language-plaintext highlighter-rouge"&gt;hits&lt;/code&gt; is populated might require a bit of explaining.
This last part implements the check for whether or not a ray actually hit the box based on the results stored in &lt;code class="language-plaintext highlighter-rouge"&gt;tMin&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;tMax&lt;/code&gt;.
This implementation takes advantage of the fact that misses in this implementation produce &lt;code class="language-plaintext highlighter-rouge"&gt;inf&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;-inf&lt;/code&gt; values; to figure out if a hit has occurred, we just have to check that in each lane, the &lt;code class="language-plaintext highlighter-rouge"&gt;tMin&lt;/code&gt; value is less than the &lt;code class="language-plaintext highlighter-rouge"&gt;tMax&lt;/code&gt; value, and &lt;code class="language-plaintext highlighter-rouge"&gt;inf&lt;/code&gt; values play nicely with this check.
So, to conduct the check across all lanes at the same time, we use &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_cmple_ps()&lt;/code&gt;, which compares if the 32-bit float in each lane of the first input is less-than-or-equal than the corresponding 32-bit float in each lane of the second input.
If the comparison succeeds, &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_cmple_ps()&lt;/code&gt; writes &lt;code class="language-plaintext highlighter-rouge"&gt;0xFFF&lt;/code&gt; into the corresponding lane in the output &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt;, and if the comparison fails, &lt;code class="language-plaintext highlighter-rouge"&gt;0&lt;/code&gt; is written instead.
The remaining &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_movemask_ps()&lt;/code&gt; instruction and bit shifts are just used to copy the results in each lane out into each component of &lt;code class="language-plaintext highlighter-rouge"&gt;hits&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I think variants of this 4-wide SSE ray-box intersection function are fairly common in production renderers; I’ve seen something similar developed independently at multiple studios and in multiple renderers, which shouldn’t be surprising since the translation from the original Williams et al. 2005 paper to a SSE-ized version is relatively straightforward.
Also, the performance results further hint at why variants of this implementation are popular!
Here is how the SSE implementation (Listing 7) performs compared to the scalar compact representation (Listing 5):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2 Speedup:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Compact:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.5159 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;81.0942 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;SSE:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;10.9660 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.0686x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;13.6353 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.9474x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The SSE implementation is almost exactly four times faster than the reference scalar compact implementation, which is exactly what we would expect as a best case for a properly written SSE implementation.
Actually, in the results listed above, the SSE implementation is listed as being slightly &lt;em&gt;more&lt;/em&gt; than four times faster, but that’s just an artifact of averaging together results from multiple runs; the amount over 4x is basically just an artifact of the statistical margin of error.
A 4x speedup is the maximum speedup we can possible expect given that SSE is 4-wide for 32-bit floats.
In our SSE implementation, the &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; struct is already set up before the function is called, but the function still needs to translate each incoming ray into a form suitable for vector operations, which is additional work that the scalar implementation doesn’t need to do.
In order to make this additional setup work not drag down performance, the &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_shuffle_ps()&lt;/code&gt; trick becomes very important.&lt;/p&gt;

&lt;p&gt;Running the x86-64 version of the test program on arm64 using Rosetta 2 produces a more surprising result: close to a 6x speedup!
Running through Rosetta 2 means that the x86-64 and SSE instructions have to be translated to arm64 and Neon instructions, and the 8x speedup here hints that for this test, Rosetta 2’s SSE to Neon translation ran much more efficiently than Rosetta 2’s x86-64 to arm64 translation.
Otherwise, a greater-than-4x speedup should not be possible if both implementations are being translated with equal levels of efficiency.
I did not expect that to be the case!
Unfortunately, while we can speculate, only Apple’s developers can say for sure what Rosetta 2 is doing internally that produces this result.&lt;/p&gt;

&lt;div id="2021-09-07-neon-implementation"&gt;&lt;/div&gt;
&lt;h2 id="neon-implementation"&gt;Neon Implementation&lt;/h2&gt;

&lt;p&gt;The second vectorized implementation we’ll look at is using Neon on arm64 processors.
Much like how all modern x86-64 processors support at least SSE2 because the 64-bit extension to x86 incorporated SSE2 into the base instruction set, all modern arm64 processors support Neon because the 64-bit extension to ARM incorporates Neon in the base instruction set.
Compared with SSE, Neon is a much more compact instruction set, which makes sense since SSE belongs to a CISC ISA while Neon belongs to a RISC ISA.
Neon includes a little over a hundred instructions, which is less than half the number of instructions that the full SSE to SSE4 instruction set contains.
Neon has all of the basics that one would expect, such as arithmetic operations and various comparison operations, but Neon doesn’t have more complex high-level instructions like the fancy shuffle instructions we used in our SSE implementation.&lt;/p&gt;

&lt;p&gt;Much like how Intel has a searchable SSE intrinsics guide, ARM provides a helpful &lt;a href="https://developer.arm.com/architectures/instruction-sets/intrinsics/"&gt;searchable intrinsics guide&lt;/a&gt;.
Howard Oakley’s &lt;a href="https://eclecticlight.co/2021/07/27/code-in-arm-assembly-rounding-and-arithmetic/"&gt;recent blog series&lt;/a&gt; on writing arm64 assembly also includes a great &lt;a href="https://eclecticlight.co/2021/08/23/code-in-arm-assembly-lanes-and-loads-in-neon/"&gt;introduction to using Neon&lt;/a&gt;.
Note that even though there are fewer Neon instructions in total than there are SSE instructions, the ARM intrinsics guide lists several &lt;em&gt;thousand&lt;/em&gt; functions; this is because of one of the chief differences between SSE and Neon.
SSE’s &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; is just a generic 128-bit container that doesn’t actually specify what type or how many lanes it contains; what type a &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; value is or how many lanes a &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; value contains interpreted as is entirely up to each SSE instruction.
Contrast with Neon, which has explicit separate types for floats and integers, and also defines separate types based on width. 
Since Neon has many different 128-bit types, each Neon instruction has multiple corresponding intrinsics that differ simply by the input types and widths accepted in the function signature.
As a result of all of the above differences from SSE, writing a Neon implementation is not quite as simple as just doing a one-to-one replacement of each SSE intrinsic with a Neon intrinsic.&lt;/p&gt;

&lt;p&gt;…or is it?
Writing C/C++ code utilizing Neon instructions can be done by using the native Neon intrinsics found in &lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;arm_neon.h&amp;gt;&lt;/code&gt;, but another option exists through &lt;a href="https://github.com/DLTcollab/sse2neon"&gt;the sse2neon project&lt;/a&gt;.
When compiling for arm64, the x86-64 SSE &lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;xmmintrin.h&amp;gt;&lt;/code&gt; header is not available for use because every function in the &lt;code class="language-plaintext highlighter-rouge"&gt;&amp;lt;xmmintrin.h&amp;gt;&lt;/code&gt; header maps to a specific SSE instruction or group of SSE instructions, and there’s no sense in the compiler trying to generate SSE instructions for a processor architecture that SSE instructions don’t even work on.
However, the function definitions for each intrinsic are just function definitions, and sse2neon project reimplements every SSE intrinsic function with a Neon implementation under the hood.
So, using sse2neon, code originally written for x86-64 using SSE intrinsics can be compiled without modification on arm64, with Neon instructions generated from the SSE intrinsics.
A number of large projects originally written on x86-64 now have arm64 ports that utilize sse2neon to support vectorized code without having to completely rewrite using Neon intrinsics; as discussed in &lt;a href="https://blog.yiningkarlli.com/2021/07/porting-takua-to-arm-pt2.html"&gt;my previous Takua on ARM post&lt;/a&gt;, this approach is the exact approach that was taken to port &lt;a href="https://www.embree.org"&gt;Embree&lt;/a&gt;
to arm64.&lt;/p&gt;

&lt;p&gt;The sse2neon project was originally started by John W. Ratcliff and a few others at NVIDIA to port a handful of games from x86-64 to arm64; the original version of sse2neon only implemented the small subset of SSE that was needed for their project.
However, after the project was posted to Github with a MIT license, other projects found sse2neon useful and contributed additional extensions that eventually fleshed out full coverage for MMX and all versions of SSE from SSE1 all the way through SSE4.2.
For example, Syoyo Fujita’s &lt;a href="https://github.com/lighttransport/embree-aarch64"&gt;embree-aarch64 project&lt;/a&gt;, which was the basis of Intel’s official Embree arm64 port, resulted in a number of improvements to sse2neon’s precision and faithfulness to the original SSE behavior. 
Over the years sse2neon has seen contributions and improvements from NVIDIA, Amazon, Google, the Embree-aarch64 project, the Blender project, and recently Apple as part of Apple’s larger slew of contributions to various projects to improve arm64 support for Apple Silicon.
Similar open-source projects also exist to further generalize SIMD intrinsics headers (&lt;a href="https://github.com/simd-everywhere/simde"&gt;simde&lt;/a&gt;), to reimplement the AVX intrinsics headers using Neon (&lt;a href="https://github.com/DLTcollab/sse2neon"&gt;AvxToNeon&lt;/a&gt;), and Intel even has a project to do the reverse of sse2neon: reimplement Neon using SSE (&lt;a href="https://github.com/intel/ARM_NEON_2_x86_SSE"&gt;ARM_NEON_2_x86_SSE&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;While learning about Neon and while looking at how Embree was ported to arm64 using sse2neon, I started to wonder how efficient using sse2neon versus writing code directly using Neon intrinsics would be.
The SSE and Neon instruction sets don’t have a one-to-one mapping to each other for many of the more complex higher-level instructions that exist in SSE, and as a result, some SSE intrinsics that compiled down to a single SSE instruction on x86-64 have to be implemented on arm64 using many Neon instructions.
As a result, at least in principle, my expectation was that on arm64, code written directly using Neon intrinsics typically should likely have at least a small performance edge over SSE code ported using sse2neon.
So, I decided to do a direct comparison in my test program, which required implementing the 4-wide ray-box intersection test using Neon:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;inline uint32_t neonCompareAndMask(const float32x4_t&amp;amp; a, const float32x4_t&amp;amp; b) {
    uint32x4_t compResUint = vcleq_f32(a, b);
    static const int32x4_t shift = { 0, 1, 2, 3 };
    uint32x4_t tmp = vshrq_n_u32(compResUint, 31);
    return vaddvq_u32(vshlq_u32(tmp, shift));
}

void rayBBoxIntersect4Neon(const Ray&amp;amp; ray,
                        const BBox4&amp;amp; bbox4,
                        IVec4&amp;amp; hits,
                        FVec4&amp;amp; tMins,
                        FVec4&amp;amp; tMaxs) {
    FVec4 rdir(vdupq_n_f32(1.0f) / ray.direction.f32x4);
    /* since Neon doesn't have a single-instruction equivalent to _mm_shuffle_ps, we just take
    the slow route here and load into each float32x4_t */
    FVec4 rdirX(vdupq_n_f32(rdir.x));
    FVec4 rdirY(vdupq_n_f32(rdir.y));
    FVec4 rdirZ(vdupq_n_f32(rdir.z));
    FVec4 originX(vdupq_n_f32(ray.origin.x));
    FVec4 originY(vdupq_n_f32(ray.origin.y));
    FVec4 originZ(vdupq_n_f32(ray.origin.z));

    IVec4 near(int(rdir.x &amp;gt;= 0.0f ? 0 : 3), int(rdir.y &amp;gt;= 0.0f ? 1 : 4),
            int(rdir.z &amp;gt;= 0.0f ? 2 : 5));
    IVec4 far(int(rdir.x &amp;gt;= 0.0f ? 3 : 0), int(rdir.y &amp;gt;= 0.0f ? 4 : 1),
            int(rdir.z &amp;gt;= 0.0f ? 5 : 2));

    tMins =
        FVec4(vmaxq_f32(vmaxq_f32(vdupq_n_f32(ray.tMin),
                                (bbox4.corners[near.x].f32x4 - originX.f32x4) * rdirX.f32x4),
                        vmaxq_f32((bbox4.corners[near.y].f32x4 - originY.f32x4) * rdirY.f32x4,
                                (bbox4.corners[near.z].f32x4 - originZ.f32x4) * rdirZ.f32x4)));
    tMaxs = FVec4(vminq_f32(vminq_f32(vdupq_n_f32(ray.tMax),
                                    (bbox4.corners[far.x].f32x4 - originX.f32x4) * rdirX.f32x4),
                            vminq_f32((bbox4.corners[far.y].f32x4 - originY.f32x4) * rdirY.f32x4,
                                    (bbox4.corners[far.z].f32x4 - originZ.f32x4) * rdirZ.f32x4)));

    uint32_t hit = neonCompareAndMask(tMins.f32x4, tMaxs.f32x4);
    hits[0] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (0)));
    hits[1] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (1)));
    hits[2] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (2)));
    hits[3] = bool(hit &amp;amp; (1 &amp;lt;&amp;lt; (3)));
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 8: Neon version of the compact Williams et al. 2005 implementation.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Even if you only know SSE and have never worked with Neon, you should already be able to tell broadly how the Neon implementation in Listing 8 works!
Just from the name alone, &lt;code class="language-plaintext highlighter-rouge"&gt;vmaxq_f32()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;vminq_f32()&lt;/code&gt; obviously correspond directly to &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_max_ps()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_min_ps()&lt;/code&gt; in the SSE implementation, and understanding how the ray data is being loaded into Neon’s 128-bit registers using &lt;code class="language-plaintext highlighter-rouge"&gt;vdupq_n_f32()&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_set1_ps()&lt;/code&gt; should be relatively easy too.
However, because there is no fancy single-instruction shuffle intrinsic available in Neon, the way the ray data is loaded is potentially slightly less efficient.&lt;/p&gt;

&lt;p&gt;The largest area of difference between the Neon and SSE implementations is in the processing of the tMin and tMax results to produce the output &lt;code class="language-plaintext highlighter-rouge"&gt;hits&lt;/code&gt; vector.
The SSE version uses just two intrinsic functions because SSE includes the fancy high-level &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_cmple_ps()&lt;/code&gt; intrinsic, which compiles down to a single &lt;code class="language-plaintext highlighter-rouge"&gt;CMPPS&lt;/code&gt; SSE instruction, but implementing this functionality using Neon takes some more work.
The &lt;code class="language-plaintext highlighter-rouge"&gt;neonCompareAndMask()&lt;/code&gt; helper function implements the &lt;code class="language-plaintext highlighter-rouge"&gt;hits&lt;/code&gt; vector processing using four Neon intrinsics; a better solution may exist, but for now this is the best I can do given my relatively basic level of Neon experience.
If you have a better solution, feel free to let me know!&lt;/p&gt;

&lt;p&gt;Here’s how the native Neon intrinsics implementation performs compared with using sse2neon to translate the SSE implementation.
For an additional point of comparison, I’ve also included the Rosetta 2 SSE result from the previous section.
Note that the speedup column for Rosetta 2 here isn’t comparing how much faster the SSE implementation running over Rosetta 2 is with the compact scalar implementation running over Rosetta 2; instead, the Rosetta 2 speedup columns here compare how much faster (or slower) the Rosetta 2 runs are compared with the &lt;em&gt;native&lt;/em&gt; arm64 compact scalar implementation:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2 Speedup over Native:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Compact:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;41.8187 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;81.0942 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.5157x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;SSE:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;13.6353 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.0669x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;SSE2NEON:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;12.3090 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.3974x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Neon:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;12.2161 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.4232x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I originally also wanted to include a test that would have been the reverse of sse2neon: use Intel’s &lt;a href="https://github.com/intel/ARM_NEON_2_x86_SSE"&gt;ARM_NEON_2_x86_SSE&lt;/a&gt; project to get the Neon implementation working on x86-64.
However, when I tried using ARM_NEON_2_x86_SSE, I discovered that the ARM_NEON_2_x86_SSE isn’t quite complete enough yet (as of time of writing) to actually compile the Neon implementation in Figure 8.&lt;/p&gt;

&lt;p&gt;I was very pleased to see that both of the native arm64 implementations ran faster than the SSE implementation running over Rosetta 2; which means that my native Neon implementation is at least halfway decent, and which also means that sse2neon works as advertised.
The native Neon implementation is also just a hair faster than the sse2neon implementation, which indicates that at least here, rewriting using native Neon intrinsics instead of mapping from SSE to Neon does indeed produce slightly more efficient code.
However, the sse2neon implementation is very very close in terms of performance, to the point where it may well be within an acceptable margin of error.
Overall, both of the native arm64 implementations get a respectable speedup over the compact scalar reference, even though the speedup amounts are a bit less than the ideal 4x.
I think that the slight performance loss compared to the ideal 4x is probably attributable to the more complex solution required for filling the output &lt;code class="language-plaintext highlighter-rouge"&gt;hits&lt;/code&gt; vector.&lt;/p&gt;

&lt;p&gt;To better understand why the sse2neon implementation performs so close to the native Neon implementation, I tried just copy-pasting every single function implementation out of sse2neon into the SSE 4-wide ray-box intersection test.
Interestingly, the result was extremely similar to my native Neon implementation; structurally they were more or less identical, but the sse2neon version had some additional extraneous calls.
For example, instead of replacing &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_max_ps(a, b)&lt;/code&gt; one-to-one with &lt;code class="language-plaintext highlighter-rouge"&gt;vmaxq_f32(a, b)&lt;/code&gt;, sse2neon’s version of &lt;code class="language-plaintext highlighter-rouge"&gt;_mm_max_ps(a, b)&lt;/code&gt; is &lt;code class="language-plaintext highlighter-rouge"&gt;vreinterpretq_m128_f32(vmaxq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))&lt;/code&gt;.
&lt;code class="language-plaintext highlighter-rouge"&gt;vreinterpretq_m128_f32&lt;/code&gt; is a helper function defined by sse2neon to translate an input &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; into a &lt;code class="language-plaintext highlighter-rouge"&gt;float32x4_t&lt;/code&gt;.
There’s a lot of reinterpreting of inputs to specific float or integer types in sse2neon; all of the reinterpreting in sse2neon is to convert from SSE’s generic &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; to Neon’s more specific types.
In the specific case of &lt;code class="language-plaintext highlighter-rouge"&gt;vreinterpretq_m128_f32&lt;/code&gt;, the reinterpretation should actually compile down to a no-op since sse2neon typedefs &lt;code class="language-plaintext highlighter-rouge"&gt;__m128&lt;/code&gt; directly to &lt;code class="language-plaintext highlighter-rouge"&gt;float32x4_t&lt;/code&gt;, but many of sse2neon’s other reinterpretation functions do require additional extra Neon instructions to implement.&lt;/p&gt;

&lt;p&gt;Even though the Rosetta 2 result is definitively slower than the native arm64 results, the Rosetta 2 result is far closer to the native arm64 results than I normally would have expected.
Rosetta 2 usually can be expected to perform somewhere in the neighborhood of 50% to 80% of native performance for compute-heavy code, and the Rosetta 2 performance for the compact scalar implementation lines up with this expectation.
However, the Rosetta 2 performance for the vectorized version lends further credence to the theory from the previous section that Rosetta 2 somehow is better able to translate vectorized code than scalar code.&lt;/p&gt;

&lt;div id="2021-09-07-autovector-implementation"&gt;&lt;/div&gt;
&lt;h2 id="auto-vectorized-implementation"&gt;Auto-vectorized Implementation&lt;/h2&gt;

&lt;p&gt;The unfortunate thing about writing vectorized programs using vector intrinsics is that… vector intrinsics can be hard to use!
Vector intrinsics are intentionally fairly low-level, which means that when compared to writing normal C or C++ code, using vector intrinsics is only a half-step above writing code directly in assembly.
The vector intrinsics APIs provided for SSE and Neon have very large surface areas, since a large number of intrinsic functions exist to cover the large number of vector instructions that there are.
Furthermore, unless compatibility layers like sse2neon are used, vector intrinsics are not portable between different processor architectures in the same way that normal higher-level C and C++ code is. 
Even though I have some experience working with vector intrinsics, I still don’t consider myself even remotely close to comfortable or proficient in using them; I have to rely heavily on looking up everything using various reference guides.&lt;/p&gt;

&lt;p&gt;One potential solution to the difficulty of using vector intrinsics is compiler &lt;a href="https://en.wikipedia.org/wiki/Automatic_vectorization"&gt;auto-vectorization&lt;/a&gt;.
Auto-vectorization is a compiler technique that aims to allow programmers to better utilize vector instructions without requiring programmers to write everything using vector intrinsics.
Instead of writing vectorized programs, programmers write standard scalar programs which the compiler’s auto-vectorizer then converts into a vectorized program at compile-time.
One common auto-vectorization technique that many compilers implement is loop vectorization, which takes a serial innermost loop and restructures the loop such that each iteration of the loop maps to one vector lane.
Implementing loop vectorization can be extremely tricky, since like with any other type of compiler optimization, the cardinal rule is that the originally written program behavior must be unmodified and the original data dependencies and access orders must be preserved.
Add in the need to consider all of the various concerns that are specific to vector instructions, and the result is that loop vectorization is easy to get wrong if not implemented very carefully by the compiler.
However, when loop vectorization is available and working correctly, the performance increase to otherwise completely standard scalar code can be significant.&lt;/p&gt;

&lt;p&gt;The 4-wide ray-box intersection test should be a perfect candidate for auto-vectorization!
The scalar implementations are implemented as just a single for loop that calls the single ray-box test once per iteration of the loop, for four iterations.
Inside of the loop, the ray-box test is fundamentally just a bunch of simple min/max operations and a little bit of arithmetic, which as seen in the SSE and Neon implementations, is the easiest part of the whole problem to vectorize.
I originally expected that I would have to compile the entire test program with all optimizations disabled, because I thought that with optimizations enabled, the compiler would auto-vectorize the compact scalar implementation and make comparisons with the hand-vectorized implementations difficult.
However, after some initial testing, I realized that the scalar implementations weren’t really getting auto-vectorized at all even with optimization level &lt;code class="language-plaintext highlighter-rouge"&gt;-O3&lt;/code&gt; enabled.
Or, more precisely, the compiler was emitting long stretches of code using vector instructions and vector registers… but the compiler was just utilizing one lane in all of those long stretches of vector code, and was still looping over each bounding box separately.
As a point of reference, here is &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGISWakrgAyeAyYAHI%2BAEaYxBJcAMykAA6oCoRODB7evv6BaRmOAqHhUSyx8VxJtpj2xQxCBEzEBDk%2BfgG19VlNLQSlkTFxCckKza3teV3j/YPllaMAlLaoXsTI7BzmiXhUANRYNOHoEAD6Z6oAHABsZzeSF0smGgCCO2HI3lj7JoluqhYLDCBGIYQAdAg/tgXu8zIk6ntDphjphThcmC1kAgHk9YR8GF8vD8/m4WiwzuEBJDofj4a49nTdoTvphfv8WEwCAgaYlsPtfm9YeNiF4HPsAGIANUwyEkvwA7FY3gKBV4GFlFRYBQB6HX7RRs9Wa/jEfYEACeKTZKXVGqM%2BwuLC4Ziu%2B0M6H2VFoqC5iTMqkeBCZSKOYTR50ut3ujzOzxVqsTTpdbudrr%2ByrhCNooZR4fRZ0xGxxsfjr0Tie9voI/sDZwIXtrkgzTIZVFhFdVIrFDZMSo7nYrVa5%2B1ULYTg9Vw4bFvH5cnU59I4AXnOF4vq/sAO5rwd9gAiu8708OXKYJgArBZmxfD4lM4mD3OBwLpbLJBAlvsQKOIBpwRoVArPsFp/gBQGkPsy5gYBwFbjBQFak%2BQrwrmqIFtcdy4nGL6SjKcqRmmbpUJIX4/kR1CkUhCqHih2ZofmkZFti2Flomb4EdOTb1l6VE/lQTaUV%2BfZWDRrYMPg7ZChOeHvtQS4NqoZyQSeFrKV6ClQepJ5bnG36/kpwGgWpwHQcucaQfBunCf2YkyRxH4nkpKmaWpLmbuZZEGRZIEQCZkFmT58H/rB1G0e8Mknqg1rEFyJCXlYt4QCC%2Bx4F%2BaAMOMWr7MQmAEOsDCns0CV4Jed7ash84CtO5g3Ps0VxHFxAJWVyWCKlNnarl%2BXEIV6BniVZUZoq4W4Q59UxU11gQBlWUObV%2BzRJ1OV5QVskEaovyWIt4KqJBFpbdq0TghaAWHTty6Wedx1bs894jWN%2BHyg1sVEMQAC0M0CHNT0LUt2XdWtDkQJt707XtIH7GDx2nVBUMXVd0Pgrdw2VexT0TY1b0AFRfZlDY1WYdX/SJK09YVwObdji37fs1PRGd9NXfTd0VXZVXrc9k1vTqeNZYTxPLYDvWcyD%2Bz6gzkMS2d0vbuLi2sw9Qpife%2BJvGEObhJzmOvSQvOzQTCkLUwkEG5zf2dbhwsU09EBMPLx0Q/bEsnZBzsI%2B6DvI6z%2BLs8KoI9vsACSGMibhxoCEhMmJt24ph9Hg4pWOqsJ52KWzinHOTilq6Z%2BuAopTued7irD6J%2B1/XFVeN7lbhT6Z7hIdyV5qh/kZbcBR325t2Fje20n6np4P7XmZBhd6T%2BreGftfk%2BYFcEQNZvcyU3BED2P7VualI8T957f%2BVBECeZZPdh37MkpS9U1Xq1KVpfsZuk9bRXnlepW3qj7OJiCC1X29LVJTvkLVaItK6vwsO/cqSt3ilzVq8WODYLAWFQJteOHMI6FTQZOE8aBepxAUAlG4Q17qDj1NvLAqg0TugUL8K8wIGAAA19j0IAJrMLCAALWYUwVQTDOSqDYfwjhQ1U4aU3Lg8IxAFCvFoMGK8ZghpXkSMQ7U4t9RhAoVQpgNCJFxASvQgA8sQAAsjwxRFgGEsOER/GS9dMy4SQSgvmDZ5pE3YdkEgkjTbfRcb9Nx/CPB4OIJbURuipEyLkRYDQ5jom3nZPuL09CID0MCZI3akEAmeLiLtH2oSsnhNkQlWJh4rxcDKvExJYRklhFSdk2GmSgknVyVnVUYTpGFKvMUhKCi4l/ASVQJJKT8ngkutw1QtTiAjOaZONpESEplI/p08pfTEk8OqR4xpEMGlpNUNMwcsyOkWAWSUo5yzEj9P4esiZrsxnXItHszsBzInHO6Wci5ayhmbIyTw65uyjzQMeu%2BdxEzPwPx8QDEBNs5JtKKWVbxQSCGlLhWChFrzbyK0qoCuUtz8mgsfkqMmQNbYwqUcikl14yX5MRRYC8ZUMV%2B1gdJeBAdxQACUmAHSwaqca%2BBcoOCyP88aJA8DADCP8k8BBjFiuLtVTSkqzENxkuy0CZtXF1V5bKBo6lVV%2BLqsK0VDBtJyqlYa9yI55WGVwomH8Gr%2BUCAgLarVwF9VVJdYa4CkqqmevdZBC1EALV6TPqNRlEVXjRFQJ4HKHLHGqCDoIfBmqRBiBaB4FgKQmAOGcfsZVC1YqwzNjGv64aIYC3NCas1wY3EWpCRzcaxBeUVK4OBeWebwSOoFTK4OGNwgtDagQCA9a8CTNQXyFZIUDh/AlPsDQ%2BlEjARBAO3lJ12QwnOdO5tk79hcH0qRUgVry79sHZM5cK6x0bsSFOsw%2BkLxLAeV2oFVBe0LqPbtU9a7x3sinYkfSGh52CEXUO5dtJ33nqnfKH8XAVj7rTv%2Bl9J7gMJI/Zui8%2BkzC3ufDJb1FSBlrJw63Vt3rIIQGiMW8E5Ke3DvKWDVtbqcl0xyku3Ze7RHYMucR0j5HMAtBOlRqNFpwS0fufRl9p1oP5wFOxlBZGqUJQoyM3jNGwQGqmcJpdy50OdotdhpJAyqkEdMRDSTqhpOoqvI%2ByjcTqMcoE0piEuzVOAaY2J1jVSjMmckdS8zPHLN8ZsyKiEQnqYieYy08TEmSNSfJV51cPnFP%2BYYCpoLamNNlzCxWK2kKy1hHZG4FZFrnzn1eAAN1QHgT0eaY1xoIAmhwkgk0GGIKm9NmazY5rcXmkLaWuv7PBYWtxEXVAaE691kbAoC3ILHP14tXBhuje6%2BNlBRaUGBGc3NmZvWJtLdUMkVba3Oyr2bG4hAhAFCzb2%2BJtVWXMpnfO%2BuS7FqFA1sTMdgg1LikVL7UsCrE2qs1eEKIBrTWM2Ho5ZBAbQ2rtveRQ92F6L/kvepccj7IIvvRp%2B/GqRiaAcptQGm4HA7QeLWm76k1iPocGbJ3DztCO0UJJWZ977KDfuY4cPVnHePM0daJ8tknYRqU9P3CTnh/O6Xw5OwlZRvS10M7R0zjHShWfY8a7j5rIPYYDeSJDiX5Phfa6p/Y9mHAVi0E4BeXgfgOBaFIKgTguXLDWH2AoNYGw2Q7B4KQAgmgjcrAANYgAvENk3HBJDm699bzgvAFAgCG57y3RvSBwFgEgNAaa6BxHIJQFPKQ0/xC%2BIYYA1QNAzZoLI/BlBohh%2BiGEFoFpODu5T2wQQBiGC0Fr3H0gWBORGHEO39tRXMBR/b5gShyAvDVbr7wEEdQw85miLFYgFoPBYDD6CPALAJ8rG9EwYACgpR4EwFuAx1oLfu/4IIer7ApAyEEIaNQYfdAzYMEYFA1hrD6DwNEKPkAVjRQaIP96BiiQkedQmqWQLgEkUwfgXACoQQEk8wwwVQCoNwqQ6QmQAgkBEgMBhQaBDA8BFQIw0ByBdgoBAgfQkwngHQmB3QJBjQEwAw4YCwBBSBtgdBGB0BLBcwDBCBmBNwKwTu6wmwegoImAWwPAxupuoe7eNuHAmE70DwD8T%2BwAW6Ta/426EAduVglgkEuAhAJAW0iQM2%2BwTWOe%2BhkGvAseWgt6pAfuAe%2BgnAIepAFuVu0hke0eHuXuVhQeZgkhzhEe7hceVh/eUiYBkgQAA"&gt;the x86-64 compiled output&lt;/a&gt; and &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGISWakrgAyeAyYAHI%2BAEaYxBJcAMykAA6oCoRODB7evv6BaRmOAqHhUSyx8VxJtpj2xQxCBEzEBDk%2BfgG19VlNLQSlkTFxCckKza3teV3j/YPllaMAlLaoXsTI7BzmiXhUANRYNOHoEAD6Z6oAHABsZzeSF0smGgCCO2HI3lj7JoluqhYLDCBGIYQAdAg/tgXu8zIk6ntDphjphThcmC1kAgHk9YR8GF8vD8/m4WiwzuEBJDofj4a49nTdoTvphfv8WEwCAgaYlsPtfm9YeNiF4HPsAGIANUwyEkvwA7FY3gKBV4GFlFRYBQB6HX7RRs9Wa/jEfYEACeKTZKXVGqM%2BwuLC4Ziu%2B0M6H2VFoqC5iTMqkeBCZSKOYTR50ut3ujzOzxVqsTTpdbudrr%2ByrhCNooZR4fRZ0xGxxsfjr0Tie9voI/sDZwIXtrkgzTIZVFhFdVIrFDZMSo7nYrVa5%2B1ULYTg9Vw4bFvH5cnU59I4AXnOF4vq/sAO5rwd9gAiu8708OXKYJgArBZmxfD4lM4mD3OBwLpbLJBAlvsQKOIBpwRoVArPsFp/gBQGkPsy5gYBwFbjBQFak%2BQrwrmqIFtcdy4nGL6SjKcqRmmbpUJIX4/kR1CkUhCqHih2ZofmkZFti2Flomb4EdOTb1l6VE/lQTaUV%2BfZWDRrYMPg7ZChOeHvtQS4NqoZyQSeFrKV6ClQepJ5bnG36/kpwGgWpwHQcucaQfBunCf2YkyRxH4nkpKmaWpLmbuZZEGRZIEQCZkFmT58H/rB1G0e8Mknqg1rEFyJCXlYt4QCC%2Bx4F%2BaAMOMWr7MQmAEOsDCns0CV4Jed7ash84CtO5g3Ps0VxHFxAJWVyWCKlNnarl%2BXEIV6BniVZUZoq4W4Q59UxU11gQBlWUObV%2BzRJ1OV5QVskEaovyWIt4KqJBFpbdq0TghaAWHTty6Wedx1bs894jWN%2BHyg1sVEMQAC0M0CHNT0LUt2XdWtDkQJt707XtIH7GDx2nVBUMXVd0Pgrdw2VexT0TY1b0AFRfZlDY1WYdX/SJK09YVwObdji37fs1PRGd9NXfTd0VXZVXrc9k1vTqeNZYTxPLYDvWcyD%2Bz6gzkMS2d0vbuLi2sw9Qpife%2BJvGEObhJzmOvSQvOzQTCkLUwkEG5zf2dbhwsU09EBMPLx0Q/bEsnZBzsI%2B6DvI6z%2BLs8KoI9vsACSGMibhxoCEhMmJt24ph9Hg4pWOqsJ52KWzinHOTilq6Z%2BuAopTued7irD6J%2B1/XFVeN7lbhT6Z7hIdyV5qh/kZbcBR325t2Fje20n6np4P7XmZBhd6T%2BreGftfk%2BYFcEQNZvcyU3BED2P7VualI8T957f%2BVBECeZZPdh37MkpS9U1Xq1KVpfsZuk9bRXnlepW3qj7OJiCC1X29LVJTvkLVaItK6vwsO/cqSt3ilzVq8WODYLAWFQJteOHMI6FTQZOE8aBepxAUAlG4Q17qDj1NvLAqg0TugUL8K8wIGAAA19j0IAJrMLCAALWYUwVQTDOSqDYfwjhQ1U4aU3Lg8IxAFCvFoMGK8ZghpXkSMQ7U4t9RhAoVQpgNCJFxASvQgA8sQAAsjwxRFgGEsOER/GS9dMy4SQSgvmDZ5pE3YdkEgkjTbfRcb9Nx/CPB4OIJbURuipEyLkRYDQ5jom3nZPuL09CID0MCZI3akEAmeLiLtH2oSsnhNkQlWJh4rxcDKvExJYRklhFSdk2GmSgknVyVnVUYTpGFKvMUhKCi4l/ASVQJJKT8ngkutw1QtTiAjOaZONpESEplI/p08pfTEk8OqR4xpEMGlpNUNMwcsyOkWAWSUo5yzEj9P4esiZrsxnXItHszsBzInHO6Wci5ayhmbIyTw65uyjzQMeu%2BdxEzPwPx8QDEBNs5JtKKWVbxQSCGlLhWChFrzbyK0qoCuUtz8mgsfkqMmQNbYwqUcikl14yX5MRRYC8ZUMV%2B1gdJeBAdxQACUmAHSwaqca%2BBcoOCyP88aJA8DADCP8k8BBjFiuLtVTSkqzENxkuy0CZtXF1V5bKBo6lVV%2BLqsK0VDBtJyqlYa9yI55WGVwomH8Gr%2BUCAgLarVwF9VVJdYa4CkqqmevdZBC1EALV6TPqNRlEVXjRFQJ4HKHLHGqCDoIfBmqRBiBaB4FgKQmAOGcfsZVC1YqwzNjGv64aIYC3NCas1wY3EWpCRzcaxBeUVK4OBeWebwSOoFTK4OGNwgtDagQCA9a8CTNQXyFZIUDh/AlPsDQ%2BlEjARBAO3lJ12QwnOdO5tk79hcH0qRUgVry79sHZM5cK6x0bsSFOsw%2BkLxLAeV2oFVBe0LqPbtU9a7x3sinYkfSGh52CEXUO5dtJ33nqnfKH8XAVj7rTv%2Bl9J7gMJI/Zui8%2BkzC3ufDJb1FSBlrJw63Vt3rIIQGiMW8E5Ke3DvKWDVtbqcl0xyku3Ze7RHYMucR0j5HMAtBOlRqNFpwS0fufRl9p1oP5wFOxlBZGqUJQoyM3jNGwQGqmcJpdy50OdotdhpJAyqkEdMRDSTqhpOoqvI%2ByjcTqMcoE0piEuzVOAaY2J1jVSjMmckdS8zPHLN8ZsyKiEQnqYieYy08TEmSNSfJV51cPnFP%2BYYCpoLamNNlzCxWK2kKy1hHZG4FZFrnzn1eAAN1QHgT0eaY1xoIAmhwkgk0GGIKm9NmazY5rcXmkLaWuv7PBYWtxEXVAaE691kbAoC3ILHP14tXBhuje6%2BNlBRaUGBGc3NmZvWJtLdUMkVba3Oyr2bG4hAhAFCzb2%2BJtVWXMpnfO%2BuS7FqFA1sTMdgg1LikVL7UsCrE2qs1eEKIBrTWM2Ho5ZBAbQ2rtveRQ92F6L/kvepccj7IIvvRp%2B/GqRiaAcptQGm4HA7QeLWm76k1iPocGbJ3DztCO0UJJWZ977KDfuY4cPVnHePM0daJ8tknYRqU9P3CTnh/O6Xw5OwlZRvS10M7R0zjHShWfY8a7j5rIPYYDeSJDiX5Phfa6p/Y9mHAVi0E4BeXgfgOBaFIKgTguXLDWH2AoNYGw2Q7B4KQAgmgjcrAANYgAvENk3HBJDm699bzgvAFAgCG57y3RvSBwFgEgNAaa6BxHIJQFPKQ0/xHJEVq470viGGANUDQM2aCyPwZQaIYfohhBaBaTg7uU9sEEAYhgtBG9x9IFgTkRhxDd/bUVzAUfu%2BYEocgLw1Wm%2B8BBHUMPOZoixWIBaDwWAw%2BgjwCwGfKxvRMGAAoKUeBMBbgMdaC37v%2BCCHq%2BwKQMhBCGjUGH3QM2DBGBQNYaw%2Bg8DRCj5AFY0UDQo%2B70BiiQkedQmqWQLgEkUwfgM2IQ4YCwIwM2hQmQAgsBegqBDQ8wwwVQ3QkBAgfQkwngHQegdgBBjQEwAwiBuBZBVBGBM2swrQOBFQyBKwTu6wmwegoImAWwPAxupuoe3eNuHAeeBe9sReDo1QAE4I26EAduVglgkEuAhAJAW0iQM2%2BwTWOe6hkGvAseWgt6pAfuAe%2BgnAIepAFuVuIhke0eHuXuRhQeZgQh1hEe9hceRhw%2BUiUBkgQAA"&gt;the arm64 compiled output&lt;/a&gt; for the compact scalar implementation.&lt;/p&gt;

&lt;p&gt;Finding that the auto-vectorizer wasn’t really working on the scalar implementations led me to try to write a new scalar implementation that would auto-vectorize well.
To try to give the auto-vectorizer as good of a chance at possible at working well, I started with the compact scalar implementation and embedded the single-ray-box intersection test into the 4-wide function as an inner loop.
I also pulled apart the implementation into a more expanded form where every line in the inner loop carries out a single arithmetic operation that can be mapped to exactly to one SSE or Neon instruction.
I also restructured the data input to the inner loop to be in a readily vector-friendly layout; the restructuring is essentially a scalar implementation of the vectorized setup code found in the SSE and Neon hand-vectorized implementations.
Finally, I put a &lt;code class="language-plaintext highlighter-rouge"&gt;#pragma clang loop vectorize(enable)&lt;/code&gt; in front of the inner loop to make sure that the compiler knows that it can use the loop vectorizer here.
Putting all of the above together produces the following, which is as auto-vectorization-friendly as I could figure out how to rewrite things:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;void rayBBoxIntersect4AutoVectorize(const Ray&amp;amp; ray,
                                    const BBox4&amp;amp; bbox4,
                                    IVec4&amp;amp; hits,
                                    FVec4&amp;amp; tMins,
                                    FVec4&amp;amp; tMaxs) {
    float rdir[3] = { 1.0f / ray.direction.x, 1.0f / ray.direction.y, 1.0f / ray.direction.z };
    float rdirX[4] = { rdir[0], rdir[0], rdir[0], rdir[0] };
    float rdirY[4] = { rdir[1], rdir[1], rdir[1], rdir[1] };
    float rdirZ[4] = { rdir[2], rdir[2], rdir[2], rdir[2] };
    float originX[4] = { ray.origin.x, ray.origin.x, ray.origin.x, ray.origin.x };
    float originY[4] = { ray.origin.y, ray.origin.y, ray.origin.y, ray.origin.y };
    float originZ[4] = { ray.origin.z, ray.origin.z, ray.origin.z, ray.origin.z };
    float rtMin[4] = { ray.tMin, ray.tMin, ray.tMin, ray.tMin };
    float rtMax[4] = { ray.tMax, ray.tMax, ray.tMax, ray.tMax };

    IVec4 near(int(rdir[0] &amp;gt;= 0.0f ? 0 : 3), int(rdir[1] &amp;gt;= 0.0f ? 1 : 4),
            int(rdir[2] &amp;gt;= 0.0f ? 2 : 5));
    IVec4 far(int(rdir[0] &amp;gt;= 0.0f ? 3 : 0), int(rdir[1] &amp;gt;= 0.0f ? 4 : 1),
            int(rdir[2] &amp;gt;= 0.0f ? 5 : 2));

    float product0[4];

#pragma clang loop vectorize(enable)
    for (int i = 0; i &amp;lt; 4; i++) {
        product0[i] = bbox4.corners[near.y][i] - originY[i];
        tMins[i] = bbox4.corners[near.z][i] - originZ[i];
        product0[i] = product0[i] * rdirY[i];
        tMins[i] = tMins[i] * rdirZ[i];
        product0[i] = fmax(product0[i], tMins[i]);
        tMins[i] = bbox4.corners[near.x][i] - originX[i];
        tMins[i] = tMins[i] * rdirX[i];
        tMins[i] = fmax(rtMin[i], tMins[i]);
        tMins[i] = fmax(product0[i], tMins[i]);

        product0[i] = bbox4.corners[far.y][i] - originY[i];
        tMaxs[i] = bbox4.corners[far.z][i] - originZ[i];
        product0[i] = product0[i] * rdirY[i];
        tMaxs[i] = tMaxs[i] * rdirZ[i];
        product0[i] = fmin(product0[i], tMaxs[i]);
        tMaxs[i] = bbox4.corners[far.x][i] - originX[i];
        tMaxs[i] = tMaxs[i] * rdirX[i];
        tMaxs[i] = fmin(rtMax[i], tMaxs[i]);
        tMaxs[i] = fmin(product0[i], tMaxs[i]);

        hits[i] = tMins[i] &amp;lt;= tMaxs[i];
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 9: Compact scalar version written to be easily auto-vectorized.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;How well is Apple Clang v12.0.5 able to auto-vectorize the implementation in Listing 9?
Well, looking at the output assembly &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGIAMwapK4AMngMmAByPgBGmMQSAOxcpAAOqAqETgwe3r4BQemZjgJhEdEscQlcybaY9iUMQgRMxAS5Pn6BdQ3Zza0EZVGx8UkpCi1tHfndEwNDFVVjAJS2qF7EyOwc5v54VADUWDQR6BAA%2BueqABwAbOe3kpfLJhoAgrvhyN5YByb%2BblULBY4QIxHCADoEP9sK8PmZ/PV9kdMCdMGdLkxWsgEI9nnDPgxvl5fv83K0WOcIgIoTCCQjXPt6XsiT9MH8ASwmAQELT/NgDn93nCJsQvA4DgAxABqmGQkj%2BiSs70Fgq8DGyiosgoA9DqDop2erNfxiAcCABPVLs1LqjVGA6XFhcMzXA6GdAHKi0VDc/xmVRPAjM5HHcLoi5XO4PJ7nF4q1WJp0ut3O13/ZXwxG0UOo8MY85Yza42Pxt6JxPe30Ef2B84EL21yQZ5mMqhwiuq0XihsmJUdzsVqvcg6qFsJweq4cNi3j8uTqc%2BkcALznC8X1YOAHc14O%2BwARXed6dHblMEwAVgszYvh/8mcTB7nA8FMrlkggywOIFHEA0EI0KhVgOC0/wAoDSAOZcwMA4CtxgoCtSfYUEVzNECxue48TjF8pVleVIzTN0qEkL8fyI6hSKQxJDxQ7M0PzSMixxbCy0TN8COnJt6y9KifyoJtKK/PsrBo1sGHwdthQnPD32oJcG1Uc5IJPC1lK9BSoPUk8tzjb9fyU4DQLU4DoOXONIPg3ThP7MSZI4j8TyUlTNLUlzN3MsiDIskCIBMyCzJ8%2BD/1g6jaI%2BGST1Qa1iG5EhLysW8IFBA48C/NAGAmLUDmITACA2BhTxaBK8EvO9tWQ%2BdBWncxbgOaL4ji4gErK5LBFSmztVy/LiEK9AzxKsqM0VcLcIc%2BqYqa6wIAyrKHNqg4Yk6nK8oK2SCNUP5LEWiFVEgi0tu1GIIQtALDp25dLPO46txee8RrG/CFQa2KiGIABaGaBDmp6FqW7LurWhyIE296dr2kCDjB47TqgqGLqu6GIVu4bKvYp6Jsat6ACovsyhsarMOr/pElaesK4HNuxxb9oOamYjO%2Bmrvpu6Krsqr1ueya3p1PGssJ4nlsB3rOZBg59QZyGJbO6Xt3FxbWYe4UxPvAl3nCHMIk5zHXpIXnZoJhSFqYSCDc5v7Otw4WKaeiAmHl46IftiWTsg52EfdB3kdZgl2ZFMEewOABJDGRNw40BCQmTE27CUw%2BjwcUrHVWE87FLZxTjnJxS1dM/XQUUp3PO9xVh9E/a/riqvG9ytwp9M9wkO5K81Q/yMtuAo77c27CxvbaT9T08H9rzMgwu9J/VvDP2vyfMCuCIGs3uZKbgiB7H9q3NSkeJ%2B89v/KgiBPMsnuw79mSUpeqar1alK0oOM3SetorzyvUrb1R9nE1BBar7elqkp3yFqtEWldX4WHfuVJWHxS5qzeLHBsFgLCoE2vHDmEdCpoMnCeNAvV4gKASrcIa91Bx6m3lgVQ6J3QKD%2BFeEEDAAAaBx6EAE1mHhAAFrMKYKoJhXJVBsP4RwoaqcNKblwREYgCg3i0GDFeMwQ0rz%2BGIdqcW%2BpwgUKoUwGhEj4gJXoQAeWIAAWR4YoiwDCWHCI/jJeumZcJIJQXzBs80ibsJyCQSRptvouN%2Bm4/hHg8HEEtqI3RUiZFyIsBocx0Tbwcn3F6ehEB6GBMkbtSCATPHxF2j7UJWTwmyISrEw8V4uBlXiYk8IyTwipOybDTJQSTq5KzqqMJ0jClXmKQlBRcT/gJKoEklJ%2BSISXW4aoWpxARnNMnG0iJCUykf06eUvpiSeHVI8Y0iGDS0mqGmYOWZHSLALJKUc5Z/h%2Bn8PWRM12YzrkWj2Z2A5kTjndLORctZQzNkZJ4dc3ZR5oGPXfO4iZn4H4%2BIBiAm2ck2lFLKt4oJBDSlwrBQi15t5FaVUBfKW5%2BTQWPyVGTIGtsYVKORSS68ZL8mIosBeMqGK/awOkvAgOEpHGBijugjUkcsGDnGuSohH8SH5zISQLACR3EMIyeEFhUqGAcO%2BbwhVMqxnCNETgqlkojbyPMcoxZFLBWqLIRozAlDPTaJRZI/R4QjGmLHHqyx1iTkxBiCgxYeibEtOqppNpmrqxzKvAKk5Nd/l2Lgd/Bgmt2TJldNTT5kihBCGwKCp%2BkKtq3HJcUiERFP7hXDZGx05wiKxp%2BfkhNSbgHkzTeS3V%2B4s0phzXAz4hwwynCYtiEs%2BIL4RvDGIv0AYgyxpqfkyImABDJoJc/WqGayoQgEv2htXb81cX7fWYt4zh2joYOOrqqap1UoSjW2dTYF1ZjbGG1UGse0ADdUB4E9EoAgbK2oNhdaoCo3j8bm38UOoJH6frvgWts%2BIITPUWvwb67ksK9VdKvK%2BiobzKlbrjdkrZJbNkPIrD6rVUTzEvNg66nwCGBlVOQ5M%2BpaG0n3P%2BYmLD1YoMnJ6U6gjLAiODJ/Wk0ZQHJnLgwzRjV2G8M4b1XBwjvTzmrNbqR9JOL0PUdafxujSK9WCZEyxsT7zJPsbqQqu5vH5MIog888xjGEqqdYx8rTkzUPrtk8XQUmKZJso/GbNlxs/2IOQWONxksXOeYWsgdzBxXNuPQCBycD6n1BHdFmyzn43ZZoo/ET8enBQRc8xAFIO1SNxay4l4gyW5Opbyk%2BswpsYsbMkTl5ACWbOVeWClg4aWnH%2BEgugcrILgJta4wVuzALGURWZWKCUAAlJgB0eWvgxvgXKDhsj/PGiQPAwBwj/JPAQYxK3etrdtc%2BGSo3QJm1cXVabcpGjqUO34uqi3lsMG0ppdb4Q7ubnWzwnCoifwndm2Oz7Z3gLXaqf927wEHtbpBz5F7rcId6TPqNfrcIyEeBYKkJgEoFCiAMGaK9%2BDNRbnBAQAgjBzSoEWuyTA2i6AHSYF4IgWOHCLeXOiOEN6705TG2yoOgh8GnckG8anqB8JvTwAz5xBx9sLViqdXC%2BdpcLl8yg5s3nX3SClzL1XiZV4K7qggQgChSAq7V2ro75oNuZT16Ig30ujcQ4UGFjcI5iDTYPQh0mXBwLywlxCH72RpOu8Au7sbnu8AzcaDc33hx9Qe69zSZcD0tuaQd0HhhCUa4VKfo7pZt5IIJ%2Bahn/cWf09CbzzlAvxTY9ly9ZubPLDk/O4nQX45%2Beg/zORdn5vmfi9N6Uwk0NkV4/Tesfq7v4m0%2Bd4sIxxvOex8t4L%2BPjvk/GNl9wlFcEN2k/V1r11APgPpMe%2B3xDXfK/IT7634fhgu1F%2B983ID6v6/1MAxP0tyEsMD%2BP7P8/h/N2bkv8/%2BN/rlZNKA4D4p4rJPwf6QijLf7gFZ5gFn4QEwEjIX4cwnhtAm41536gEWgQgg7QGYHYGs64Em44FYEm6IH/6V4Q5oFD7d4ToB4Q5EF0H4HEE8L0G2qIF9xAoRCtDPoQCt654ciwjiYhSHD/CSgHAaD6T%2BDASgg8H15nICEJJCEciiFcD6SkRm6gblwEAyGj4L50iCFu4iEHBmD6QXj1b/Ia5ehcHSG8GF78ErKKGGH%2BD6QaBSGCDaGT7HJ2H6F%2B6GEKg/hcCrD66aHuFopD7yFiEGH%2BCiEXj6RmBmENyX4jipDEAYA9ixKD67ZZjJFMDABcgPwGAOg%2BjRQHC06C7C6MBMAxD0BsRTgkAHDPqpQVLRL3R4AchuAHDNgtHWDWC26djJGpEODpGQIVLOry4QjkqcFkbmLDFgzX6DQGpBEg7UrDErKjGBjjH7pXiTEjLTHlKzGn4D6QKFYHD9HoBpHzFUEnEpFnGDEXF0xz434QIqKLEm7LEIZLF3HUzZ6HHPGiKnHnFvzmatz/G3GAnt4fFgn7gNYQlPF35rGSAbGopbFk5Wa7FxL7Gv5r6wm1yiIwkrHiZ4nlJfHTZYlHG9aJiEl34DJrIoEraQmQSUlQnHGMkVLUnAnXEAnYkMmvEXHNJBEgkEBDEIbwmImSLUpUCtAnRokJIYk3aPFknl4VjW53GrFK6in4IJQSncbSnwyAEXHHEClCl36GmfEPH6nkmqjKmQkVJWnYn3HfHmmKmJgmnWkrLEZboulcnG48JvHorMm2q%2BmXEinkpam7Q6mynhCkm/Gga2n4kJKxlElz5RkLG4kBkqnibuk8EUH0nemqCBnQlpmukZlJKemQLck%2Bm8mZGTja4ECBk2k8lFluArIJkpkcyYo0QcCrC0CcAXi8B%2BAcBaCkCoCcBNmWDWCNbrCbDsi7A8CkAECaCdmrAADWIAF4QQ3ZHAkgfZC5Q5nAvACgIAQQ85A5nZpAcAsASAaASOdA8Q5AlAV5qQN5CQ3whgwAXAXAGgKQNAsi%2BClAMQO5MQ4QrQFonAs5V5bAggBiEaIFJ5pAWAXIRg4gsFXuWOB5sFJqco1O2ws5oI9QO5OYMQsUxAFoHgWAO5YIeALAoFp53oORCg0oeAmAW4Bi1o/Zs5/AggIgYg7AUgMggghoagO5ugKQBRxg3Rlg%2BgeAMQB5kAqw0UjQaF70Bi/g%2B59Qp22QLgEk0wfgCIwQEkCwIwCQgQiQaQGQWQAg2lIAulRQ5lDABllQowxlPQ6lAg/QUwngnQVlLWdgLlTQkwgw4YbqRlGgJlcw7leQOl3l/l9lSwxlqwCgk5WwegYImA2Fp5G5vZpA/Zg5w5HAmE70jw%2BRr5Bw75AEEIKhEAo5VgElBwuAhAdRM5kEiOj59AZoM5ywvAx5Wg9WpAK5a5%2BgnAW5WVO5uV%2B5h5c5C5PVG5Zg25sFo1E1J5PVWOUiGlkgQAA%3D%3D"&gt;on x86-64&lt;/a&gt; and &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGe1wAyeAyYAHI%2BAEaYxCAAnGakAA6oCoRODB7evnrJqY4CQSHhLFEx8baY9vkMQgRMxASZPn5cFVXptfUEhWGR0XEJCnUNTdmtQ109xaUDAJS2qF7EyOwc5gDMeFQA1Fg0IegQAPpHqgAcAGxHF5InsyYaAIIbwcjeWNsm626qLCzBBGIwQAdAgvtgHs8zOtKltdph9phDicmPVkAgbndIS8GG8vB8vm56iwjiEBKDwdjoa4tlTNrj3phPt8WEwCAgKetsNtPk9IUNiF4HNsAGIANUwyEknwA7FYnjyeV4GOlZRYeQB6DXbRRM5Wq/jEbYEACeiSZiWVKqM2xOLC4ZjO20M6G2VFoqDZ6zMqluBDpcL2wSRx1Ol2utyO9wVitjdodTvtjq%2B8qhMNogYRweRR1Rywxkejj1jsfdnoI3t9RwIbsrkhTdJpVEhJcVAqFNZMcpbrZLZbZ21UDZjvcV/ZrJuHxdHY49A4AXlOZ7Py9sAO5L3tdgAim9b492bKYJgArBZ6yfd%2BtU7Gd1OezzxZLJBBZtsQIOIBpgRoqPNtiaX4/n%2BpDbPOQG/v%2Ba4QX%2Bap3ny0KZoiObnFcmJRg%2BooSlKoZJk6VCSG%2BH54dQhFwTKu4IemSHZqGebouhRaxk%2BOHjnW1ZumRH5UHWpFvl2VgUY2DD4M2fIjlhz7UHONaqEcoEHia8lujJYHKQea5Ru%2Bn5yf%2BgFKf%2B4HzlGoHQZp/HdkJEksS%2BB5yQpqlKQ5q7GUROkmQBEAGaBRkedB36QeRlHPBJB6oOaxBsiQp5WJeEAAtseBvmgDBDGq2zEJgBBLAwh51DFeCnle6rwdOPLjuYFzbOF0RRcQMVFfFgiJRZ6qZdlxC5egR4FUVKaysFmE2dVEV1dYEApWlNmVdsEStRlWU5ZJOGqJ8lizcCqigSaa3qhEwImj5u0bfOpnHfta73NeA1Ddh0o1ZFRDEAAtBNAhTXdM1zel7VLTZECrc9G1bQB2xA/th1gWDJ1neDwKXf1pXMXdI21U9ABUb2pTWFVmFV30CQtHW5f9q3o7N23bOTERHdTZ3U1dJVWWVy33aNT0aljaW4/j82/Z1rMA9s2o06DItHeL67C7NjM3XyQnXtiTzBBmISs6jj0kJzk04zJM1MKBOus19rWYfzJN3RATDS/tIPWyLB2gfbMPOjb8OM9izP8oCHbbAAkijAmYfqAhwRJsbtsKQfh72CVDorMetglk4Jyzo4JYuqfLjyCUblnW4KzesfNd1%2BVnhexWYXeqeYQHUluaoX56U3Pkt%2BuTdBbXltx8pye981xmgbnWkfo3unbV5Hm%2BVBEDmZ3El1zhPdD81TmJQPI/uc33lgRArmmR3QdexJCUPWNZ6NQlSXbEbhPm3lx5noVl6I8zsYAjNZ9PQ1cVX3zi0C1Lo/Cwz9ipy2eIXJWjxI41gsBYVAq1o4sxDrlJBo4DxoE6tEBQMULh9Wur2LU68sCqCRM6BQnwzz/AYAADW2NQgAmvQ4IAAtehTBVB0NZKoJh3CWF9UTipVcmCQjEAUI8Wg/ozxmD6medY%2BD1TC21MEEhZCmAUJEdEGK1CADyxAACyHDZEWBoQw/hL8JLV1TJhOBCCuY1mmnjZhGQSCiMNu9Bxn0nHcI8Fg4gptBGaLERIqRFgNDGPCZeZk243TUIgNQ3xojNqgR8a46Im0PaBLScEyRMVIm7jPFwIq0TYnBHicERJ6TIapL8QdTJadFRBPEbks8%2BSYoyKiV8GJVA4kJOycCU67DVCVOIAM%2Bpo4mkhJikUl%2BrTildNiRw8pLjakgxqUk1Q4zeyTJaRYGZBS9nzPWN07hyyRmOyGeck0WzWw7NCfs9pRyTlLL6aslJHDzmbL3OA26z5nEjNfDfDxP0AEWykk0vJRV3F%2BJwYUqFQKYWPMvLLUqvypSXOyYC2%2BcoiZ/UthCuR8KCXniJdk2FFgTxFRRV7SB4loE%2B2FLY30YdkEqlDmg3sw1iV4JfgQ7ORCSBYBiM4mhKTggMLFQwFh7zOEyolUM/hgiMFkpFHraRxj5GzJJbyxRRCVGYFIa6dRCLRHaOCHowxQ4tWmPMQciIEQEHTC0RYhp5VVJNNVeWKZZ4eUHIrt8qxUCXg7CDAcOiaICxYhPgwVWTI2I%2Bj9OTV5ojQiYAEICu%2BoK1oXGJfk4EPEE2v2Cu/GNwYhFegTdWJNHzsmpvTf/Ym2biWau3PmusRbhKiSgSW2N2wABuqA8CuiUAQJlTUawOtUMUdx2NjbeIqdkmdH1nwzXWdEAJrqTXYM9WySFWq2lnkncUJ5pSGBnP6Wsmtqybklg9WqsJxiHmHsdT4E9PSynJqqTKq5N7Yx3vLHug5HS7UvpYG%2B3pC7amDLXaM%2Bcv7GkqvvU%2Bh9Wqj2vs6ccxZjdP2jMvcMi98GeT/t3XCrVyG0NgYw887DkGknVKvXRwjW6xE7vucY4DMUKPgZebR9JeHPk3tRRJJlL4jZMv1ku2B8ChxONFmJ6TM1kCSe2OJpx6AN2jhHWOjQTtgQ4dfLpmDr4mNaekxAVoG19P/n2kZ2YJmspjoSDfPTvHiAGec7Z%2Bzo6zPrFAugFzKzRHuf855gNNLK50pgdsAASkwHaHLHwo3wJlBw6RvnDRIHgYAwRvkHgIPonL%2Bc3Wrny0YmuElYuASNo4qqyXJTVGUtVrxVVMvZYYOpVS%2BXggdZK5ajCgiPx1dS%2BmobDX/ytbKRN9r/4utntmx5UrjdFtaSPoNWlIVHhEI8CwRITBhQKFEAYI0fbsGqjXECAgBBGDGlQLNJkmB1F0B2kwLwRATsOEy/OJEkIB1DoynFplftBDYPq5IR4r3UDYSengL79iYtxZmpFQ6mFs6o5nPJhB9ZZOTukCjtH%2BPYyLyx1VBAhAFCkDxwTgnNXjQFdShTwRVPUc08WwoDTK4BzEGSzFFtJTCZcGAtLJHwJRvpGSdsAXv4hdxZF3gFL1QLmS52NqYXovyTzhukV8tNYudy5oTFCufOcW6/qnMy8oETeAYt9zs325rdy8A5rouxXOfJYYQbk9d8beHPNxlb3%2Bz7em593bv3DvSMxMDaFVSJvzHaoj5hr3YeLDAcD0ikPlv1W%2B4z8n%2BZ63SyqSm/r8unvjcy6m%2BL4X5eQaV6BG1ivZfa8gkQXnjnNYpvu%2BL1Rn6DessgkhjX3vDALkD7r/3nvo%2BneYTCo3qVHuu933HyCQZI%2Bl8W8X0P5f6%2BBmT6j6uBodO5/x4j6Xk0wJZtr9P%2Bf/7l%2B6cX7P3TnfLMDz76MZ3o/3eb8cLv4t7/lrf8cMfy7j%2BRCHqHHQgGz3yWZAhEwwCh2C%2BBFG2A0G0nWH/ABHAP9yOWgJiVgOZAQK4G0kIgZ03WLgIHQKT2AygIWRwPgO2DMG0hPDs2%2BSJzdFALQIgMwKoMFxoPWG0g0FQMEDIKD32UoJgK4PWAQOlA/C4HmEpxIMELTxEOwLEIQJPG0jMEYPKyf1UkSGIAwA7EiTj3vCoh0KYGAFZBvgMBtA9HCn7Xq0%2B0wAgEYCYAiHoCYjHBIG2HHUShKXCWujwGZDcG2HrD8OsGsHZ1bB0L0IcAMNARKXtUx2BGJRANGUnC1ViKBnb16h1VkNm3JViIWXiN9ESLJRimSIGWMXSOqhn1j1AW%2BVjEiPQH0KyPfwaKaKfmKXJhNw7xAQURyLpzyJPVyOaKplD2IBqN6MEVaOiOGIWR6SWSmIIBiPhSGPaORTqMVBWJ6K70KMkGKMRTPDKKtQOUqML2aPWJ5E2PyMw0uI6NGKLy2Ii03RuK7zmMbn3xy1WJD2eO3CY2%2BJKVeIgAWKWN92%2BPqVkKBJmMwx2L2NEXJSoHqAOgqOKQyJn26NqK11jFZ0hJiWhOJXhNgyRKiRRMH3GOyMmN0MaOmM%2BJKQhOpM6LdzOIxI2MtQGK7yxLpNGNJMeNHFpIeP%2BLiV5NAVAnZIeN%2BJZOxNmhxxhOwRinxM2kJJiWJLa3uPROdxLBFKuJiQ1NuJNxVImKePFOpNmLiRfytQeOFMNNFPONpw4VZPf3fTPUFOWMtNATBMEVJwIDtJKT%2BMJAWW1LJJZlRQog4HmFoE4BPF4D8A4C0FIFQE4DcFCPWgUEWGWCZA2B4FIAIE0BDPmAAGsQATwdMwyOBJBIzszYzOBeAFAQAdMszoyQzSA4BYAkA0Ads6BohyBKBWzEh2yYhiQ%2B0zhno3hDBgAuAuANBWgaBJFsFKAIhyyIhgh6gTROAMzWy2BBAdEY1lz6zSAsBWQjBxAdzRcTtqydyDVJRXtVgMyARKhyyMwIhIpiATQPAsByzAQ8AWAVyGz3RTCFAxQ8BMA1wdFzQoyMz%2BBBARAxB2ApAZBBBdQ1ByzdBWhLDjBEybB7zqzIB5hwpqhTznodF1gqzKh6t0gXARJRgWhSBAhgwnUYhWhcg0gBAKKcgUhGKGApg%2Bg6K2gSKBBOgRhPBmg9A7AeKahhhugaLOKhKxLmLxgxKOKSh%2BhpCFglgVg9BARMAryGziyIzSAoyYy4yOB%2BzBzrZhybQxyfxgR8CIAEzLBrBQJcBCAPD0zQJtsez6AjR0zZheA6ytA7NSB8zCz9BOBSzdLyyDKqyazMzsy/LiyzAyydzwqor6y/KTsxFSLJAgA%3D%3D%3D"&gt;on arm64&lt;/a&gt;… the result is disappointing.
Much like with the compact scalar implementation, the compiler is in fact emitting nice long sequences of vector intrinsics and vector registers… but the loop is still getting unrolled into four repeated blocks of code where only one lane is utilized per unrolled block, as opposed to produce a single block of code where all four lanes are utilized together.
The difference is especially apparent when compared with the hand-vectorized &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGe1wAyeAyYAHI%2BAEaYxBJmAOykAA6oCoRODB7evnrJqY4CQSHhLFExXPG2mPb5DEIETMQEmT5%2BXJXV6XUNBIVhkdGxCQr1jc3ZbcPdvcWlgwCUtqhexMjsHOYAzHhUANRYNCHoEAD6x6oAHABsx5eSp3MmGgCCm8HI3lg7JhtuqiwswQIxGCADoEN9sI8XmYNlVtntMAdMEdTkwGsgELd7lDXgx3l5Pt83A0WMcQgIwRCcTDXNtqVs8R9MF8fiwmAQEJSNtgdl9nlDhsQvA4dgAxABqmGQki%2BcSsz15vK8DHSsosvIA9BqdopmcrVfxiDsCABPRLMxLKlVGHanFjlc47QzoHZUWiodkbMyqO4Eenw/bBZEnM5XG53Y4PBWKmN2h07e1mc7feXQ2G0AOIoMo45olaYiNRp4xmNuj0EL0%2B44EV2VyQp%2Bm0qhQkuKwXCmsmOUt1slsvsnaqBvR3uK/s1k3D4ujsfugcALynM9n5Z2AHcl72uwARTet8d7dlMEwAVgs9ZPu42qZjO6nPd5Eqlkggcx2IEHEA0II0VAWOxNL8fz/UgdnnIDf3/NcIL/NU735GFMyRHMLmuLFIwfMVJWlENE0dKhJDfD88OoQi4LiXcEPTJDsxDPMMXQosYyfHDxzratXTIj8qDrUi3y7KwKMbBh8GbfkRyw59qDnGtVGOUCDxNeTXRksDlIPNdI3fT85P/QClP/cD50jUDoM0/juyEiSWJfA85IU1SlIc1djKInSTIAiADNAoyPOg79IPIyiXgkg9UHNYh2RIU8rEvCBAR2PA3zQBhhjVHZiEwAhlgYQ96hivBTyvdV4OnXlx3MS4dnC6IouIGKiviwREos9VMuy4hcvQI8CqKlNZWCzCbOqiK6usCAUrSmzKp2CJWoyrKcsknDVC%2BSxZpBVRQJNNb1QiEETR83aNvnUzjv2tcHmvAahuwmUasiohiAAWgmgQpruma5vS9qlpsiBVuejatoAnYgf2w6wLBk6zvBkFLv60rmLukbaqegAqN7UprCqzCq76BIWjrcv%2B1b0dm7adnJiIjups7qaukqrLK5b7tGp6NSxtLcfx%2Bbfs61mAZ2bUadBkWjvF9dhdmxmbv5ITrxxZ5ggzEJWdRx6SE5yacZkmamFAnXWa%2B1rMP5km7ogJhpf2kHrZFg7QPtmGnRt%2BHGZxZmBSBDsdgASRRgTMP1AQ4IkmN2xFIPw97BKh0VmPWwSycE5Z0cEsXVPl15BKNyzrcFZvWPmu6/KzwvYrMLvVPMIDqS3NUL89KbnyW/XJugtry24%2BU5Pe%2Ba4zQNzrSP0b3Ttq8jzfKgiBzM7iS65wnuh%2BapzEoHkf3Ob7ywIgVzTI7oOvYkhKHrGs9GoSpKdiNwnzby48z0Ky9EeZmNARms%2BnoauKr75xaBal0fhYZ%2BxU5YvELkrJ4kcawWAsKgVa0cWYh1ykg0cB40CdWiAoGKlw%2BrXV7FqdeWBVDIidAoL4Z4AQMAABoJmCAATXoQwAAWgmJgqg6FslUEw7hLC%2BqJxUquTBIRiAKCeLQP0Z4zB9TPBsfB6phbamCCQshTAKEiOiDFahAB5YgABZDhsiLA0IYfwl%2BElq6pkwnAhBXMazTTxswjwWCyA33eg4z6TjuEuNEabQRmixESKkRYDQxiwmXhZNuV01CIDUN8dETaoEfEkFEZtD2ATUnYOCTFCJu4zxcCKlEmJwQ4nBAScQR27DVAVIOhktOipAniMkbk4xMjInfGiVQWJ8SsmVNOtU2p856mjiaTkgp4SimdJiRwspGQ%2BlJMGQs1QIzexjJaRMl%2BmzonTO6bM3priqkpMOSaVZrZ1khMKVsiw7SdkbC6dwuZtSQbHLSSsvc4DbrPmcX0187jsY/QARbKSTTWmXkNn0nB2yIWuKhTcoqstSpfOlEs1xfzb5yiJn9S2oK5FFRhaIuFFcCXYJiieBFr9BqQPEtAn2IpbE%2BjDsglUoc0G9mGriiweCX4EOzkQkgWAYjMJockxhorWHJI4SK6pDDJWqH4YIjBkLRR62kcY%2BR1yK79SUcQzApCXTqPca47RwQ9GGKHNc0x5j8kWAiBEBBMwtEWIaeVVSTSVXlnGVy4xWr868isVA9%2BDBVbMjjEmcmBzRFCCENgP5d8gVrUuJyvJII8KUswirIMtpjh4QjRwip0bY3/2JomzlGrtypodOmmlrxdiBkOHRdEBZsQn2DVmti3pfQRvKX00ImABBxsxffSqyaiogh4p26tLNM1qw7VWAgeaam9v7QwQdbUE0jshTFct466xTs2E2QNioZ3MgAG6oDwC6JQBAGVNRrPa1QxQIUAscVVSN0Rn0fWfDNV50R/EuqNYSj17IwU2ryTFB9xQpn3JKau99lSXn5uWWcks7rVWhOMVcm1kGfDQYeaU%2BDRykMnJQzGND5ZQMxVuRBh1uGOkwe6QRnthyBm/v6aRxpyr0NYco2eHDLA8MzMboRxDS7DnvL9ahrjFHtkxR43x2jAn6MPP2cxtJkM2N1I%2BWR6TIHZNquufxwTezhNqcSaJ55KGkUSQZS%2BI2DL9aftgfAocTjRb2ZczNZATmdgOacegf9o5r23o0E7VNZniCvjC2x18HHeTBZcxANoG14NRZS8R0RsXtNtiyreswhtwvzLRf%2BZAqaMvRCy5JiOuXEsbFAugQrFS0sNZi3MKzXtqUhVpUKEUAAlJgO02WPhRvgTKDh0gfOGiQPAwBggfIPAQfRc3JMLfNfeCS/XAJG1fXsPAY2ajKW214qq03ZsMHUqpRbwQLurkWxwjCgiPyjalDUCAz3xsCA8qd0p33zv/iu6ugHHk7uNxB1pI%2BVLK40qIYWnYp7sGqlQLsDkzI0AsESEwEUAB1OgGZWAUKyk6WgIIdhmA0BoE8iV0f0DYIIdkE3njnsvRlAbDK/aCGwS9yQhb7E7E2zNSKh1MLZ2zh5hB9Y3MPukMLkXM5F4S6qggQgChSAy9l%2By47xolupVV4I9XyNv1OJBwoQLiphrEFGycf4xxr1cGOIkBQSXgJvm1ILkE72aiVqTChjU5MvBKFtNbhQCAvBUDdJge3Kv1xK4xMaSKqUDAEEwBQogrtUhGHoOvGBqo1xK8zzm0ktvI/r3PbQeHFDjyJ30To8U0bjrW2D6H8PVMNTIplBbvbNCreF5D2H%2BgkeIAd8qXhUCQ%2BvfnFAscfR%2BjjhCAABJyFFKKAIsbQs7DXxvtrKHzejYYd3m3vfw8D7HyPjKo3x%2BT%2Bn7PhfS%2BV9JdAslx/W/JsoyHyw/fje%2B8R4d4P8/p%2BT8OiX4z7z6L7L6xr5ak6gQQFmDP6SZTbAhnZd4F4H5N794/5u6/YX4s4mggiYGn5T7AE35gFfigQb6kGwFFzDbfK/Z77IGf5H7oEDa4EIGgj/5MF4GAG2hX4gG36xqP4P4P4UFt7VQsGsIf6H5oGO4YGiFYHSEzasGcEEHX6gF37QFQFQFCE0oxjy47AhANB3q/57abQsiQgwYBS7DfCijr7aQbD/iAiGGVKDbcjTLmEshWFcDaSES64AbFwEAOEgjzgmEuHARuGk7aQnhtYfI6FUD6H2Fj6ILOFmEhGWE7AbDaQaB2GCD%2BFOGmHRKuEpEygfhcALBq6%2BH%2BGBFUhJG/ihGU4fgwH1KYQA4ULTL/TIHcID6lFtH3Y/50FZR26ME4EA7/gQB2ri4gicp6EIZjp4TQy/Y0Lj5vjkxD7zF4QlGCJdFyQ/4jFS7jFbpniTEHTTHxhAw0ELFUxn57YMIX6lHZzbFjETGYANABFHFJizGiEsJnFLGjYfGrGRGSbG7FKtHW7UIdHrHAnXY9FB59HH5MEg7DGjE%2Bi7GwoxQxFTGXjj5vHyG0KfEXHEArEOhrEAZtEQmO53GImcqomHHokzEnGiFXGrHnFD70mAE3HLhkmSBImEoolPGLjUnHEiFYk/EEmMnfELF/HWKto1hK6dgwZQAeFEhEg7BkRAzFGJqB6kgsCoDw5sgKAADWA%2ByByA1O3%2BjuTRWBxuYpHy0pcKeSxS9qngEA0papSWLIbgipTc4pmE1pcmgm9ptAjphAzp8pPw7pxRnpEk3pBmdy0SfpAZnYTiLpCpPwOwEA9RKGkZFg5adpqADpTpM0iZIZyZEAth4ZECwUHACwtAnAJ4vAfgHAWgpAqAnAbplg1gOwCgSwKwzImwPApABAmgFZCwupIAJ4oWVZHAkgtZA5jZnAvACgIAoW/Z9ZFZpAcAsASAaOiQdAH6FAb06O25MQ7whgwAXAXAGgbQNAki2ClAEQ05EQwQDQJonAvZaOtOBAOiwaT5y5pAWAbIGeawDZHueA5e05eqUoXgSez5vAgIVQ05GYEQkUxAJoHgWA05QIeALAUFCwboTAwACg4oeAmAa4Oi5odZvZ/AggIgYg7AUgMggguoag05ugbQBgRgKA1g1g%2BgeAEQ85kACw4UNQ85HAz0OiGwc5VQL26QLgIkYwrQpAgQQYjqZQSQKQaQAgslOQqlNQ0w/QyldgklAgXQowngLQeg%2BlH2tQIwPQilulZlVlGlEwVlOlJQAwxRiwywqwegQImAawPAlZ1ZU535TZHAqEz0twN8rFwAOwp5P4IIHhEALZVglgoEuAhAJAa0GwyWHg%2B59ARoPZcwvAS5WgbWpAw5o5%2BgnAk5pAdZDZwVc5C5fZA5JV45ZggVtVs5jVy5JV8OYiUlkgQAA%3D"&gt;SSE compiled output&lt;/a&gt; and the hand-vectorized &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGe1wAyeAyYAHI%2BAEaYxCAAnGakAA6oCoRODB7evnrJqY4CQSHhLFEx8baY9vkMQgRMxASZPn5cFVXptfUEhWGR0XEJCnUNTdmtQ109xaUDAJS2qF7EyOwc5gDMeFQA1Fg0IegQAPpHqgAcAGxHF5InsyYaAIIbwcjeWNsm626qLCzBBGIwQAdAgvtgHs8zOtKltdph9phDicmPVkAgbndIS8GG8vB8vm56iwjiEBKDwdjoa4tlTNrj3phPt8WEwCAgKetsNtPk9IUNiF4HNsAGIANUwyEknwA7FYnjyeV4GOlZRYeQB6DXbRRM5Wq/jEbYEACeiSZiWVKqM2xOLC4ZjO20M6G2VFoqDZ6zMqluBDpcL2wSRx1Ol2utyO9wVitjdodTvtjq%2B8qhMNogYRweRR1Rywxkejj1jsfdnoI3t9RwIbsrkhTdJpVEhJcVAqFNZMcpbrZLZbZ21UDZjvcV/ZrJuHxdHY49A4AXlOZ7Py9sAO5L3tdgAim9b492bKYJgArBZ6yfd%2BtU7Gd1OezzxZLJBBZtsQIOIBpgRoqPNtiaX4/n%2BpDbPOQG/v%2Ba4QX%2Bap3ny0KZoiObnFcmJRg%2BooSlKoZJk6VCSG%2BH54dQhFwTKu4IemSHZqGebouhRaxk%2BOHjnW1ZumRH5UHWpFvl2VgUY2DD4M2fIjlhz7UHONaqEcoEHia8lujJYHKQea5Ru%2Bn5yf%2BgFKf%2B4HzlGoHQZp/HdkJEksS%2BB5yQpqlKQ5q7GUROkmQBEAGaBRkedB36QeRlHPBJB6oOaxBsiQp5WJeEAAtseBvmgDBDGq2zEJgBBLAwh51DFeCnle6rwdOPLjuYFzbOF0RRcQMVFfFgiJRZ6qZdlxC5egR4FUVKaysFmE2dVEV1dYEApWlNmVdsEStRlWU5ZJOGqJ8lizcCqigSaa3qhEwImj5u0bfOpnHfta73NeA1Ddh0o1ZFRDEAAtBNAhTXdM1zel7VLTZECrc9G1bQB2xA/th1gWDJ1neDwKXf1pXMXdI21U9ABUb2pTWFVmFV30CQtHW5f9q3o7N23bOTERHdTZ3U1dJVWWVy33aNT0aljaW4/j82/Z1rMA9s2o06DItHeL67C7NjM3XyQnXtiTzBBmISs6jj0kJzk04zJM1MKBOus19rWYfzJN3RATDS/tIPWyLB2gfbMPOjb8OM9izP8oCHbbAAkijAmYfqAhwRJsbtsKQfh72CVDorMetglk4Jyzo4JYuqfLjyCUblnW4KzesfNd1%2BVnhexWYXeqeYQHUluaoX56U3Pkt%2BuTdBbXltx8pye981xmgbnWkfo3unbV5Hm%2BVBEDmZ3El1zhPdD81TmJQPI/uc33lgRArmmR3QdexJCUPWNZ6NQlSXbEbhPm3lx5noVl6I8zsYAjNZ9PQ1cVX3zi0C1Lo/Cwz9ipy2eIXJWjxI41gsBYVAq1o4sxDrlJBo4DxoE6tEBQMULh9Wur2LU68sCqCRM6BQnwzz/AYAADW2NQgAmvQ4IAAtehTBVB0NZKoJh3CWF9UTipVcmCQjEAUI8Wg/ozxmD6medY%2BD1TC21MEEhZCmAUJEdEGK1CADyxAACyHDZEWBoQw/hL8JLV1TJhOBCCuY1mmnjZhGQSCiMNu9Bxn0nHcI8Fg4gptBGaLERIqRFgNDGPCZeZk243TUIgNQ3xojNqgR8a46Im0PaBLScEyRMVIm7jPFwIq0TYnBHicERJ6TIapL8QdTJadFRBPEbks8%2BSYoyKiV8GJVA4kJOycCU67DVCVOIAM%2Bpo4mkhJikUl%2BrTildNiRw8pLjakgxqUk1Q4zeyTJaRYGZBS9nzPWN07hyyRmOyGeck0WzWw7NCfs9pRyTlLL6aslJHDzmbL3OA26z5nEjNfDfDxP0AEWykk0vJRV3F%2BJwYUqFQKYWPMvLLUqvypSXOyYC2%2BcoiZ/UthCuR8KCXniJdk2FFgTxFRRV7SB4loE%2B2FLY30YdkEqlDmg3sw1iV4JfgQ7ORCSBYBiM4mhKTggMLFQwFh7zOEyolUM/hgiMFkpFHraRxj5GzJJbyxRRCVGYFIa6dRCLRHaOCHowxQ4tWmPMQciIEQEHTC0RYhp5VVJNNVeWKZZ4eUHIrt8qxUCXg7CDAcOiaICxYhPgwVWTI2I%2Bj9OTV5ojQiYAEICu%2BoK1oXGJfk4EPEE2v2Cu/GNwYhFegTdWJNHzsmpvTf/Ym2biWau3PmusRbhKiSgSW2N2wABuqA8CuiUAQJlTUawOtUMUdx2NjbeIqdkmdH1nwzXWdEAJrqTXYM9WySFWq2lnkncUJ5pSGBnP6Wsmtqybklg9WqsJxiHmHsdT4E9PSynJqqTKq5N7Yx3vLHug5HS7UvpYG%2B3pC7amDLXaM%2Bcv7GkqvvU%2Bh9Wqj2vs6ccxZjdP2jMvcMi98GeT/t3XCrVyG0NgYw887DkGknVKvXRwjW6xE7vucY4DMUKPgZebR9JeHPk3tRRJJlL4jZMv1ku2B8ChxONFmJ6TM1kCSe2OJpx6AN2jhHWOjQTtgQ4dfLpmDr4mNaekxAVoG19P/n2kZ2YJmspjoSDfPTvHiAGec7Z%2Bzo6zPrFAugFzKzRHuf855gNNLK50pgdsAASkwHaHLHwo3wJlBw6RvnDRIHgYAwRvkHgIPonL%2Bc3Wrny0YmuElYuASNo4qqyXJTVGUtVrxVVMvZYYOpVS%2BXggdZK5ajCgiPx1dS%2BmobDX/ytbKRN9r/4utntmx5UrjdFtaSPoNWlIVHgqzLV4AE3oOJkhcSwRI9RMCPBEoYhQABrex5aKyVv9E4g2QLZ3xqrA93mLLYw7cEOxGsaAjvRcwAoOQCUFl9reJgAAjkcAtVtQJzW%2BRMRwyBntpV2/d7YCgEBbE7JhwmOntgWac%2BsG6RXtjfbu2940R2Sl9qx8QaH7WvDejegDoHIPBCgXWFwG998%2B1MHQOgPt0PmdmAgHThAtARcs4IEd0CWOcd2fvMfR4RC625T7dg1UqAdjsiZP947woADqdAMysAoVlZ0tBgTbDMBoDQJ5EpHfoGwQQbI0tPAHUOjKcWmV%2B0ENg%2Brkh1c3cqzNSKh1MLZ2j/JhB9ZZOTukFH6Py5F7x6qtjggChSDJ5T6OGrxoCupRz4IvPJYC%2BLYUBpxUw1iDJfF%2BgLwiRGcw5Z1wYCb5tQR%2BBKN9IbaE03o1OTVIuImShGwDo0IuxUBA4YGADgNYEBME167EfwB6DPWCDA1UUOdv8/oM1Igto/hHCx14Kg7pMBHESNn9cTJtBeDSnUS7mBc968xx6NcGVFgECZAgaITILo2wc4roAIqA2wmATA6It2v2VMGoaK0odeeAxANCDeTeLesOSBuGSuZOteyWDCaBzepIreYuWBdSN6eByBLChBGBLOZBcGFBKMU2qBfajeRB7WmBcWwIU2GSjBfyU2BBrB6BxBnBJo3BQIbW5B6WTBEhrCNBIhdBXBPBDB94C8KMIQ9Q46EAZBiCXICyAUOwXwIo2wGg2k6w/4AI2hyWB0zIEImGBhzIxhXA2khEJem6xcBAVhyBAyth%2BhwEjhtu2kJ4OBRcPIaebomhlhOhvh9h/hRh2wJOH4GgFhggXhoy8WehsRv4AR0oH4PObheeUR1h84MRMSDh8RjuH4ZgIR3aios2FCXSuegsfa3C0OsOLRHCbRLOQh7BJB2hXBs28wTRZe2cEA9qcewIxKGhuGRU/ezKQMzBcxZE5MWBNCSxQxpeZeHRqgXRYuYxiekxZKMU0xB0sxBa8x1UshDADC6xVMGU%2BBSxBRIxzx%2BxExUxkBsGZxdY0MU2LCtxKxyWfx5xhENREkleJS/0LRwQux4u1CMJPRtBpBAxlqGx7hzx0erxvohxiKZ4VA9Qm0XxCaPxVxaxwJb4AJyBpJdYqJ6Jy4UJDAMJmJkg2Joi5KeJ6RhJFxAh/x9xyBNxwJTxtJLx4xWJxK7JAynJ0oCxVxQJ1JdxWBspA%2BoJG2X26OHEmeJSB2HgR2J2Z26AF2129RjxheHCCg6x3yme5K%2BSJSDqngEAGpM05mzIbghI2wTcypsYlp0yJ6tptA9phA2abpzhhIrp5mdmN6Xp6qVGs0qAdpDpTiTpIZ3wbp1RHpiokZFgLaNpsZfp8ZVUiZ3woZ5hypO4HA8wtAnAJ4vAfgHAWgpAqAnALplg1gmOiwywTIGwPApABAmgZZ8wl2IAJ4OmFZHAkg1ZvZ9ZnAvACgIAOmPZtZZZpAcAsASABudA0Q5AlAa59AMQxIfaZwz0bwhgwAXAXAGgrQNAki2ClAEQE5EQwQ9QJonAXZ/2ruBAOiMaT5C5pAWArIRg4g35vemuM535BqkoXgv%2Bz5vAAIlQE5GYEQkUxAJoHgWAE5gIeALAUF8w7oTAwACgYoeAmAa4Oi5oNZXZ/AggIgYg7AUgMggguoagE5ugrQBgRgKA1g1g%2BgeAEQM5kA8w4U1QIFz0Oi6w05lQ9W6QLgIkowLQpAgQwYTqMQrQuQaQAgMlOQKQqlDAUwfQSlbQElAgnQIwngzQegdgBlNQww3QClulZlVl6l4wVlOlJQ/Q%2BRCgbZKweggImAqwPA5ZlZ4535DZHAe5B51sR5Nop5P4wIzhEATZVglgoEuAhAJAa03OoE2piQ65RonZswvA85WgdmpAA5Q5%2BgnAY5pANZdZwV05s53ZvZRVI5ZggV1VU59VC5RVmuYiklkgQAA%3D"&gt;Neon compiled output&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the results of running the auto-vectorized implementation above, compared with the reference compact scalar implementation:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2 Speedup:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Compact:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.5159 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;41.8187 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;81.0942 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Autovectorize:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;34.1398 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.3069x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;38.1917 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0950x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;59.9757 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.3521x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;While the auto-vectorized version certainly is faster than the reference compact scalar implementation, the speedup is far from the 3x to 4x that we’d expect from well vectorized code that was properly utilizing each processor’s vector hardware.
On arm64, the speed boost from auto-vectorization is almost nothing.&lt;/p&gt;

&lt;p&gt;So what is going on here?
Why is compiler failing so badly at auto-vectorizing code that has been explicitly written to be easily vectorizable?
The answer is that the compiler is in fact producing vectorized code, but since the compiler doesn’t have a more complete understanding of what the code is actually trying to do, the compiler can’t set up the data appropriately to really be able to take advantage of vectorization.
Therein lies what is, in my opinion, one of the biggest current drawbacks of relying on auto-vectorization: there is only so much the compiler can do without a higher, more complex understanding of what the program is trying to do overall.
Without that higher level understanding, the compiler can only do so much, and understanding how to work around the compiler’s limitations requires a deep understanding of how the auto-vectorizer is implemented internally.
Structuring code to auto-vectorize well also requires thinking ahead to what the vectorized output assembly should be, which is not too far from just writing the code using vector intrinsics to begin with.
At least to me, if achieving maximum possible performance is a goal, then all of the above actually amounts to &lt;em&gt;more&lt;/em&gt; complexity than just directly writing using vector intrinsics.
However, that isn’t to say that auto-vectorization is completely useless- we still did get a bit of a performance boost!
I think that auto-vectorization is definitely better than nothing, and when it does work, it works well.
But, I also think that auto-vectorization is not a magic bullet perfect solution to writing vectorized code, and when hand-vectorizing is an option, a well-written hand-vectorized implementation has a strong chance of outperforming auto-vectorization.&lt;/p&gt;

&lt;div id="2021-09-07-ispc-implementation"&gt;&lt;/div&gt;
&lt;h2 id="ispc-implementation"&gt;ISPC Implementation&lt;/h2&gt;

&lt;p&gt;Another option exists for writing portable vectorized code without having to directly use vector intrinsics: &lt;a href="https://ispc.github.io/"&gt;ISPC&lt;/a&gt;, which stands for “Intel SPMD Program Compiler”.
The ISPC project was started and initially developed by Matt Pharr after he realized that the reason auto-vectorization tends to work so poorly in practice is because &lt;em&gt;auto-vectorization is not a programming model&lt;/em&gt; &lt;a href="https://pharr.org/matt/blog/2018/04/30/ispc-all"&gt;[Pharr 2018]&lt;/a&gt;.
A programming model both allows programmers to better understand what guarantees the underlying hardware execution model can provide, and also provides better affordances for compilers to rely on for generating assembly code.
ISPC utilizes a programming model known as &lt;a href="https://en.wikipedia.org/wiki/SPMD"&gt;SPMD&lt;/a&gt;, or single-program-multiple-data.
The SPMD programming model is generally very similar to the &lt;a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_threads"&gt;SIMT&lt;/a&gt; programming model used on GPUs (in many ways, SPMD can be viewed as a generalization of SIMT): programs are written as a serial program operating over a single data element, and then the serial program is run in a massively parallel fashion over many different data elements.
In other words, the parallelism in a SPMD program is implicit, but unlike in auto-vectorization, the implicit parallelism is also a &lt;em&gt;fundamental&lt;/em&gt; component of the programming model.&lt;/p&gt;

&lt;p&gt;Mapping to SIMD hardware, writing a program using a SPMD model means that the serial program is written for a single SIMD lane, and the compiler is responsible for multiplexing the serial program across multiple lanes &lt;a href="https://doi.org/10.1109/InPar.2012.6339601"&gt;[Pharr and Mark 2012]&lt;/a&gt;.
The difference between SPMD-on-SIMD and auto-vectorization is that with SPMD-on-SIMD, the compiler can know much more and rely on much harder guarantees about how the program wants to be run, as enforced by the programming model itself.
ISPC compiles a special variant of the C programming language that has been extended with some vectorization-specific native types and control flow capabilities.
Compared to writing code using vector intrinsics, ISPC programs look a lot more like normal scalar C code, and often can even be compiled as normal scalar C code with little to no modification.
Since the actual transformation to vector assembly is up to the compiler, and since ISPC utilizes LLVM under the hood, programs written for ISPC can be written just once and then compiled to many different LLVM-supported backend targets such as SSE, AVX, Neon, and even CUDA.&lt;/p&gt;

&lt;p&gt;Actually writing an ISPC program is, in my opinion, very straightforward; since the language is just C with some additional builtin types and keywords, if you already know how to program in C, you already know most of ISPC.
ISPC provides vector versions of all of the basic types like &lt;code class="language-plaintext highlighter-rouge"&gt;float&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;int&lt;/code&gt;; for example, ISPC’s &lt;code class="language-plaintext highlighter-rouge"&gt;float&amp;lt;4&amp;gt;&lt;/code&gt; in memory corresponds exactly to the &lt;code class="language-plaintext highlighter-rouge"&gt;FVec4&lt;/code&gt; struct we defined earlier for our test program.
ISPC also adds qualifier keywords like &lt;code class="language-plaintext highlighter-rouge"&gt;uniform&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;varying&lt;/code&gt; that act as optimization hints for the compiler by providing the compiler with guarantees about how memory is used; if you’ve programmed in GLSL or a similar GPU shading language before, you already know how these qualifiers work.
There are a variety of other small extensions and differences, all of which are well covered by the &lt;a href="https://ispc.github.io/ispc.html"&gt;ISPC User’s Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The most important extension that ISPC adds to C is the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; control flow construct.
Normal loops are still written using &lt;code class="language-plaintext highlighter-rouge"&gt;for&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;while&lt;/code&gt;, but the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop is really how parallel computation is specified in ISPC.
The inside of a &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop describes what happens on one SIMD lane, and the iterations of the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop are what get multiplexed onto different SIMD lanes by the compiler.
In other words, the contents of the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop is roughly analogous to the contents of a GPU shader, and the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop statement itself is roughly analogous to a kernel launch in the GPU world.&lt;/p&gt;

&lt;p&gt;Knowing all of the above, here’s how I implemented the 4-wide ray-box intersection test as an ISPC program.
Note how the actual intersection testing happens in the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop; everything before that is setup:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;typedef float&amp;lt;3&amp;gt; float3;

export void rayBBoxIntersect4ISPC(const uniform float rayDirection[3],
                                const uniform float rayOrigin[3],
                                const uniform float rayTMin,
                                const uniform float rayTMax,
                                const uniform float bbox4corners[6][4],
                                uniform float tMins[4],
                                uniform float tMaxs[4],
                                uniform int hits[4]) {
    uniform float3 rdir = { 1.0f / rayDirection[0], 1.0f / rayDirection[1],
                            1.0f / rayDirection[2] };

    uniform int near[3] = { 3, 4, 5 };
    if (rdir.x &amp;gt;= 0.0f) {
        near[0] = 0;
    }
    if (rdir.y &amp;gt;= 0.0f) {
        near[1] = 1;
    }
    if (rdir.z &amp;gt;= 0.0f) {
        near[2] = 2;
    }

    uniform int far[3] = { 0, 1, 2 };
    if (rdir.x &amp;gt;= 0.0f) {
        far[0] = 3;
    }
    if (rdir.y &amp;gt;= 0.0f) {
        far[1] = 4;
    }
    if (rdir.z &amp;gt;= 0.0f) {
        far[2] = 5;
    }

    foreach (i = 0...4) {
        tMins[i] = max(max(rayTMin, (bbox4corners[near[0]][i] - rayOrigin[0]) * rdir.x),
                    max((bbox4corners[near[1]][i] - rayOrigin[1]) * rdir.y,
                        (bbox4corners[near[2]][i] - rayOrigin[2]) * rdir.z));
        tMaxs[i] = min(min(rayTMax, (bbox4corners[far[0]][i] - rayOrigin[0]) * rdir.x),
                    min((bbox4corners[far[1]][i] - rayOrigin[1]) * rdir.y,
                        (bbox4corners[far[2]][i] - rayOrigin[2]) * rdir.z));
        hits[i] = tMins[i] &amp;lt;= tMaxs[i];
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 10: ISPC implementation of the compact Williams et al. 2005 implementation.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;In order to call the ISPC function from our main C++ test program, we need to define a wrapper function on the C++ side of things.
When an ISPC program is compiled, ISPC automatically generates a corresponding header file named using the name of the ISPC program appended with “_ispc.h”.
This automatically generated header can be included by the C++ test program.
Using ISPC through CMake 3.19 or newer, ISPC programs can be added to any normal C/C++ project, and the automatically generated ISPC headers can be included like any other header and will be placed into the correct place by CMake.&lt;/p&gt;

&lt;p&gt;Since ISPC is a separate language and since ISPC code has to be compiled as a separate object from our main C++ code, we can’t pass the various structs we’ve defined directly into the ISPC function.
Instead, we need a simple wrapper function that extracts pointers to the underlying basic data types from our custom structs, and passes those pointers to the ISPC function:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;void rayBBoxIntersect4ISPC(const Ray&amp;amp; ray,
                        const BBox4&amp;amp; bbox4,
                        IVec4&amp;amp; hits,
                        FVec4&amp;amp; tMins,
                        FVec4&amp;amp; tMaxs) {
    ispc::rayBBoxIntersect4ISPC(ray.direction.data, ray.origin.data, ray.tMin, ray.tMax,
                                bbox4.cornersFloatAlt, tMins.data, tMaxs.data, hits.data);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 11: Wrapper function to call the ISPC implementation from C++.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Looking at the assembly output from ISPC &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1B4FAB2Skl9ZATwDKjdAGFUtAK4sGIAGylHAGTwGTAA5DwAjTGIQSQBWUlNUBUI7Bhd3Tx8EpJSBQOCwlkjouMtMa1sBIQImYgJ0jy9fK0wbVOragnzQiKiY%2BIUauobM5qGuoJ6ivriASktUN2Jkdg4AagIAT1NMLCo1qlpUJgIAUgBmJ3OL7AOjk%2BvzgCFTjQBBV7e177XMVUS6msAG6oPDoNbEJibJ5PVCqACSgiiShsknhQmUTggaAYgzWbgYeH4xBYd2OBAhUIAInhiK1KgxTrEntdYlTSJ8flzuTzeXyfji8QSiSRSYdyZTNgB5Yh4YBBJkspnszn8tXq7mCinC4li%2B4UyGbAAqAFkghz3hqreqtfjCbqySdJaamKoLV9rZ6ebadaLHRTwuE4ZI0MRgsQFIrvMrFZJle6vYmfr6Sf6NmbcbH46qk16U3qJQQTa7I8y42yE7m8/a/UEKQhCKWnuWqbM1qcAOwvS28/P%2B84Q/DEdvnKntrtrLgAOg0%2BwA9JKaXS2gJFRp45OZ/PF7T6alFVxsz2q2rp7O1gvDUu96vmQAmZXjqkXbsfY/cvt1tbBWqK1lji4AInc5SDWaQ1liJ8XxzbkiTWCBiCHKdVBHbBALWDQtzbTtXw1H9iDXR90PXZ4YK5Ttn3fLk4IQpDNlQ4isPHXD1Xwg8iNHSdoKon4KLIn4aMQ2kpwALwYzjMNnbCu34nk2PvDixwfUieO%2BPj3lku0RVTL8qF/Zl/xHICngw0CuFAu8oJUj0eUEpCUJuRipOYzSuT0gjmXXNkjLWR4WJ5dSbNg/ZaOE%2BjHIkpicNcn53PY7z0Ljay%2BUCvk7OEsSIrHSSqGk/z%2BTihSEs42JuKC8iO0ot9ytikhMCYZAEHgvAfMwqcp0kPKYu%2BIsgibPBFLWFhXQgYbVAQqFTXNeDA2DUNwybeSni8582UVAbvIAWklGU5QVTzlTbAAqQdhNUeZuqtMaoFm1QQxIBbFSWw81tep4NrHbbDV2%2BVGWZF7WzWE6hOIKdNkrE9uQgW77rDZEnvqjyngfV7Vvex8vqhH79uRw6gdO0GRNmWYyo1IsS3WwaWCCUaacNF03RmoM7vm%2BHmUK5aYzej61kx6VZV%2Bwi2WOgnkIu1TE2phgbuZ2HHvZ/SngBrn0a2naBZxgGRZBsGIchn5odl1mI0VDmUdWtGeb57G/tx4X8Z1omSeS9UGwIfrBt6zNmR5i4nHQ8nVA9taXYCyqYIojh5loThYl4LwOC0UhUE4ExzDWBRFmWTB2zvc4eFIAhNCj%2BYAGsQFiDR9E4SR4%2BL5POF4BQQCrovE6j0g4FgJA0BYUw6CichKF7/v6GiNPkC4LhvHMmhaAIZFKHCevwiCWpNk4Ave7YQQpQYWgN/b0gsGGoxxCPoc9yBTBm6Pv5WjcBfN94Otynr2g8HCSFiE2FwsHrggsoWDP3mIcJgwAFAADU8CYAAO5Sh2AnAu/BBAiDEOwKQMhBCKBUOoI%2BuhzIGCMCACe%2BhP7N0gPMVApgGS302lKAcm1NpDGAJgM4o4FBKFrmUCoqQHAMGcK4RoegAiTEKMUPQiRkgMhGF4cyUjcgMG6OIvo5kWgrgYB0YYQjMhqPKDeTR4xlG9GiGo8Ysi9CDE6MY6Ypj5iZyWCsPQgDMCrB4NHWOdcj4pw4BPTc08pxcGamYCwaxcCEBILnfOoEXB9wHsOU4ecuCzF4G3LQxNSDl0rtXDg3CE5Jx8U3Fuhdi4ZJjhwO8XiCmNxKe3DJ18Ix8MkEAA%3D"&gt;for x86-64 SSE4&lt;/a&gt; and &lt;a href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1B4FAB2Skl9ZATwDKjdAGFUtAK4sGIAGylHAGTwGTAA5DwAjTGIQSQBWUlNUBUI7Bhd3Tx8EpJSBQOCwlkjouMtMa1sBIQImYgJ0jy9fK0wbVOragnzQiKiY%2BIUauobM5qGuoJ6ivriASktUN2Jkdg4AagIAT1NMLCo1qlpUJgIAUgBmJ3OL7AOjk%2BvzgCFTjQBBV7e177XMVUS6msAG6oPDoNbEJibJ5PVCqACSgiiShsknhQmUTggaAYgzWbgYeH4xBYd2OBAhUIAInhiK1KgxTrEntdYlTSJ8flzuTzeXyfji8QSiSRSYdyZTNgB5Yh4YBBJkspnszn8tXq7mCinC4li%2B4UyGbAAqAFkghz3hqreqtfjCbqySdJaamKoLV9rZ6ebadaLHRTwuE4ZI0MRgsQFIrvMrFZJle6vYmfr6Sf6NmbcbH46qk16U3qJQQTa7I8y42yE7m8/a/UEKQhCKWnuWqbM1qcAOwvS28/P%2B84Q/DEdvnKntrtrLgAOg0%2BwA9JKaXS2gJFRp45OZ/PF7T6alFVxsz2q2rp7O1gvDUu96vmQAmZXjqkXbsfY/cvt1tbBWqK1lji4AInc5SDWaQ1liJ8XxzbkiTWCBiCHKdVBHbBALWDQtzbTtXw1H9iDXR90PXZ4YK5Ttn3fLk4IQpDNlQ4isPHXD1Xwg8iNHSdoKon4KLIn4aMQ2kpwALwYzjMNnbCu34nk2PvDixwfUieO%2BPj3lku0RVTL8qF/Zl/xHICngw0CuFAu8oJUj0eUEpCUJuRipOYzSuT0gjmXXNkjLWR4WJ5dSbNg/ZaOE%2BjHIkpicNcn53PY7z0Ljay%2BUCvk7OEsSIrHSSqGk/z%2BTihSEs42JuKC8iO0ot9ytikhMCYZAEHgvAfMwqcp0kPKYu%2BIsgibPBFLWFhXQgYbVAQqFTXNeDA2DUNwybeSni8582UVAbvIAWklGU5QVTzlTbAAqQdhNUeZuqtMaoFm1QQxIBbFSWw81tep4NrHbbDV2%2BVGWZF7WzWE6hOIKdNkrE9uQgW77rDZEnvqjyngfV7Vvex8vqhH79uRw6gdO0GRNmWYyo1IsS3WwaWCCUaacNF03RmoM7vm%2BHmUK5aYzej61kx6VZV%2Bwi2WOgnkIu1TE2phgbuZ2HHvZ/SngBrn0a2naBZxgGRZBsGIchn5odl1mI0VDmUdWtGeb57G/tx4X8Z1omSeS9UGwIfrBt6zNmR5i4nHQ8nVA9taXYCyqYIojh5loThYl4LwOC0UhUE4ExzDWBRFmWTB2zvc4eFIAhNCj%2BYAGsQFiDR9E4SR4%2BL5POF4BQQCrovE6j0g4FgJA0BYUw6CichKF7/v6GiNPkC4LhvHMmhaAIZFKHCevwiCWpNk4Ave7YQQpQYWgN/b0gsGGoxxCPoc9yBTBm6Pv5WjcBfN94Otynr2g8HCSFiE2FwsHrggsoWDP3mIcJgwAFAADU8CYAAO5Sh2AnAu/BBAiDEOwKQMhBCKBUOoI%2BuhzIGCMCACe%2BhP7N0gPMVApgGS302lKAcm1NpDGAJgM4o5ggCCbuUG8XgICOBGF4cyARJiFGKHoRIyQGSCIkTkBk3QxF9HMi0FcDAOjDFcI0PQKiGTqImAUXo0RlHjBkcYzoCjDESHmJnJYKw9CAMwKsHg0dY51yPinDgE9NzTynFwZqZgLBrFwIQEgud86gRcH3Aew5Th5y4LMXgbctDE1IOXSu1cOC11IAnJOHim4t0LsXFJMcOB3jcbkxuhT24pOvhGVIMQgA%3D%3D%3D"&gt;for arm64 Neon&lt;/a&gt;, things look pretty good!
The contents of the &lt;code class="language-plaintext highlighter-rouge"&gt;foreach&lt;/code&gt; loop have been compiled down to a single straight run of vectorized instructions, with all four lanes filled beforehand.
Comparing ISPC’s output with the compiler output for the hand-vectorized implementations, the core of the ray-box test looks very similar between the two, while ISPC’s output for all of the precalculation logic actually seems slightly better than the output from the hand-vectorized implementation.&lt;/p&gt;

&lt;p&gt;Here is how the ISPC implementation performs, compared to the baseline compact scalar implementation:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2 Speedup:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Compact:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.5159 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;41.8187 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;81.0942 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;ISPC:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;8.2877 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.3835x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;11.2182 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.7278x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;11.3709 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;7.1317x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The performance from the ISPC implementation looks really good!
Actually, on x86-64, the ISPC implementation’s performance looks &lt;em&gt;too good to be true&lt;/em&gt;: at first glance, a 5.3835x speedup over the compact scalar baseline implementation shouldn’t be possible since the maximum expected possible speedup is only 4x.
I had to think about this result a while; I think the explanation for this apparently better-than-possible speedup is because the setup versus the actual intersection test parts of the 4-wide ray-box test need to be considered separately.
The actual intersection part is the part that is an apples-to-apples comparison across all of the different implementations, while the setup code can vary significantly both in how it is written and in how well it can be optimized across different implementations.
The reason for the above is that the setup code is more inherently scalar.
I think that the reason the ISPC implementation has an overall more-than-4x speedup over the baseline is because in the baseline implementation, the scalar setup code is not much out of the &lt;code class="language-plaintext highlighter-rouge"&gt;-O3&lt;/code&gt; optimization level, whereas the ISPC implementation’s setup code is both getting more out of ISPC’s &lt;code class="language-plaintext highlighter-rouge"&gt;-O3&lt;/code&gt; optimization level and is additionally just better vectorized on account of being ISPC code.
A data point that lends credence to this theory is that when Clang and ISPC are both forced to disabled all optimizations using the &lt;code class="language-plaintext highlighter-rouge"&gt;-O0&lt;/code&gt; flag, the performance difference between the baseline and ISPC implementations falls back into a much more expected multiplier below 4x.&lt;/p&gt;

&lt;p&gt;Generally, I really like ISPC!
ISPC delivers on the promise of write-once compiler-and-run-anywhere vectorized code, and unlike auto-vectorization, ISPC’s output compiler assembly performs as we expect for well-written vectorized code.
Of course, ISPC isn’t 100% fool-proof magic; care still needs to be taken in writing good ISPC programs that don’t contain excessive amounts of execution path divergence between SIMD lanes, and care still needs to be taken in not doing too many expensive gather/scatter operations.
However, these types of considerations are just part of writing vectorized code in general and are not specific to ISPC, and furthermore, these types of considerations should be familiar territory for anyone with experience writing GPU code as well.
I think that’s a general strength of ISPC: writing vector CPU code using ISPC feels a lot like writing GPU code, and that’s by design!&lt;/p&gt;

&lt;div id="2021-09-07-results"&gt;&lt;/div&gt;

&lt;h2 id="final-results-and-conclusions"&gt;Final Results and Conclusions&lt;/h2&gt;

&lt;p&gt;Now that we’ve walked through every implementation in the test program, below are the complete results for every implementation across x86-64, arm64, and Rosetta 2.
As mentioned earlier, all results were generated by running on a 2019 16 inch MacBook Pro with a Intel Core i7-9750H CPU for x86-64, and on a 2020 M1 Mac Mini for arm64 and Rosetta 2.
All results were generated by running the test program with 100000 runs per implementation; the timings reported are the average time for one run.
I ran the test program 5 times with 100000 runs each time; after throwing out the highest and lowest result for each implementation to discard outliers, I then averaged the remaining three results for each implementation for each architecture.
In the results, the “speedup” columns use the scalar compact implementation as the baseline for comparison:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Results&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;x86-64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;arm64 Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Rosetta2 Speedup:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Compact:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.5159 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;41.8187 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;81.0942 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar Original:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;44.1004 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0117x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;78.4001 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.5334x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;90.7649 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8935x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Scalar No Early-Out:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;55.6770 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8014x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;85.3562 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.4899x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;102.763 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.7891x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;SSE:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;10.9660 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.0686x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;13.6353 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.9474x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;SSE2NEON:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;12.3090 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.3974x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Neon:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;12.2161 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.4232x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Autovectorize:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;34.1398 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.3069x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;38.1917 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0950x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;59.9757 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.3521x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;ISPC:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;8.2877 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.3835x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;11.2182 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.7278x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;11.3709 ns&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;7.1317x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;In each of the sections above, we’ve already looked at how the performance of each individual implementation compares against the baseline compact scalar implementation.
Ranking all of the approaches (at least for the specific example used in this post), ISPC produces the best performance, hand-vectorization using each processor’s native vector intrinsics comes in second, hand-vectorization using a translation layer such as sse2neon follows very closely behind using native vector intrinsics, and finally auto-vectorization comes in a distant last place.
Broadly, I think a good rule of thumb is that auto-vectorization is better than nothing, and that for large complex programs where vectorization is important and where cross-platform is required, ISPC is the way to go.
For smaller-scale things where the additional development complexity of bringing in an additional compiler isn’t justified, writing directly using vector intrinsics is a good solution, and using translation layers like sse2neon to port code written using one architecture’s vector intrinsics to another architecture without a total rewrite can work just as well as rewriting from scratch (assuming the translation layer is as well-written as sse2neon is).
Finally, as mentioned earlier, I was very surprised to learn that Rosetta 2 seems to be much better at translating vector instructions than it is at translating normal scalar x86-64 instructions.&lt;/p&gt;

&lt;p&gt;Looking back over the final test program, around a third of the total lines of code in the test program aren’t ray-box intersection code at all.
Around a third of the code is made up of just defining data structures and doing data marshaling to make sure that the actual ray-box intersection code can be efficiently vectorized at all.
I think that in most applications of vectorization, figuring out the data marshaling to enable good vectorization is just as important of a problem as actually writing the core vectorized code, and I think the data marshaling can often be even harder than the actual vectorization part.
Even the ISPC implementation in this post only works because the specific memory layout of the &lt;code class="language-plaintext highlighter-rouge"&gt;BBox4&lt;/code&gt; data structure is designed for optimal vectorized access.&lt;/p&gt;

&lt;p&gt;For much larger vectorized applications, such as full production renderers, planning ahead for vectorization doesn’t just mean figuring out how to lay out data structures in memory, but can mean having to incorporate vectorization considerations into the fundamental architecture of the entire system.
A great example of the above is DreamWorks Animation’s Moonray renderer, which has an entire architecture designed around coalescing enough coherent work in an incoherent path tracer to facilitate ISPC-based vectorized shading &lt;a href="https://dl.acm.org/citation.cfm?doid=3105762.3105768"&gt;[Lee et al. 2017]&lt;/a&gt;.
Weta Digital’s Manuka renderer goes even further by fundamentally restructuring the typical order of operations in a standard path tracer into a &lt;em&gt;shade-before-hit&lt;/em&gt; architecture, also in part to facilitate vectorized shading &lt;a href="https://doi.org/10.1145/3182161"&gt;[Fascione et al. 2018]&lt;/a&gt;.
Pixar and Intel have also worked together recently to extend OSL with better vectorization for use in RenderMan XPU, which has necessitated the addition of a new batched interface to OSL &lt;a href="https://www.youtube.com/watch?v=-WqrP50nvN4"&gt;[Liani and Wells 2020]&lt;/a&gt;.
Some other interesting large non-rendering applications where vectorization has been applied through the use of clever rearchitecting include JPEG encoding &lt;a href="https://blog.cloudflare.com/neon-is-the-new-black/"&gt;[Krasnov 2018]&lt;/a&gt; and even &lt;a href="https://github.com/simdjson/simdjson"&gt;JSON parsing&lt;/a&gt; &lt;a href="https://doi.org/10.1007/s00778-019-00578-5"&gt;[Langdale and Lemire 2019]&lt;/a&gt;.
More generally, the entire domain of data-oriented design &lt;a href="https://www.youtube.com/watch?v=rX0ItVEVjHc"&gt;[Acton 2014]&lt;/a&gt; revolves around understanding how to structure data layout according to how computation needs to access said data; although data-oriented design was originally motivated by a need to efficiently utilize the CPU cache hierarchy, data-oriented design is also highly applicable to structuring vectorized programs.&lt;/p&gt;

&lt;p&gt;In this post, we only looked at 4-wide 128-bit SIMD extensions.
Vectorization is not limited to 128-bits or 4-wide instructions, of course; x86-64’s newer &lt;a href="https://en.wikipedia.org/wiki/Advanced_Vector_Extensions"&gt;AVX instructions&lt;/a&gt; use 256-bit registers and, when used with 32-bit floats, AVX is 8-wide.
The newest version of AVX, &lt;a href="https://en.wikipedia.org/wiki/AVX-512"&gt;AVX-512&lt;/a&gt;, extends things even wider to 512-bit registers and can support a whopping 16 32-bit lanes.
Similarly, ARM’s new &lt;a href="https://developer.arm.com/architectures/instruction-sets/simd-isas/sve/sve-programmers-guide"&gt;SVE vector extensions&lt;/a&gt; serve as a wider successor to Neon (ARM also recently introduced a new lower-energy lighter weight companion vector extension to Neon, named &lt;a href="https://developer.arm.com/architectures/instruction-sets/simd-isas/helium/helium-programmers-guide"&gt;Helium&lt;/a&gt;).
Comparing AVX and SVE is interesting, because their design philosophies are much further apart than the relatively similar design philosophies behind SSE and Neon.
AVX serves as a direct extension to SSE, to the point where even AVX’s YMM registers are really just an expanded version of SSE’s XMM registers (on processors supporting AVX, the XMM registers physically are actually just the lower 128 bits of the full YMM registers).
Similar to AVX, the lower bits of SVE’s registers also overlap Neon’s registers, but SVE uses a new set of vector instructions separate from Neon.
The big difference between AVX and SVE is that while AVX and AVX-512 specify fixed 256-bit and 512-bit widths respectively, SVE allows for different implementations to define different widths from a minimum of 128-bit all the way up to a maximum of 2048-bit, in 128-bit increments.
At some point in the future, I think a comparison of AVX and SVE could be fun and interesting, but I didn’t touch on them in this post because of a number of current problems.
In many Intel processors today, AVX (and especially AVX-512) is so power-hungry that using AVX means that the processor has to throttle its clock speeds down &lt;a href="https://blog.cloudflare.com/on-the-dangers-of-intels-frequency-scaling/"&gt;[Krasnov 2017]&lt;/a&gt;, which can in some cases completely negate any kind of performance improvement.
The challenge with testing SVE code right now is… there just aren’t many arm64 processors out that actually implement SVE yet!
As of the time of writing, the only publicly released arm64 processor in the world that I know of that implements SVE is Fujitsu’s A64FX supercomputer processor, which is not exactly an off-the-shelf consumer part.
NVIDIA’s upcoming Grace arm64 server CPU is also supposed to implement SVE, but as of 2021, the Grace CPU is still a few years away from release.&lt;/p&gt;

&lt;p&gt;At the end of the day, for any application where vectorization is a good fit, not using vectorization means leaving a large amount of performance on the table.
Of course, the example used in this post is just a single data point, and is a relatively small example; your mileage may and likely will vary for different and larger examples!
As with any programming task, understanding your problem domain is crucial for understanding how useful any given technique will be, and as seen in this post, great care must be taken to structure code and data to even be able to take advantage of vectorization.
Hopefully this post has served as a useful examination of several different approaches to vectorization!
Again, I have put all of the code in this post in &lt;a href="https://github.com/betajippity/sseneoncompare"&gt;an open Github repository&lt;/a&gt;; feel free to play around with it yourself (or if you are feeling especially ambitious, feel free to use it as a starting point for a full vectorized BVH implementation)!&lt;/p&gt;

&lt;hr /&gt;

&lt;div id="2021-09-07-addenda"&gt;&lt;/div&gt;
&lt;h2 id="addendum-2022-09-07"&gt;Addendum 2022-09-07&lt;/h2&gt;

&lt;p&gt;After I published this post, &lt;a href="https://twitter.com/romainguy"&gt;Romain Guy&lt;/a&gt; wrote in with a suggestion to use &lt;code class="language-plaintext highlighter-rouge"&gt;-ffast-math&lt;/code&gt; to improve the auto-vectorization results.
I gave the suggestion a try, and the result was indeed markedly improved!
Across the board, using &lt;code class="language-plaintext highlighter-rouge"&gt;-ffast-math&lt;/code&gt; cut the auto-vectorization timings by about half, corresponding to around a doubling of performance.
Using &lt;code class="language-plaintext highlighter-rouge"&gt;ffast-math&lt;/code&gt;, the auto-vectorized implementation still trails behind the hand-vectorized and ISPC implementations, but by a much narrower margin than before, and overall is much much better than the compact scalar baseline.
Romain previously presented &lt;a href="https://www.youtube.com/watch?v=Lcq_fzet9Iw"&gt;a talk in 2019&lt;/a&gt; about Google’s Filament real-time rendering engine, which includes many additional tips for making auto-vectorization work better.&lt;/p&gt;

&lt;hr /&gt;

&lt;div id="2021-09-07-references"&gt;&lt;/div&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;

&lt;p&gt;Mike Acton. 2014. &lt;a href="https://www.youtube.com/watch?v=rX0ItVEVjHc"&gt;Data-Oriented Design and C++&lt;/a&gt;. In &lt;em&gt;CppCon 2014&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;AMD. 2020. &lt;a href="https://gpuopen.com/rdna2-isa-available/"&gt;“RDNA 2” Instruction Set Architecture Reference Guide&lt;/a&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2021. &lt;a href="https://developer.arm.com/architectures/instruction-sets/intrinsics/"&gt;ARM Intrinsics&lt;/a&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2021. &lt;a href="https://developer.arm.com/architectures/instruction-sets/simd-isas/helium/helium-programmers-guide"&gt;Helium Programmer’s Guide&lt;/a&gt;. Retrieved September 5, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2021. &lt;a href="https://developer.arm.com/architectures/instruction-sets/simd-isas/sve/sve-programmers-guide"&gt;SVE and SVE2 Programmer’s Guide&lt;/a&gt;. Retrieved September 5, 2021.&lt;/p&gt;

&lt;p&gt;Holger Dammertz, Johannes Hanika, and Alexander Keller. 2008. &lt;a href="https://doi.org/10.1111/j.1467-8659.2008.01261.x"&gt;Shallow Bounding Volume Hierarchies for Fast SIMD Ray Tracing of Incoherent Rays&lt;/a&gt;. &lt;em&gt;Computer Graphics Forum (Proc. of Eurographics Symposium on Rendering&lt;/em&gt; 27, 4 (Jun. 2008), 1225-1234.&lt;/p&gt;

&lt;p&gt;Manfred Ernst and Günther Greiner. 2008. &lt;a href="https://doi.org/10.1109/RT.2008.4634618"&gt;Multi Bounding Volume Hierarchies&lt;/a&gt;. In &lt;em&gt;Proc. of IEEE Symposium on Interactive Ray Tracing (RT 2008)&lt;/em&gt;. 35-40.&lt;/p&gt;

&lt;p&gt;Luca Fascione, Johannes Hanika, Mark Leone, Marc Droske, Jorge Schwarzhaupt, Tomáš Davidovič, Andrea Weidlich and Johannes Meng. 2018. &lt;a href="https://dl.acm.org/citation.cfm?id=3182161"&gt;Manuka: A Batch-Shading Architecture for Spectral Path Tracing in Movie Production&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics&lt;/em&gt; 37, 3 (Jul. 2018), Article 31.&lt;/p&gt;

&lt;p&gt;Romain Guy and Mathias Agopian. 2019. &lt;a href="https://www.youtube.com/watch?v=Lcq_fzet9Iw"&gt;High Performance (Graphics) Programming&lt;/a&gt;. In &lt;em&gt;Android Dev Summit ‘19&lt;/em&gt;. Retrieved September 7, 2021.&lt;/p&gt;

&lt;p&gt;Intel Corporation. 2021. &lt;a href="https://software.intel.com/sites/landingpage/IntrinsicsGuide/"&gt;Intel Intrinsics Guide&lt;/a&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;Intel Corporation. 2021. &lt;a href="https://ispc.github.io/ispc.html"&gt;Intel ISPC User’s Guide&lt;/a&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;Thiago Ize. 2013. &lt;a href="http://jcgt.org/published/0002/02/02/"&gt;Robust BVH Ray Traversal&lt;/a&gt;. &lt;em&gt;Journal of Computer Graphics Techniques&lt;/em&gt; 2, 2 (Jul. 2013), 12-27.&lt;/p&gt;

&lt;p&gt;Tero Karras and Timo Aila. 2013. &lt;a href="https://doi.org/10.1145/2492045.2492055"&gt;Fast Parallel Construction of High-Quality Bounding Volume Hierarchies&lt;/a&gt;. In &lt;em&gt;Proc. of High Performance Graphics (HPG 2013)&lt;/em&gt;. 89-88.&lt;/p&gt;

&lt;p&gt;Vlad Krasnov. 2017. &lt;a href="https://blog.cloudflare.com/on-the-dangers-of-intels-frequency-scaling/"&gt;On the dangers of Intel’s frequency scaling&lt;/a&gt;. In &lt;em&gt;Cloudflare Blog&lt;/em&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;Vlad Krasnov. 2018. &lt;a href="https://blog.cloudflare.com/neon-is-the-new-black/"&gt;NEON is the new black: fast JPEG optimization on ARM server&lt;/a&gt;. In &lt;em&gt;Cloudflare Blog&lt;/em&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;Geoff Langdale and Daniel Lemire. 2019. &lt;a href="https://doi.org/10.1007/s00778-019-00578-5"&gt;Parsing Gigabytes of JSON per Second&lt;/a&gt;. &lt;em&gt;The VLDB Journal&lt;/em&gt;. 28 (2019), 941-960.&lt;/p&gt;

&lt;p&gt;Mark Lee, Brian Green, Feng Xie, and Eric Tabellion. 2017. &lt;a href="https://dl.acm.org/citation.cfm?doid=3105762.3105768"&gt;Vectorized Production Path Tracing&lt;/a&gt;. In &lt;em&gt;Proc. of High Performance Graphics (HPG 2017)&lt;/em&gt;. Article 10.&lt;/p&gt;

&lt;p&gt;Max Liani and Alex M. Wells. 2020. &lt;a href="https://www.youtube.com/watch?v=-WqrP50nvN4"&gt;Supercharging Pixar’s RenderMan XPU with Intel AVX-512&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2020 Exhibitor Sessions&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Alexander Majercik, Cyril Crassin, Peter Shirley, and Morgan McGuire. 2018. &lt;a href="http://jcgt.org/published/0007/03/04/"&gt;A Ray-Box Intersection Algorithm and Efficient Dynamic Voxel Rendering&lt;/a&gt;. &lt;em&gt;Journal of Computer Graphics Techniques&lt;/em&gt; 7, 3 (Sep. 2018), 66-81.&lt;/p&gt;

&lt;p&gt;Daniel Meister, Shinji Ogaki, Carsten Benthin, Michael J. Doyle, Michael Guthe, and Jiri Bittner. 2021. &lt;a href="https://doi.org/10.1111/cgf.142662"&gt;A Survey on Bounding Volume Hierarchies for Ray Tracing&lt;/a&gt;. &lt;em&gt;Computer Graphics Forum (Proc. of Eurographics)&lt;/em&gt; 40, 2 (May 2021), 683-712.&lt;/p&gt;

&lt;p&gt;NVIDIA. 2021. &lt;a href="https://raytracing-docs.nvidia.com/optix7/guide/index.html"&gt;NVIDIA OptiX 7.3 Programming Guide&lt;/a&gt;. Retrieved August 30, 2021.&lt;/p&gt;

&lt;p&gt;Howard Oakley. 2021. &lt;a href="https://eclecticlight.co/2021/08/23/code-in-arm-assembly-lanes-and-loads-in-neon/"&gt;Code in ARM Assembly: Lanes and loads in NEON&lt;/a&gt;. In &lt;em&gt;The Eclectic Light Company&lt;/em&gt;. Retrieved September 7, 2021.&lt;/p&gt;

&lt;p&gt;Matt Pharr. 2018. &lt;a href="https://pharr.org/matt/blog/2018/04/30/ispc-all"&gt;The Story of ISPC&lt;/a&gt;. In &lt;em&gt;Matt Pharr’s Blog&lt;/em&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Matt Pharr and William R. Mark. 2012. &lt;a href="https://doi.org/10.1109/InPar.2012.6339601"&gt;ispc: A SPMD compiler for high-performance CPU programming&lt;/a&gt;. In &lt;em&gt;Proc. of nnovative Parallel Computing (InPar 2012)&lt;/em&gt;. 184-196.&lt;/p&gt;

&lt;p&gt;Martin Stich, Heiko Friedrich, and Andreas Dietrich. 2009. &lt;a href="https://doi.org/10.1145/1572769.1572771"&gt;Spatial Splits in Bounding Volume Hierarchies&lt;/a&gt;. In &lt;em&gt;Proc. of High Performance Graphics (HPG 2009)&lt;/em&gt;. 7-13.&lt;/p&gt;

&lt;p&gt;John A. Tsakok. 2009. &lt;a href="https://doi.org/10.1145/1572769.1572793"&gt;Faster Incoherent Rays: Multi-BVH Ray Stream Tracing&lt;/a&gt;. In &lt;em&gt;Proc. of High Performance Graphics (HPG 2009)&lt;/em&gt;. 151-158.&lt;/p&gt;

&lt;p&gt;Nathan Vegdahl. 2017. &lt;a href="https://psychopath.io/post/2017_08_03_bvh4_without_simd"&gt;BVH4 Without SIMD&lt;/a&gt;. In &lt;em&gt;Psychopath Renderer&lt;/em&gt;. Retrieved August 20, 2021.&lt;/p&gt;

&lt;p&gt;Ingo Wald, Carsten Benthin, and Solomon Boulos. 2008. &lt;a href="https://doi.org/10.1109/RT.2008.4634620"&gt;Getting Rid of Packets - Efficient SIMD Single-Ray Traversal using Multi-Branching BVHs&lt;/a&gt;. In &lt;em&gt;Proc. of IEEE Symposium on Interactive Ray Tracing (RT 2008)&lt;/em&gt;. 49-57.&lt;/p&gt;

&lt;p&gt;Ingo Wald, Philipp Slusallek, Carsten Benthin, and Markus Wagner. 2001. &lt;a href="https://doi.org/10.1111/1467-8659.00508"&gt;Interactive Rendering with Coherent Ray Tracing&lt;/a&gt;. &lt;em&gt;Computer Graphics Forum (Proc. of Eurographics&lt;/em&gt; 20, 3 (Sep. 2001), 153-165.&lt;/p&gt;

&lt;p&gt;Ingo Wald, Sven Woop, Carsten Benthin, Gregory S. Johnson, and Manfred Ernst. 2014. &lt;a href="https://doi.org/10.1145/2601097.2601199"&gt;Embree: A Kernel Framework for Efficient CPU Ray Tracing&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics (Proc. of SIGGRAPH)&lt;/em&gt; 33, 4 (Jul. 2014), Article 143.&lt;/p&gt;

&lt;p&gt;Amy Williams, Steve Barrus, Keith Morley, and Peter Shirley. 2005. &lt;a href="https://doi.org/10.1080/2151237X.2005.10129188"&gt;An Efficient and Robust Ray-Box Intersection Algorithm&lt;/a&gt;. _Journal of Graphics Tools) 10, 1 (Jan. 2005), 49-54.&lt;/p&gt;

&lt;p&gt;Henri Ylitie, Tero Karras, and Samuli Laine. 2017. &lt;a href="https://doi.org/10.1145/3105762.3105773"&gt;Efficient Incoherent Ray Traversal on GPUs Through Compressed Wide BVHs&lt;/a&gt;. In &lt;em&gt;Proc. of High Performance Graphics (HPG 2017)&lt;/em&gt;. Article 4.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Advanced_Vector_Extensions"&gt;Advanced Vector Extensions&lt;/a&gt;. Retrieved September 5, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Automatic_vectorization"&gt;Automatic Vectorization&lt;/a&gt;. Retrieved September 4, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/AVX-512"&gt;AVX-512&lt;/a&gt;. Retrieved September 5, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_threads"&gt;Single Instruction, Multiple Threads&lt;/a&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/SPMD"&gt;SPMD&lt;/a&gt;. Retrieved July 18, 2021.&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Tue, 07 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/09/neon-vs-sse.html</guid></item><item><title>Learning new things and developer productivity - 123dev #2</title><link>https://justingarrison.com/123dev/2021-09-07-123dev-2/</link><description>&lt;img alt="someone skis off a cliff and thankfully has a parachute" src="https://justingarrison.com/img/123dev/2-0.gif" width="100%" /&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Learning new things&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The gif reminded me of what it feels like to be new to something. You think you know where you&amp;rsquo;re going, you start to understand what you&amp;rsquo;re doing, and then you&amp;rsquo;re free falling. You pull the rip cord, gain a resemblance of control, and slow down—just a little bit.&lt;/p&gt;
&lt;p&gt;Learning new things doesn&amp;rsquo;t end the way it started and you still don&amp;rsquo;t understand it all, but you&amp;rsquo;re having fun and want more!&lt;/p&gt;</description><author>Justin Garrison's Homepage</author><pubDate>Tue, 07 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-07-123dev-2/</guid></item><item><title>How arbitrary structured data per request helps Opsgenie to resolve and prevent incidents</title><link>/posts/2021-09-07-how-arbitrary-structured-data-per-request-helps-opsgenie-to-resolve-and-prevent-incidents/</link><description>It&amp;rsquo;s been a long time since I&amp;rsquo;ve blogged. My backlog of topics is full, however, I could not publish due to both professional and personal developments in the last 1.5 years. Anyways, glad to be back.
For some context, I&amp;rsquo;m from Opsgenie. Since Opsgenie is an on-call and alert management tool, it needs to be highly available than most of the services. We take it very seriously. Over the years, we have employed and evolved many engineering practices and principles to consistently achieve this.</description><author>Mustafa Akın</author><pubDate>Tue, 07 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/posts/2021-09-07-how-arbitrary-structured-data-per-request-helps-opsgenie-to-resolve-and-prevent-incidents/</guid></item><item><title>Hey Google, Siri, Alexa... tell me how this ends</title><link>https://stop.zona-m.net/2021/09/hey-google-siri-alexa...-tell-me-how-this-ends/</link><description>&lt;p&gt;Another serious issue that should be settled for good. Will ANYBODY listen, this time?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 06 Sep 2021 11:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/hey-google-siri-alexa...-tell-me-how-this-ends/</guid></item><item><title>Hiking the Translagorai 25 years later</title><link>https://nicolaiarocci.com/hiking-the-translagorai-25-years-later/</link><description>&lt;p&gt;A couple of weeks ago I went hiking the Translagorai, an 80 km-long trail that
traverses the whole Lagorai mountain range in the Alps. The route takes place
in a mountain environment where the anthropic pressure is reduced, especially
compared to the more famous nearby Dolomites destinations. There are very few
managed huts, most of which do not offer overnight stays, and relatively few
bivouacs (emergency huts, usually dislocated in the most isolated areas along
the hike). These characteristics make the Translagorai very attractive to
those looking for wild hikes to be travelled in complete self-sufficiency
with tent, food, etc. There is rich evidence (remains of barracks, trenches)
dating back to the First World War. Translagorai is a brutal, wild, restless,
and beautiful route reserved for well-trained, experienced mountain hikers.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 06 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/hiking-the-translagorai-25-years-later/</guid></item><item><title>Let the music play</title><link>https://codeexplainer.wordpress.com/2021/09/06/let-the-music-play/</link><description>Good news for all International Obfuscated C Code Contest fans &amp;#8212; here&amp;#8217;s another winning piece from 2013 written by Yusuke Endoh, who is no stranger to the Contest. It is dubbed the Most Tweetable 1-Liner, because is small enough to &amp;#8230; &lt;a href="https://codeexplainer.wordpress.com/2021/09/06/let-the-music-play/"&gt;Continue reading &lt;span class="meta-nav"&gt;&amp;#8594;&lt;/span&gt;&lt;/a&gt;</description><author>Code Explainer</author><pubDate>Mon, 06 Sep 2021 06:40:02 GMT</pubDate><guid isPermaLink="true">https://codeexplainer.wordpress.com/2021/09/06/let-the-music-play/</guid></item><item><title>Welcome to 123dev - 123dev #1</title><link>https://justingarrison.com/123dev/2021-09-06-123dev-1/</link><description>Since this is the 1st newsletter I’ll explain how it works. I’d like to help you learn development or become a better developer. I also want you to look forward to opening this newsletter.</description><author>Justin Garrison's Homepage</author><pubDate>Mon, 06 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/123dev/2021-09-06-123dev-1/</guid></item><item><title>Boss Level</title><link>https://olshansky.info/movie/boss_level/</link><description>Olshansky's review of Boss Level</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 05 Sep 2021 11:05:51 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/boss_level/</guid></item><item><title>Correlations, seasonality, lift and CUPED</title><link>https://bytepawn.com/correlations-seasonality-lift-and-cuped.html</link><description>&lt;p&gt;In this final blog post about CUPED, I will address some questions about CUPED, such as, &lt;em&gt;is correlation between "before" and "after" the same as seasonality?&lt;/em&gt;&lt;br /&gt;&lt;br /&gt; &lt;img alt="." src="/images/aa_cuped_3.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 05 Sep 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/correlations-seasonality-lift-and-cuped.html</guid></item><item><title>Peak car is just one face of peak complexity</title><link>https://stop.zona-m.net/2021/09/peak-car-is-just-one-face-of-peak-complexity/</link><description>&lt;p&gt;We are too &amp;ldquo;smart&amp;rdquo; for our own good.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 04 Sep 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/peak-car-is-just-one-face-of-peak-complexity/</guid></item><item><title>Roads as Catwalks</title><link>https://nonhuman.party/post/fastest_catwalks/</link><description>Even before the decline of the horse-drawn transportation industry (and the subsequent loss of jobs that nobody cares about anymore), road vehicles have been status symbols. Stage coaches were meant to show off the wealth of the occupants, while at the same time, giving them &amp;ldquo;privacy&amp;rdquo; from all the dirty riff-raff outside.</description><author>Non-Human Party</author><pubDate>Sat, 04 Sep 2021 15:25:05 GMT</pubDate><guid isPermaLink="true">https://nonhuman.party/post/fastest_catwalks/</guid></item><item><title>Get the Fucking Vaccine Already, You Fucking Fucks</title><link>https://nicolaiarocci.com/get-the-fucking-vaccine-already-you-fucking-fucks/</link><description>&lt;p&gt;Wendy Molyneux, &lt;a href="https://www.mcsweeneys.net/articles/oh-my-fucking-god-get-the-fucking-vaccine-already-you-fucking-fucks"&gt;writing unabashedly&lt;/a&gt; for McSweeney&amp;rsquo;s, summarizes all I have
to say on the COVID vaccine and never dared to say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You think vaccines don’t fucking work? Oh, fuck off into the trash, you
attention-seeking fuckworm-faced shitbutt. This isn’t even a point worth
discussing, you fuck-o-rama fuck-stival of ignorance. Vaccines got rid of
smallpox and polio and all the other disgusting diseases that used to kill
off little fucks like you en masse. Your relatives got fucking vaccinated and
let you live, and now here you are signing up to be killed by a fucking
disease against which there is a ninety-nine-percent effective vaccine. You
fucking moron. Go in the fucking ocean and fuck a piranha. Fuck. Fuck that.
Fuck you. Get vaccinated.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 04 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/get-the-fucking-vaccine-already-you-fucking-fucks/</guid></item><item><title>A Review of Darktable</title><link>https://bastibe.de/2021-09-04-a-review-of-darktable.html</link><description>&lt;p&gt;With version 3.0, my favorite photo editing software Darktable started the journey towards a scene-referred editing pipeline. Which means that most edits are no longer bounded between a fixed black zero, and a white one, but can range between zero and infinity, like light itself. This is the norm in video editing and video games, but unique in photo editors at the moment. The scene-referred pipeline has brought changes to pretty much all parts of the editing workflow. I have been frustrated with it, too, even opting to use &lt;a href="https://bastibe.de/2021-05-04-a-review-of-the-raw-photo-editor-capture-one.html"&gt;Capture One&lt;/a&gt; for a while. But with version 3.6, Darktable seems to have found a new stride. This article is about my favorite features in Darktable 3.6, contrasting it with Capture One's more traditional toolset.&lt;/p&gt;
&lt;p&gt;A camera sensor records light. When there is very little light, we call that &amp;quot;black&amp;quot;, but there is really no similar label for when there is a lot of it. It's just very bright. A very bright blue light is still blue, and could always be brighter. However, screens and print and analog film can not get arbitrarily bright. Instead, they have a limited maximum brightness, and the brightest color is always white. Which is why pushing things ever brighter in photo editing eventually turns them white, because that is the brightest thing possible on screen or print.&lt;/p&gt;
&lt;p&gt;But this is a limitation of the display, not the editor or the recording. Thus it makes sense to edit photos with a physical understanding of light, where making things brighter does not make them whiter. Consider sharpening, or color grading, or contrast adjustments: All of these can push pixels brighter or darker, but really shouldn't suddenly whiten them at some arbitrary brightness. In the new scene-referred Darktable, colors are squeezed into a viewable or printable form only at the very end of the processing, retaining their physical properties for as long as possible. In most other tools, the squeezing is done early on, and edits hobbled thereafter.&lt;/p&gt;
&lt;p&gt;In practice, this means Darktable can change tones with any number of tools without losing color information: the tone equalizer, or the rgb curve, or levels rgb, or the contrast equalizer, or sharpening, or color balance all manipulate tones without fear of losing highlight information. Only filmic rgb at the very end determines which bright pixels to turn white. In a way, the scene-referred part of the pipeline feels like raw editing, while the rest feels like editing a JPEG. Contrast this with Capture One, where only the sliders in the Exposure and High Dynamic Range modules can recover bright pixels from white, but e.g. Clarity, Levels, and Curve can not. After getting used to the scene-referred way of working, this seems like an arbitrary limitation, and really makes Levels and Curve less useful than they should be.&lt;/p&gt;
&lt;figure style="width: 100%; text-align: center;"&gt;
&lt;a href="/static/2021-09/darktable tone eq and exposure.png" target="_blank"&gt;&lt;img alt="Darktable screen layout with tone equalizer and exposure module opened" src="/static/2021-09/darktable tone eq and exposure.png" width="75%" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Darktable's main development screen with the &lt;em&gt;tone equalizer&lt;/em&gt; and &lt;em&gt;exposure&lt;/em&gt; module&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Interestingly, this leads to a somewhat different editing workflow in Darktable compared to Capture One. Since the scene-referred pipeline is mostly nondestructive, I can play with the overall tonality with abandon, safe in the knowledge that I will be able to recover any lost detail later. Thus I commonly use the &lt;em&gt;exposure&lt;/em&gt; slider to expose for my mid-tones, and then reign in my burning highlights with the tone equalizer&lt;sup class="footnote-ref" id="fnref-1"&gt;&lt;a href="#fn-1"&gt;1&lt;/a&gt;&lt;/sup&gt; or filmc rgb &lt;em&gt;white relative exposure&lt;sup class="footnote-ref" id="fnref-2"&gt;&lt;a href="#fn-2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;. In Capture One, digital overexposure is much more difficult to recover than underexposure, so I usually use the high dynamic range tools to underexpose slightly, and then recover mid-tone exposure with the high dynamic range &lt;em&gt;shadows&lt;/em&gt; slider or tone curve. While these two workflows are largely equivalent, I find it interesting that Darktable can go either way, while Capture One can not.&lt;/p&gt;
&lt;p&gt;A related area of rapid development in Darktable has been the color balance rgb module, and color calibration. Both of these allow for very fine-grained control over various aspects of color, handily parametrized by hue or tone or channel. There is some subtlety in the &lt;a href="https://www.darktable.org/usermanual/3.6/en/special-topics/color-management/color-dimensions/"&gt;various controls&lt;/a&gt; for &amp;quot;colorfulness&amp;quot;, &amp;quot;vibrance&amp;quot;, &amp;quot;chroma&amp;quot;, &amp;quot;brilliance&amp;quot;, and &amp;quot;saturation&amp;quot; that took some learning. But it also taught me to see color in a more nuanced way, which is a good thing. In mixed lighting, I can now use several instances of color calibration modules&lt;sup class="footnote-ref" id="fnref-3"&gt;&lt;a href="#fn-3"&gt;3&lt;/a&gt;&lt;/sup&gt; for different image areas, producing more natural results that I am capable of in Capture One.&lt;/p&gt;
&lt;p&gt;A particular pain point in Darktable 3.4 and Capture One was the rendering of colored highlights. In Darktable 3.4, bright colors often turned weirdly radiant, and then aggressively desaturated, with a sharp and unnatural edge in between. I don't quite know why, but this problem seems much reduced in 3.6. Both issues are now dealt with quickly and cleanly with a quick tug on the filmic rgb white relative exposure slider. A real boon that slider is. Now I just wish it didn't affect contrast quite so much and didn't also influence shadows, despite its name. Oh well, a man can dream. Capture One had the opposite problem, and sometimes wisted colored highlights into bright primary colors before desaturating, leading to cyan skies and yellow skin at the fringes of overexposure. This is still one of my major complaints about Capture One.&lt;/p&gt;
&lt;p&gt;My last point of struggle with Darktable has long been the rendering of colors of my Fujifilm camera. Something about the colors always seemed off, in a way they weren't in Capture One. Greens too blue, browns too magenta, and skin too pink. After quite some experimentation, I found that this was simply due to Fujifilm depending on a bit of proprietary color magic for appropriate rendering. I now apply one of &lt;a href="https://blog.sowerby.me/fuji-film-simulation-profiles/"&gt;Stuart Sowerby's LUTs&lt;/a&gt; in chromaticity blending mode after filmic rgb. This leaves tonal adjustments fully to Darktable, but fixes the awkward colors perfectly. And it even allows me to play with the film simulations Fuji is famous for.&lt;/p&gt;
&lt;figure style="width: 100%; text-align: center;"&gt;
&lt;a href="/static/2021-09/darktable filmic and lut.png" target="_blank"&gt;&lt;img alt="Darktable with lut 3D in chromaticity blend mode after filmic rgb" src="/static/2021-09/darktable filmic and lut.png" width="75%" /&gt;&lt;/a&gt;
&lt;figcaption&gt;A LUT is applied in chromaticity blend mode after &lt;em&gt;filmic rgb&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Actually, this highlights a unique strength of Darktable: the order of modules in the image editing pipeline is customizable, and most modules can be applied with masks and blending modes. Only since I used Capture One for a few months did I understand how powerful this concept really is. The key difference is that &amp;quot;layers&amp;quot; in Capture One (et al) merely apply the image editing pipeline with different parameters in different places[^4], while Darktable's modules actually apply sequentially. This enables true incremental editing, such as fixing white balance correctly for one part of the image, then applying another level of white balance just for the blue parts. Or fixing colors in a LUT &lt;em&gt;after&lt;/em&gt; all the tonal edits, without fear of destroying highlights or shadows in the process.&lt;/p&gt;
&lt;p&gt;[fn:4 i.e. you can fully desaturate a picture to black-and-white, then &amp;quot;recover&amp;quot; saturation in a layer&lt;/p&gt;
&lt;p&gt;So overall, I am very happy with Darktable in its latest iteration. As always, there are plenty of nitpicks I could bring forward, such as being a bit sluggish in operation, and the UI could be streamlined a bit&lt;sup class="footnote-ref" id="fnref-4"&gt;&lt;a href="#fn-4"&gt;4&lt;/a&gt;&lt;/sup&gt;. But it has once again displaced Capture One for my own work, and it makes me happy.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-1"&gt;&lt;p&gt;similar to the high dynamic range module, but more fine-grained&lt;a class="footnote" href="#fnref-1"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-2"&gt;&lt;p&gt;essentially a tone curve adjustment&lt;a class="footnote" href="#fnref-2"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-3"&gt;&lt;p&gt;the new way of doing white balance&lt;a class="footnote" href="#fnref-3"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn-4"&gt;&lt;p&gt;the new quick access panel in 3.6 has already helped a lot!&lt;a class="footnote" href="#fnref-4"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>bastibe.de</author><pubDate>Sat, 04 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://bastibe.de/2021-09-04-a-review-of-darktable.html</guid></item><item><title>Decentralization will never be enough. Even if it is digital</title><link>https://stop.zona-m.net/2021/09/decentralization-will-never-be-enough.-even-if-it-is-digital/</link><description>&lt;p&gt;And no amount of tech is going to change this fact.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 04 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/decentralization-will-never-be-enough.-even-if-it-is-digital/</guid></item><item><title>2021–09–04: Pinephone HDMI hot-plug-detection HW bug</title><link>https://xnux.eu/log/#045</link><author>megi's PinePhone Development Log</author><pubDate>Sat, 04 Sep 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#045</guid></item><item><title>The Swipe Files Strategy for Part Time Creators</title><link>https://www.swyx.io/swipe-files-strategy</link><description>Swipe Files are underrated, passively compounding sources of personal leverage for your creator journey.</description><author>swyx's site RSS Feed</author><pubDate>Fri, 03 Sep 2021 23:59:46 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/swipe-files-strategy</guid></item><item><title>Are you speaking to an empty Room...err Zoom?</title><link>https://world.hey.com/sunjain/are-you-speaking-to-an-empty-room-err-zoom-75880ffb</link><description>&lt;div class="trix-content"&gt;
  &lt;div&gt;You are into the fourth Zoom meeting of the day, and it is only 2pm. And while the speaker wants participants to pay 100% attention to this meeting, there is not much the speaker can do... as folks type away on chat while staring into the camera. Good typing skills were never so handy... or destructive depending on which side of the equation you are on.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As most of the work went virtual during the last 2 years, it is obvious that we have to do a "Think Again" on how we work as a team.&amp;nbsp; And how to lead better. The leaders have their work cut out for them. Even more so for the coming hybrid work environment. There have been great innovations in terms of a new set of tools coming out in this space - especially the ones using VR look most promising. &amp;nbsp; Facebook and Google are cooking up something there...with Microsoft not too far behind. And there are lot more virtual whiteboarding/collaboration solutions now - you have Ziteboard, Miro, Witeboard, DomainStoryTelling, GatherTown... the list goes on and on.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There has been lack of acknowledgement though, around the fact, that the virtual (and hybrid) model requires a change in how we work and how we use the tools we already have. You cannot continue to have the same communication style as you did when onsite and not see impact on team member stress levels - as well as yours. You cannot replace a conference room meeting with the Zoom one and a walk-up with a Slack ping - and expect it to all work out. Biggest gains can be had from changing our communication style, and how we lead people, without compromising on human interactions.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In my previous post on the topic of efficient communication, I had started with the importance of time. Of carving out uninterrupted blocks of time. It is better instead to think in terms of attention.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I would say that the most valuable currency... is not time...not the dollar, nor bitcoin but “attention.” We need to rethink our communication style if we want to avoid burnout without impacting productivity. This is true regardless of team being remote, onsite or hybrid. While being onsite may mask some of the stress and inefficiencies, it does not minimize the damage improper use of meetings and excessive direct chat pings does in those settings as well.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Increased communication does not equate to increased collaboration. In fact, it may even make leaders less effective if not done carefully. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Zoom fatigue is a real problem now - as well as Slack fatigue. And no, the solution is not to reduce their usage to a minimum - nor to abandon these. But if we do not use these properly, that is precisely what might already be happening. I am sure you have been in a Zoom meeting where you felt that most were chatting away - where the speaker feels hopeless listening to his own voice. Or folks have quit their chat for hours, just to get some work done. But that results in a move to text message on phone, and next to a phone call.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This post is not about trivializing any of these tools but how to use these better, collectively. To get more work done while reducing stress. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Not communicating is not an option. Communication is the most crucial tool for working well together. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you are looking for a fancy new productivity tool that can replace meetings and chats to improve the team's productivity and reduce stress, this is not the post for you. This post is about how to use existing tools better.&amp;nbsp; &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As they say in sports, more important than equipment is skill.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The defaults of any system should favor adoption of right practices - not encourage damaging ones. Right now, especially in virtual remote environment, it appears the bar for poor practices is set even lower. An inefficient meeting is just a click away. There is no constraint on conference room availability now. Since boundaries between work and life have been blurred, you do not even need to get up and walk to a person's desk - when there is this cool hip chat feature called direct ping just a click away, any time of the day.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There are a few misconceptions about how much stress and time wastage improper use of these causes:&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;While everyone understands the mental fatigue inefficient and excessive meetings cause...but most think of these as necessary evil, needed to get work done&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Meetings alone can ensure that team members are interacting and asking questions&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;That chat is great for all kinds of communication. And in the virtual world, it is the best tool for communication all the time, for all scenarios&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;While excessive and improper use of meetings and chats may have an impact on team member productivity and stress... but impact is minimal, and benefits far outweigh the downside&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Let us start with these misconceptions first and then see how we can improve upon them.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We are not going to get in the weeds of project management philosophy as to why projects get delayed. Also, we are not going to get into why we have some non-motivated team members or ones lacking skills. That is a much broader topic worthy of books which I am unqualified to write.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The best place to start though is to start this discussion is with that word "attention".&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One of the reasons as to why a team member is unable to finish their work on time is because they did not have enough attention cycle that work demands. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Consider this scenario which is not that uncommon. For quite a few, most workdays are chopped into a collection of meetings and a stream of unplanned direct chat pings. That leaves very little "attention cycle" to produce quality work. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It is 4pm and your fourth meeting of the day just finished. And you were only able to respond to the fifth chat ping out of fifty that have been on your chat notification. Quite a few of these chats lined up when you were in meetings (and even those five chats you were able to respond to, you did it while in the meeting). Of course, you are going to be stressed about the task that you have not even started working on that was due yesterday - unless you are already checked out and just collecting paycheck.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The only way to get work done in this scenario is to work late at night or on weekends - when you get a breather from inefficient meetings and excessive direct chat pings. That is the only time when you have attention cycles available that are needed to get that work done. This may be another version of "masochistic management" and "rock star employee" who gets work done only by working long hours. One of the factors pushing team members towards "late night worker" or "weekend warrior" is constant drain on attention by improper use of meetings and unplanned excessive chat messages. I am not saying this is the only reason but is a big factor.&amp;nbsp; "Brain Rules" by J Medina (section Attention) has good citations on impact of these constant interruptions as well as the wastefulness of multi-tasking.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There is a viable alternative approach, where more work can be done without stressing team members out due to improper use of meetings and unplanned excessive direct chat pings. While this may not be applicable all the time, it can be adopted for most scenarios.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Meeting, chats, and channels are not the cause of stress, it is how these are being used. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When done properly and in the right amount, meetings are the most useful tool to work together and so is chat. Creating a channel and setting up a series of meetings at the start of an initiative, without having clear guidelines on how and when to use these, is a sure way to generate more stress for team members.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Tip# 1: Use chats for communications that are:&amp;nbsp; social, urgent, troubleshooting and deployments.&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Chat is a great tool for water cooler type informal discussions. Be it about how the weekend was, or next coffee bean discovery or what have you. Great for these kinds of discussions and builds team bonds, should be encouraged. If these are not direct pings all day long, and there is no pressure on team members to always be available for these - nor is the expectation to respond immediately to such discussions.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Using chat for project discussions and agile team conversation is great - when used properly. If there are direct pings all day long and everything is urgent all the time, it can turn into a stress channel. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is more a matter of working style and less about the tool itself.&amp;nbsp; &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Team interactions in such project team channels go a long way to unblock others and move fast. This does not preempt the need to think ahead. Creating some initial written material ahead of time can reduce some of the back and forth, and provide context for some recurring discussions, as well as issues.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Chat is also a great tool for two other scenarios - Urgent and Troubleshooting/Deployments.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let us talk about troubleshooting first. When there is an active problem identified and the team is diagnosing the issue, there is no other tool better than chat. The team can quickly hop in a channel and work together, most of the time without resorting to a meeting. In all hands-on deck kind of situations, a meeting can also be setup. Same goes for deployments - most deployment tools have chat integrations - and the ability to not just notify but trigger deployments via chats is extremely useful.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Next, let us talk about urgent communication. When you want a quick answer to your questions, the ability to reach out to someone via direct chat ping, and expect a quick answer, is especially useful. Expecting an instant email response during such times usually does not work. And picking up the phone or setting up a meeting for each such request is impractical.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Chat works great for such urgent needs. The problem is when every request turns into an urgent one, and everyone feels entitled to demand an instant response, all the time. And this is where chatting is most destructive. It is not the tools itself but how they are used and the expectation. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When you have fifty direct pings (red dots) on your chat notification, there is no precise way to tell, in most chat applications, as to which one of those fifty pings is urgent. And which ones are causal/social discussions? This is exactly what forces anyone who uses chat to constantly check on chat - else they are going to miss something urgent from their manager or good old FOMO. Or abandon chat completely for extended periods just to get a respite and get work done.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead of direct pings all the time, it's better to take the conversation to a channel as much as possible - it provides visibility, prevents building knowledge silos, and saves another red dot on chat notification. Reserve the need for immediate response only for that is truly urgent. And do not make everything urgent. Reserve channel wide ping(@channel or @here) for rare situations - as this also contributes to those fifty red dots on notification.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here is another side effect of using chat improperly. You have realized that no one pays attention to email any more. So, there is a company-wide announcement (or even group wide announcement) that is important - but not urgent. But since you are not sure if most are even going to read that announcement via email, you do company-wide (or group wide) announcement, using "@here" or "@channel". On chat notification, this is just a red dot. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There is not much to differentiate, for a team member, just from notification, if amongst the fifty red dots they see, is an important company announcement. Or another direct ping from co-worker. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Considering how many channels have been created, it makes it even more difficult to traverse this. There is nothing wrong in over communicating that is important and may be via multiple communication medium. However, when team members are already tired of those red dots on their chat notification, as well as gazillion channels, they are in, such announcements will lose the importance they deserve.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead, it is better to differentiate between urgent and important. Maybe use different mechanism and frequency for communicating the important. We know chat is good for urgent. If you use chat for all important communication as well as for social, casual conversations - folks cannot differentiate when it is casual and when important. They just need to be on top of chat all the time.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Tip #2: Train the Leaders on how to communicate effectively in the new work environment&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I would start with what is the most key area that needs to be addressed - and that would be the manager's communication style. If a manager's primary style of working is to get work done primarily via inefficient meetings and excessive direct chat pings, and that is not addressed first, everything else will fail. If a manager is constantly doing direct pings on their team members or others, team members feel the need to respond to chat right away - and they resort to the same behavior themselves. And that is how we get fifty unaddressed notifications all the time. &amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Tip #3:&amp;nbsp; Clear guidelines on what communication mechanism to use for what style of communication and when&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;There needs to be a difference between urgent and important. When everything is urgent all the time, then truly urgent is difficult to convey.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Anything that is important should be first given some thought and circulated via email or another asynchronous method - where the expectation from receiver is to not respond right away. This is the reason email and other asynchronous methods should be encouraged for important communication. Anything that is important should not be expected to be responded to instantly and using tiny exchanges typical of a chat.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Another reason email and asynchronous methods shine for important communications is that they have a subject line, which tells you what the topic of communication is (unlike chat). You can filter on all different criteria to arrange the communication you like, as well as flag it for importance, follow-up, or junk. And search it easily ...or forward these to others - with whole context. Good luck trying this with chat.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;strong&gt;&lt;br /&gt;Tip #4:&amp;nbsp; Be miserly with meetings and email to large audiences, use it carefully - as you would do with chat.&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;While need to keep everyone updated is important, it is counter-productive to start with an email or a meeting invite to 30+ people. Start small first with key stakeholders. And bring people in at the right time, once some basic consensus has been reached. Otherwise, it will freak some people out as they may not have context. And many will tune out due to gazillion email flying back and forth in their inbox. And if they are 1 of 30 participants in that meeting when there was hardly any room for most to contribute, they will tune out, chatting away on something else while you see them via that square in Zoom wondering if they are paying attention. The best virtual (or in person) discussions happen in small groups.&amp;nbsp; As the number of participants increases in a meeting, the engagement level drops. Same is true for email as well. There is a complete set guidelines that can be used to effectively use email as a team - that would be its own writeup though. Here are some quick pointers:&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Know your audience. If sending to executives – keep it less than 10 lines – less is more. If sending to technical folks – provide details, and specifics, instead of being vague. Reduce back and forth by avoiding an open-ended email: Instead of asking when you will be available, include couple of slots, that way there is no back and forth just on that topic&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Choose subject carefully – that is a big factor in people opening/reading(or not) your email&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;If you want response back, always end with a question&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;It is polite to respond, just to the sender, with comments or acknowledgement..but avoid replying all, just to say ‘Thank You”&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;In most situations it is best to not start email or meeting with biggest audience, thinking that this way that everyone will be on same page (you would not do same on chat, would you?).. Once content is solidified, and consensus reached, then it is better to send via one single, wide email out. To everyone.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Tip #5: Get the most out of meetings&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ahead of the meeting, the organizer should have a collaborative document started and shared with the participants. This gives all an opportunity to think about the topic and contribute. The key again here is to create that space and time for team members - to not just write that document but read as well. When most of your day is spent jumping from meeting to another and the rest on catching up to excessive direct chat pings, prepping that document and reading it ahead of the meeting is an unreasonable ask.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;strong&gt;Tip #6: Spend few minutes at the start of the meeting, collectively reviewing that document that was prepared, with camera off&amp;nbsp;&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In virtual environment, where folks have screen fatigue due to staring into Zoom all day long, this can also help everyone in meetings to turn off their cameras for few minutes and focus on that document. Over a day it adds up and helps a lot. Yes, having a camera on during meetings is immensely helpful - the solution is not to have cameras turned off during all meetings but minimize the number of meetings and provide opportunities like this to turn off the camera when you can.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When you are running from meeting to meeting, allocating a few minutes at the start of the meeting to read that document has many benefits. It brings everyone quickly on the same page (because someone already spent time ahead to put context in that document). You just need to read it, and the time has already been carved out as part of the meeting. It by default encourages documenting the meeting, which otherwise would have been merely verbal exchange of words without knowing what came out of the meeting or any follow-ups. Besides for months/years to come, it is available for new team members to glean context from - if there is need to revisit the topic and how decision was made.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Tip #7: Standups are useful...some can be inefficient and ineffective&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Stand-ups are a terrific way to quickly check where the team is and if anyone is blocked by anyone else or could use help. However, if these lack proper participation or turn into 1 hour discussions every other day, then these become counterproductive. Specific issues are better addressed with a separate conversation. And simple updates can be handled via chat.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Tip #8: Train the leaders - yes, this one deserves repetition&lt;/strong&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Leaders set the example by how they work, and not just what they say. If the leader does not make the above-mentioned changes in their communication style, words of sympathy around team member stress carry little weight. They need to use currency of meetings wisely or else risk losing their credibility and effectiveness of the tools.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Any written medium cannot convey the emotional queues that only visuals can provide. And those casual water cooler kinds of discussions are very useful.&amp;nbsp; In fact, that is more of a reason to preserve sanctity of meetings and use these wisely. The idea is not to completely minimize or eliminate these, but how to combine these effectively with other methods.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The primary role of a leader is motivating team members, keeping them engaged and reducing stress for them without compromising productivity. The old days of leader's role as a task-master who gets work done micro-managing via inefficient meetings and excessive direct chat pings are waning. By making the above changes in your communication style, you will reduce team member stress, as well as yours. You will create space to produce more without the need to always work long hours. During certain stages of a project, it is reasonable to pull long hours to close out the project. Those should be exceptions not the norm. Plus, time gained using efficient communication methods can be used towards having more informal 1-on-1s where team members can open up and express their concerns freely...so can you. And for more book clubs, game hours or whatever your team likes doing as a group. You will have more fruitful discussions and interactions happening in these kinds of sessions than is possible in any other forum.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;</description><author>Sunil Jain</author><pubDate>Fri, 03 Sep 2021 21:52:26 GMT</pubDate><guid isPermaLink="true">https://world.hey.com/sunjain/are-you-speaking-to-an-empty-room-err-zoom-75880ffb</guid></item><item><title>Performance improvements in .NET6</title><link>https://nicolaiarocci.com/performance-improvements-in-.net6/</link><description>&lt;p&gt;I&amp;rsquo;m pretty psyched about the upcoming .NET6 release. I&amp;rsquo;ve already &lt;a href="https://nicolaiarocci.com/will-.net-6-minimal-apis-turn-heads/"&gt;touched&lt;/a&gt;
on ASP.NET 6 Minimal APIs. Continuing on the long-established tradition, the
team has also worked hard on the performance side of things. File IO, for
example, is seeing &lt;a href="https://devblogs.microsoft.com/dotnet/file-io-improvements-in-dotnet-6/"&gt;impressive gains&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For .NET 6, we have made FileStream much faster and more reliable, thanks to
an almost entire re-write. For same cases, the async implementation is now
a few times faster! We also recognized the need of having more
high-performance file IO features: concurrent reads and writes,
scatter/gather IO and introduced new APIs for them. TL;DR File I/O is better,
stronger, faster!&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 03 Sep 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/performance-improvements-in-.net6/</guid></item><item><title>Open Property Industry</title><link>https://www.tderflinger.com/en/open-property-industry</link><description>The construction industry's value stands at 11 trillion USD worldwide in 2020. It is an important industry, but it is slow to embrace modern technology. How could the property industry become more open?</description><author>Thoughts by Thomas Derflinger</author><pubDate>Fri, 03 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/open-property-industry</guid></item><item><title>Nextcloud snap finds a new home</title><link>https://kyrofa.com/posts/nextcloud-snap-finds-a-new-home/</link><description>If you&amp;rsquo;re an avid reader of the blog (and come on, who isn&amp;rsquo;t?), you&amp;rsquo;ll know that I&amp;rsquo;ve been maintaining a snap for Nextcloud since Nextcloud came into existence. Originally, though, that snap wasn&amp;rsquo;t called &amp;ldquo;nextcloud&amp;rdquo;, it was called &amp;ldquo;nextcloud-kyrofa&amp;rdquo;. My original goal was that I would bootstrap the snap for them, and then they&amp;rsquo;d take it over when they were ready. I hosted the code as a personal project on GitHub.</description><author>kyrofa's blog</author><pubDate>Fri, 03 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://kyrofa.com/posts/nextcloud-snap-finds-a-new-home/</guid></item><item><title>Pandemics, pension funds, and cheap money</title><link>https://stop.zona-m.net/2021/09/pandemics-pension-funds-and-cheap-money/</link><description>&lt;p&gt;How long can they coexist?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 03 Sep 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/pandemics-pension-funds-and-cheap-money/</guid></item><item><title>Link to https://georgefrancis.dev/writing/crafting-organic-patterns-with-voronoi-tessellations/</title><link>https://qubyte.codes/links/1630576061198</link><description>&lt;p&gt;George does incredible things with Voronoi diagrams. I&amp;#x27;ll read this later to figure out how George does it... &lt;a href="https://qubyte.codes/links/1630576061198"&gt;Crafting Organic Patterns With Voronoi Tessellations - George Francis&lt;/a&gt;&lt;/p&gt;</description><author>Qubyte Codes</author><pubDate>Thu, 02 Sep 2021 12:47:41 GMT</pubDate><guid isPermaLink="true">https://qubyte.codes/links/1630576061198</guid></item><item><title>The Handmaid's Tale</title><link>https://arunmani.in/library/the-handmaids-tale/</link><description>If this is the future, then please don't.</description><author>Arun Mani</author><pubDate>Thu, 02 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/the-handmaids-tale/</guid></item><item><title>Soaring High</title><link>https://www.marginalia.nu/log/18-soaring-high/</link><description>&lt;p&gt;I&amp;rsquo;m currently indexing with my search engine. This isn&amp;rsquo;t an always-on sort of an affair, but rather something I turn on and off as it tends to require at least some degree of babysitting.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also been knocked out by the side-effects of the vaccine shot I got the other day, so it&amp;rsquo;s been mostly hands-off &amp;ldquo;parenting&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;What I&amp;rsquo;m trying to figure out just how far I can take it. I really don&amp;rsquo;t know. I took some backups and just let it do its thing relatively unmonitored.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Thu, 02 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/18-soaring-high/</guid></item><item><title>Weekend Strolls</title><link>https://faingezicht.com/photos/2021/09/02/weekend-strolls/</link><description>After a long hiatus, I'm back with my street photography.</description><author>Avy Faingezicht</author><pubDate>Thu, 02 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/09/02/weekend-strolls/</guid></item><item><title>China's heist, and why it may be good</title><link>https://stop.zona-m.net/2021/09/chinas-heist-and-why-it-may-be-good/</link><description>&lt;p&gt;With any luck, even microprocessors heists may have silver linings.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 02 Sep 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/09/chinas-heist-and-why-it-may-be-good/</guid></item><item><title>Work-from-home is the new BlackBerry</title><link>https://jasoneckert.github.io/myblog/work-from-home-is-the-new-blackberry/</link><description>&lt;p&gt;&lt;img alt="BlackBerry" src="blackberry.jpeg#right" title="BlackBerry" /&gt;&lt;/p&gt;
&lt;p&gt;I vividly remember the first time I was issued a BlackBerry smartphone back in the 2000s for work. I&amp;rsquo;d read emails while in line at the Pita shop during lunch hour, formulating a plan and response before I returned to my desk to reply. For emails that required a quick response, I&amp;rsquo;d hammer out a response on the BlackBerry keyboard while in line. And having access to my Outlook calendar on the BlackBerry meant that I could view, schedule and prioritize my tasks anywhere. There was never any doubt in my mind that it made me far more productive.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Wed, 01 Sep 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/work-from-home-is-the-new-blackberry/</guid></item><item><title>September</title><link>https://gyani.net/blog/september/</link><description>.. another month - 
		  Ok, September is here. 4 more months left. I know I am writing this on the 5th of the month. The last few days have been busy! Retrospective Running: I wanted to run 180k, I ran 182k. ✅ Reading: I wanted to read “Red Mars”, I ended up reading “Attack...</description><author>Gyanendra Mishra</author><pubDate>Tue, 31 Aug 2021 21:30:00 GMT</pubDate><guid isPermaLink="true">https://gyani.net/blog/september/</guid></item><item><title>The madness of strategic desk ornaments</title><link>https://stop.zona-m.net/2021/08/the-madness-of-strategic-desk-ornaments/</link><description>&lt;p&gt;Strategic. Seriously.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 31 Aug 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/the-madness-of-strategic-desk-ornaments/</guid></item><item><title>TIL how to upload files to Gitlab snippets from the CLI</title><link>https://www.zufallsheld.de/2021/08/31/til-how-to-upload-files-to-gitlab-snippets-from-the-cli/</link><description>&lt;p&gt;I recently wanted to share a code-snippet with a colleague. I decided to use Gitlab Snippets for this. However the snippet was more of some hundred lines of code in a text-file which I didn&amp;#8217;t want to simply&amp;nbsp;copy-paste.&lt;/p&gt;
&lt;p&gt;Lucky for me I had &lt;a href="https://python-gitlab.readthedocs.io/en/stable/"&gt;python-gitlab&lt;/a&gt; installed, a Python library …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Tue, 31 Aug 2021 13:58:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/08/31/til-how-to-upload-files-to-gitlab-snippets-from-the-cli/</guid></item><item><title>Azure Video Analyzer: Features and Best Practices</title><link>https://blog.adnansiddiqi.me/azure-video-analyzer-features-and-best-practices/</link><description>&lt;p&gt;What is Azure Video Analyzer? Azure Video Analyzer for Media (also called Video Indexer) is part of Azure Applied AI services. This application is built on Azure Cognitive Services (including Computer Vision, Translator, Face, and Speech) and Azure Media Services. It lets you gather information for your videos through the use of Video Analyzer for Media audio and video models.  To begin gathering insights with Video Analyzer for Media, you must establish an account and upload your videos. Once you have uploaded your videos to this application, it examines both audio and visuals by running AI models. In this sense, Azure Video Analyzer is an alternative to setting up your own machine learning operation for video analysis. Azure Video Analyzer capabilities include: Deep search—improves the search experience of a video library using insights gained from videos Content creation—develop trailers, social media content, highlight reels, or news clips using the insights Video Analyzer Media extracts from your content Monetization—insights can improve a video’s value by driving content and advertising optimization Recommendations—insights from the video may be used to enhance user engagement by featuring relevant or compelling video moments What Insights Does Azure Video Analyzer Provide? Most of the value of Azure Video Analyzer is in the insights it can derive from video content via AI analysis. Here are some of the insights the solution can provide: Face detection—groups and isolates faces that appear in the video Visual text recognition (OCR)—extracts text that is displayed in a visual form in the video Shot detection—decides when a shot alters in the video according to visual cues Keyframe extraction—isolates keyframes that are stable in the video Audio transcription—translates speech to written text in over 50 languages Automatic language detection—identifies the main language that is spoken Two channel processing—auto detects transcripts and unifies them to a single timeline Noise reduction—eliminates noisy recordings and improves poor quality telephony audio Translation—translates any audio transcript into 54 languages Quick Tutorial: Uploading Videos and Deriving Insights A note about pricing: You can start using Video Analyzer for Media with a free account, which offers up to 600 free indexing minutes for web interface users and 2400 free minutes for video API users, for a trial period. There is also a paid version with no limitations, and usage is billed by connecting Azure Media Services to your Azure subscription. Learn more in this in-depth blog post about Azure pricing. You can sign up for a free trial on the official product page. 1. Upload a Video via Web Interface To upload a video to Azure Video Analyzer: Log into the web interface Click on the Upload button to upload your video 3. After you have uploaded your video, the application will begin analyzing and indexing your video. You can view progres.  4. After Video Analyzer for Media has finished analyzing, you will receive an email that will include a link to the video and a concise description on what was identified in your video. For instance, written and spoken words, people, named entities, and topics. 2. View Insights To view the insights derived from your video content: Select a video from the list of uploaded videos and click Play. Summarized insights for the video are shown.  These summarized insights reveal an aggregate reading of the data in the video: keywords, sentiments, and faces. For instance, you can view people’s faces and the time ranges of each face, and the percentage of time the face appears.  3. Search for Moments in Videos This is an example of Azure Video Analyzer’s advanced AI analysis capabilities. To look for an exact moment in a video: Type one or more search keywords and click the green → button to search all the videos in your account’s library.  2. The search results show a list of moments within videos that match your search. Select a result, and the player will take you to that precise moment in the video. 3. You can view and edit each moment by clicking it. You can search, edit, and view video transcripts by selecting the Timeline tab. Considerations When Using Video Analyzer When Uploading Videos Prefer Using a URL and Direct Upload Video Analyzer for Media provides you with the option to upload videos from a URL or to upload files directly as a byte array. The second option does have some limitations.  To start with, it has file size constraints. The byte array file size is restricted to 2 GB, in contrast to the 30-GB upload size restrictions you get when using a URL.  Secondly, there are several problems with the byte array method that can affect upload performance and reliability: Connectivity issues can disrupt the upload Speed of upload will be determined by your bandwidth Lost packets When you use an URL to upload videos, you simply have to provide a path to the media file’s location and the Video Indexer deals with the rest.  Automatic Scaling of Media Reserved Units Azure Video Analyzer for Media allows Reserved Units (MRUs) to be auto-scaled via Azure Media Services (AMS), beginning August 1st 2021. Due to this, you don’t have to organize them via Azure Video Analyze for Media. This may result in a reduction in price, because Azure Video Analyzer resources may be automatically scaled down if you use it less.  Index in Optimal Resolution, not Highest Resolution In most instances, indexing performance shows little difference between 4K videos and HD (720P) videos. In the end, you will receive similar insights with the same level of confidence. The better the quality of the video you upload, the greater the file size, and this results in higher power of computing and greater time required for the video to upload.   For instance, when using the face detection tool, a higher resolution may assist where there are various small yet contextually significant faces. However, this will greatly increase the run time and also cause greater risk of false positives.  You should test your use case locally first to verify that you get the correct results. Upload the identical video in 4K and in 720P and look at the insights you receive side by side. Select the resolution that gives you adequate results with minimal file size. Conclusion In this article I introduced Azure Video Analyzer, a managed AI service that helps you derive insights from your videos, which can help you provide a better user experience to viewers. I showed how to: Upload a video via the Video Analyzer web interface View insights extracted from your video content Search for moments in your videos using the Video Analyzer video indexing engine In addition, I shared a few useful tips on making the best use of Video Analyzer in your organization. I hope this will be of help as you consider automating video analysis to take your video content to the next level.&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/azure-video-analyzer-features-and-best-practices/"&gt;Azure Video Analyzer: Features and Best Practices&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Tue, 31 Aug 2021 11:48:47 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/azure-video-analyzer-features-and-best-practices/</guid></item><item><title>Goodnight Twitter</title><link>https://blog.herlein.com/post/goodnight-twitter/</link><description>&lt;p&gt;Once again, I&amp;rsquo;m the product, being manipulated. This time it&amp;rsquo;s Twitter&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Tue, 31 Aug 2021 11:00:01 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/goodnight-twitter/</guid></item><item><title>TIL how to get a plaintext list of Ansible hosts</title><link>https://www.zufallsheld.de/2021/08/31/til-how-to-get-a-plaintext-list-of-ansible-hosts/</link><description>&lt;p&gt;There was a need to iterate over the hosts in an Ansible inventory-file (don&amp;#8217;t ask). After fiddling around with grep and sed, I found an easier&amp;nbsp;method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible-inventory --list all | jq " ._meta.hostvars| keys[]"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ansible-inventory --list all&lt;/code&gt; lists all hosts including their variables in a nice big json&amp;nbsp;response …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Tue, 31 Aug 2021 10:04:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/08/31/til-how-to-get-a-plaintext-list-of-ansible-hosts/</guid></item><item><title>Book review - Strategic Writing for UX</title><link>https://river.me/blog/book-review-strategic-writing-ux/</link><description>A review of &amp;ldquo;Strategic Writing for UX&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 31 Aug 2021 06:50:07 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-strategic-writing-ux/</guid></item><item><title>Exploring Editors</title><link>https://peterlyons.com/problog/2021/08/exploring-editors/</link><description>&lt;p&gt;Content warning: programmer discussing text editors.&lt;/p&gt;
&lt;p&gt;I have been down a path spurred primarily by 2 events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Development of the Atom editor becoming uncertain after microsoft acquired github and thinking it likely there was no room for both VS Code and Atom, and thus Atom would be sunset&lt;/li&gt;
&lt;li&gt;My rage quitting of workflowy after staring at the damn loading spinner and general disgust/contempt for the web as a platform that takes a todo list app in which my lifetime full data set could fit in memory 100 times over and makes me wait 20 seconds to see the grocery list I was looking at just a moment ago.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I sampled some workflowy alternatives including roam research, nvalt, a vim org-mode plugin, emacs org mode, and spacemacs org mode.&lt;/p&gt;
&lt;p&gt;I eventually concluded &lt;a href="https://orgmode.org/"&gt;emacs org mode&lt;/a&gt; was a suitable replacement for workflowy and fully converted my entire data set. I learned just enough spacemacs/emacs to use org mode with moderate efficiency, but it became clear to me that my go-to editor would still be neovim. I did use a vim org-mode plugin for a few weeks, but its feature set was incredibly paltry and there was no active development, so I went for the real thing.&lt;/p&gt;
&lt;p&gt;My distaste for emacs stems I think primarily from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;terrible chorded keyboard shortcut sequences which the community just cannot stop doubling down on every decade&lt;/li&gt;
&lt;li&gt;Arcane docs and terminology which again the community cannot just admit that for every keyboard with a meta key ever built, there have been like 20,000 keyboards built without a key labeled "meta" and update their stupid confusing nonsense docs.&lt;/li&gt;
&lt;li&gt;Slow startup time which reflects performance as not important and the "emacs is a place you live" approach which does not really provide a good path to gradual adoption.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I doubled down a bit on neovim. I did find &lt;a href="https://www.spacemacs.org/"&gt;spacemacs&lt;/a&gt; overall a breath of fresh air and am now also test driving &lt;a href="https://spacevim.org/"&gt;spacevim&lt;/a&gt;, but it's docs are also a bit of a struggle for me to use.&lt;/p&gt;
&lt;p&gt;I'm also watching &lt;a href="https://github.com/vhyrro/neorg"&gt;neorg&lt;/a&gt; as an update on the org mode file format with a real grammar that programs other than emacs can reasonably read and manipulate. Doesn't quite look ready to try yet but hopefully soon.&lt;/p&gt;
&lt;p&gt;In my editor experimentation I was aware of &lt;a href="https://kakoune.org/"&gt;kakoune&lt;/a&gt; and thought about taking it for a spin but...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I am very skeptical of viability of a C++ codebase these days as not being an unstable insecure nightmare&lt;/li&gt;
&lt;li&gt;There seems to be fairly good user community interest but the ratio of user requests to maintainer/implementer activity seems way skewed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That led me to my signature line of "found a new version in rust" in &lt;a href="https://helix-editor.com/"&gt;helix&lt;/a&gt;. It seems very nascent still but I'm curious to play around with the multicursor model and am very encourage by on-screen keybinding menus. God bless modern TUI apps that put the stupid keybinds on the damn screen, and hats off to old school ones like the PINE email client and nano text editor that have had on-screen keybinding menus since the beginning.&lt;/p&gt;
&lt;p&gt;So I think I'll keep an eye on helix development and occasionally fire it up to check on progress. I'll try spacevim a little but if I can't grok how to properly customize it for thinks like a plugin to manage quotes/parens, I'll likely abandon it for basic neovim.&lt;/p&gt;</description><author>Pete's Points</author><pubDate>Tue, 31 Aug 2021 02:36:08 GMT</pubDate><guid isPermaLink="true">https://peterlyons.com/problog/2021/08/exploring-editors/</guid></item><item><title>Speaking of electric cars powering the grid...</title><link>https://stop.zona-m.net/2021/08/speaking-of-electric-cars-powering-the-grid.../</link><description>&lt;p&gt;Possible, it&amp;rsquo;s possible. Worthwhile, is another issue.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 31 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/speaking-of-electric-cars-powering-the-grid.../</guid></item><item><title>3 Reasons to Upgrade Git For The First Time Ever</title><link>https://www.swyx.io/upgrade-git</link><description>Relatively new Git features that actually have me excited</description><author>swyx's site RSS Feed</author><pubDate>Tue, 31 Aug 2021 01:29:07 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/upgrade-git</guid></item><item><title>1Gbps@10ms network in a 100 year old house</title><link>https://zdwolfe.medium.com/1gbps-10ms-network-in-a-100-year-old-house-6ae2abbf65bb?source=rss-65d40a7904f------2</link><description>&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1000/0*DGM2V7tC15bM3eO6" /&gt;&lt;figcaption&gt;Photo by &lt;a href="https://unsplash.com/photos/rWgPHUJ4L0E'"&gt;Ralph Kayden&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;This year marks my Seattle home’s 100st birthday! Hurray! 🥳🎉&lt;/p&gt;&lt;p&gt;Moving from a brand new high-rise downtown apartment to a 100-year-old house in the suburbs brought dozens of little challenges.&lt;/p&gt;&lt;p&gt;One of the most interesting was configuring a home network capable of supporting the two nearly-always-on video conferencing feeds of my wife and I as we continue to work remotely through the COVID pandemic and late-night low-latency gaming binges into a house originally built with only knob-and-tube electricity!&lt;/p&gt;&lt;h3&gt;Previous Home Network&lt;/h3&gt;&lt;p&gt;Our previous home network was sweet! An Ethernet connection came up directly from the ISP to an in-apartment closet. From the closet, into a router, then again back into the closet to Ethernet outlets in every room.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/617/1*HGR5hEoG83x9Z-wCTBQbVA.png" /&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/876/1*ZAA5LMfv6qmdrn7Y2f39eA.png" /&gt;&lt;figcaption&gt;Sweet in-apartment network closet with Ethernet directly to our unit, no modem needed.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Spending six to eight hours on video conference calls during the day, watching Twitch streams in the evening and playing latency-sensitive games at night, we both enjoyed the hard-wired connection’s 900Mb up/900Mb down at &amp;lt; 1ms latency.&lt;/p&gt;&lt;h3&gt;Old House — No Ethernet!&lt;/h3&gt;&lt;p&gt;Contrast our previous apartment with our new (to us) 100 year old house: no Ethernet, thick plaster walls extremely effective at blocking an 802.11ac WiFi signal, and only one ISP-active CoAX port which meant limited flexibility.&lt;/p&gt;&lt;p&gt;We came up with a hack to at least get online, but it involved gross latency and 100ft of dusty (?how that happens is beyond me?) Ethernet cable from a local Ace hardware store strewn across the living room and down a hallway for a few weeks while I figured out a better solution. Something like this:&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/781/1*FxqWkxzefuFSTy0sTnMwuA.png" /&gt;&lt;/figure&gt;&lt;p&gt;While we paid for a 1 Gigabit connection from our ISP, only the two hard-wired computers on the first floor were getting anything near that, pulling ~900Mbps down/40Mbps up at 7ms.&lt;/p&gt;&lt;p&gt;The second floor PC and laptop was able to pull 18Mbps at 48ms, just barely enough for a video call, but only if someone else wasn’t using the network to watch TV.&lt;/p&gt;&lt;p&gt;The WiFi signal wasn’t enough to stream 1080p video to our TV or anywhere except standing within 15ft of the router because of plaster walls on the first floor. If I was playing games and the TV was on, both would buffer and drop packets.&lt;/p&gt;&lt;p&gt;Being cable cutters using internet for all entertainment, we needed a better solution.&lt;/p&gt;&lt;h3&gt;WiFi6&lt;/h3&gt;&lt;p&gt;My first thought was to stay wireless in most places of the house and get a fancy WiFi6 mesh network. I was primarily worried about latency while gaming, though, but short of getting a low voltage contractor to hook up Ethernet to the house, it seemed like the only option.&lt;/p&gt;&lt;h3&gt;MoCA&lt;/h3&gt;&lt;p&gt;Luckily, an off-hand comment by my wife wondering if it was possible to have multiple modems, one in each office, led me down a Google rabbit hole to discover tech I hadn’t ever heard of before: MoCA.&lt;/p&gt;&lt;p&gt;MoCA adapters convert CoAX to Ethernet. This could be useful, since even though there was only one live CoAX connection in the house at the moment, by testing the various random CoAX ports and cables in the basement I found that there was dead CoAX lines for TV service that ran to most rooms.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/333/1*bYMW-I4rZ4phZwO4myGgxA.png" /&gt;&lt;figcaption&gt;MoCA gives you a bi-directional CoAX &amp;lt;-&amp;gt; CAT5 connection&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/220/1*GfZp39n_3bs7-LQlHUrmzQ.png" /&gt;&lt;/figure&gt;&lt;p&gt;I was skeptical that the extra hops through a device type with only a few not-so-well-known manufacturers would introduce a lot of latency, but I figured I’d give it a try for the promise of wired internet.&lt;/p&gt;&lt;h3&gt;Solution: Both!&lt;/h3&gt;&lt;p&gt;I opted for a solution with both WiFi6 to penetrate the thick plaster walls from the basement to the second floor, then 3 MoCA adapters for a low-latency wired connection for gaming and video conferencing.&lt;/p&gt;&lt;p&gt;The plan was to move the existing single live CoAX port from the first floor to the basement, terminate there with a modem, then use the other dead CoAX lines the house already had for TV to run an internet signal up to the second floor. It ended up looking something like this:&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/832/1*E5trIgr7vqMRndyCoXhRSw.png" /&gt;&lt;figcaption&gt;Adding three MoCA adapters allowed me to re-use existing CoAX through the house to reach the second floor.&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/333/1*8arb85QVJRtoEB6mJPhLNw.png" /&gt;&lt;figcaption&gt;&lt;a href="https://www.amazon.com/gp/product/B08TDVN14D"&gt;Motorola MM1025 MoCA&lt;/a&gt; Adapter&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;Dry Run&lt;/h3&gt;&lt;p&gt;To check that everything worked well together before disconnecting service, I did a (messy) dry run with some patch cables and was satisfied.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KGBBwuMrZRJ_DkeLvNTDBg.jpeg" /&gt;&lt;/figure&gt;&lt;h3&gt;Installation — Moving the live CoAX port&lt;/h3&gt;&lt;p&gt;One final hurdle was to move the single live CoAX port in the house to a location close to a line that ran to both first and second floor offices.&lt;/p&gt;&lt;p&gt;…which unfortunately involved rooting around in a crawlspace under the house fit for a horror movie opening scene.&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/863/1*dKEvISqWWwI5Cf4TNbQkCA.png" /&gt;&lt;figcaption&gt;Into the murder crawlspace we go!&lt;/figcaption&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Mhvfar3HDNHFt74LH1G7Ng.png" /&gt;&lt;/figure&gt;&lt;p&gt;Crawling around over some leftover junk from a previous tenant’s renovation, I found the disconnected line that runs up through the house using a CoAX continuity tester (&lt;a href="https://www.amazon.com/Continuity-Audible-Klein-Tools-VDV512-007/dp/B07FB2MGDC"&gt;like this&lt;/a&gt;), wired everything up and ran some speed tests.&lt;/p&gt;&lt;h3&gt;Speed Comparison: 4ms wired latency penalty with MoCA&lt;/h3&gt;&lt;p&gt;Before (with no MoCA): 913Mbps@7ms&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/722/1*XoOEfgh_81IgP7t9ZR-43g.png" /&gt;&lt;/figure&gt;&lt;p&gt;After (through MoCA): 916Mbps@11ms:&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/712/1*4HIz5eZsTTweOojap9oGYg.png" /&gt;&lt;/figure&gt;&lt;p&gt;I ran 3 tests before and after, all with consistent results: adding MoCA came at about a 4ms latency penalty, which is way better than I was expecting and still plenty fast for what we need.&lt;/p&gt;&lt;p&gt;Overall I’m really happy with how the network turned out! Would recommend MoCA to anyone as an option to re-use existing CoAX connections for home internet.&lt;/p&gt;&lt;img alt="" height="1" src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=6ae2abbf65bb" width="1" /&gt;</description><author>Stories by Zach Wolfe on Medium</author><pubDate>Mon, 30 Aug 2021 10:23:49 GMT</pubDate><guid isPermaLink="true">https://zdwolfe.medium.com/1gbps-10ms-network-in-a-100-year-old-house-6ae2abbf65bb?source=rss-65d40a7904f------2</guid></item><item><title>How to Review Code Well</title><link>https://denvaar.dev/posts/how_to_review_code.html</link><description>Plain and simple things for team members to follow when reviewing each other's code.</description><author>denvaar's website</author><pubDate>Mon, 30 Aug 2021 08:40:09 GMT</pubDate><guid isPermaLink="true">https://denvaar.dev/posts/how_to_review_code.html</guid></item><item><title>The Self Provisioning Runtime</title><link>https://www.swyx.io/self-provisioning-runtime</link><description>Improvements in DX in both programming languages and cloud infrastructure will eventually converge in a single paradigm, where you truly "just write business logic" and the platform mostly figures out the rest.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 30 Aug 2021 04:02:48 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/self-provisioning-runtime</guid></item><item><title>Resource Bundle Look-ups in Modular Java Applications</title><link>https://www.morling.dev/blog/resource-bundle-lookups-in-modular-java-applications/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_the_resourcebundleprovider_interface"&gt;The &lt;code&gt;ResourceBundleProvider&lt;/code&gt; Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_resource_bundle_providers"&gt;Resource Bundle Providers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_running_on_the_classpath"&gt;Running on the Classpath&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_discussion_and_wrap_up"&gt;Discussion and Wrap-Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The &lt;a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ResourceBundle.html"&gt;&lt;code&gt;ResourceBundle&lt;/code&gt;&lt;/a&gt; class is Java’s workhorse for managing and retrieving locale specific resources,
such as error messages of internationalized applications.
With the advent of the module system in Java 9, specifics around discovering and loading resource bundles have changed quite a bit, in particular when it comes to retrieving resource bundles across the boundaries of named modules.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;In this blog post I’d like to discuss how resource bundles can be used in a multi-module application
(i.e. a "modular monolith") for internationalizing error messages.
The following requirements should be satisified:&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Sun, 29 Aug 2021 19:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/resource-bundle-lookups-in-modular-java-applications/</guid></item><item><title>Taking holidays is important</title><link>https://utf9k.net/blog/taking-holidays/</link><description>Relaxing some of the time is a good idea. Who would have guessed?</description><author>utf9k</author><pubDate>Sun, 29 Aug 2021 12:04:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/taking-holidays/</guid></item><item><title>Docker Learning Resources for Absolute Beginners Programming With Golang</title><link>https://bartlomiejmika.com/posts/2021/docker-learning-resources-for-absolute-beginners-programming-with-golang/</link><description>&lt;p&gt;Recently I was working on an &lt;a href="https://github.com/over55/workery-server"&gt;open-source project&lt;/a&gt; which I wanted to &lt;em&gt;dockerize&lt;/em&gt; - problem is I had no idea experience. In this post I&amp;rsquo;ll list all the resources I used to learn &lt;a href="https://www.docker.com/"&gt;&lt;code&gt;docker&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Sun, 29 Aug 2021 05:50:02 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/docker-learning-resources-for-absolute-beginners-programming-with-golang/</guid></item><item><title>Trust on Cloud AWS S3 Threat Model</title><link>https://blog.cetinich.net/content/2021/2021-trust-on-cloud-s3-aws-threat-model/</link><description>Trust on Cloud have open sourced their AWS S3 threat model, this release
is a great tool to make sense of the “shared responsibility model” and
what “responsibility” means for AWS customers.</description><author>Brent's Blog</author><pubDate>Sun, 29 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.cetinich.net/content/2021/2021-trust-on-cloud-s3-aws-threat-model/</guid></item><item><title>What if the city were bad for brain?</title><link>https://stop.zona-m.net/2021/08/what-if-the-city-were-bad-for-brain/</link><description>&lt;p&gt;It would explain a few things, for sure.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 28 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/what-if-the-city-were-bad-for-brain/</guid></item><item><title>Dima's mom</title><link>https://rybakov.com/blog/dimamama/</link><description>&lt;figure class="full"&gt;



&lt;img alt="" class="lazyload" src="../images/hiq/dimamama.jpg" /&gt;




&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;2007, St.Petersburg, Russia.&lt;/em&gt;&lt;/p&gt;</description><author>Blogs on Michail Rybakov</author><pubDate>Sat, 28 Aug 2021 17:07:46 GMT</pubDate><guid isPermaLink="true">https://rybakov.com/blog/dimamama/</guid></item><item><title>Testing GPU passthrough on AMD Ryzen 7 5700G APU</title><link>https://ounapuu.ee/posts/2021/08/29/testing-gpu-passthrough-on-amd-ryzen-5700g/</link><description>A quick overview of my experience with setting it up on a small PC box and things to consider when attempting this yourself.</description><author>./techtipsy</author><pubDate>Sat, 28 Aug 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/08/29/testing-gpu-passthrough-on-amd-ryzen-5700g/</guid></item><item><title>Book Review: Proud tobea Flyer</title><link>https://nicolaiarocci.com/book-review-proud-tobea-flyer/</link><description>&lt;p&gt;I happened across this book by pure chance. After having ice cream in our
favourite place in Milano Marittima, my wife and I visited a small street
market with all kinds of booths. Of course, there was a used book stand toward
which I immediately gravitated. A quick scan revealed nothing of interest, so
I moved along. But Serena, who arrived at the booth moments after I left, knew
more. A simple, no-frills, cardboard-covered book which carried a giant PROUD
2BEA FLYER title on the spine caught her attention. As she opened it and
flipped through the pages, she immediately knew this was a unique item I would
love to have, so she bought it (for cheap!) along with another one, then let it
slip into a shopping bag, and then reached an unaware me.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 28 Aug 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-proud-tobea-flyer/</guid></item><item><title>A Docker Compose Setup for Clojure/Script Development</title><link>https://blog.janissary.xyz/posts/clojure-docker-compose</link><author>janissary</author><pubDate>Sat, 28 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.janissary.xyz/posts/clojure-docker-compose</guid></item><item><title>Git Isn't A Web Service</title><link>https://www.marginalia.nu/log/17-git-isnt-a-web-service/</link><description>&lt;p&gt;This an expansion on a comment I left on Lettuce&amp;rsquo;s gemlog post, &amp;ldquo;Personal Experiences and Opinions on Version Control Software&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve seen similar questions posed several times recently, in essence people searching for a good git provider.&lt;/p&gt;
&lt;p&gt;The thing is you don&amp;rsquo;t need a git provider. Git is a shell command, and you can host a server yourself with almost no extra work. You can even host it off a system you don&amp;rsquo;t have administrative access to.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sat, 28 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/17-git-isnt-a-web-service/</guid></item><item><title>React Native Audio Recording App</title><link>https://www.tderflinger.com/en/react-native-audio-recording-flask</link><description>Often you need to record audio on a mobile device and send it to
a server for further processing. In this article, I show you how to do that with React Native and Flask.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Sat, 28 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/react-native-audio-recording-flask</guid></item><item><title>The next cyber attack that will hit Italy was just announced</title><link>https://stop.zona-m.net/2021/08/the-next-cyber-attack-that-will-hit-italy-was-just-announced/</link><description>&lt;p&gt;Not explicitly, of course. But it WAS officially announced.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 28 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/the-next-cyber-attack-that-will-hit-italy-was-just-announced/</guid></item><item><title>How to read Windows-1252 encoded files with .NETCore and .NET5+</title><link>https://nicolaiarocci.com/how-to-read-windows-1252-encoded-files-with-.netcore-and-.net5-/</link><description>&lt;p&gt;Another day, another lesson learned: modern .NET does not support the
Windows-1252 encoding out of the box. Today my colleague was happily porting
a legacy NET4+ app to NET6. As usual, the port was super-easy; it would compile
and run just fine, so he was surprised when the app crashed reading a few
specific XML files. That&amp;rsquo;s when I was called in. A closer inspection revealed
a pattern: all those crashing files were Windows 1252-encoded (the rest, a vast
majority, were UTF-8.)&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 27 Aug 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/how-to-read-windows-1252-encoded-files-with-.netcore-and-.net5-/</guid></item><item><title>Cursed Motivation</title><link>https://www.marginalia.nu/log/16-cursed-motivation/</link><description>&lt;p&gt;A question I often see asked is one along the lines of&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt;How do I motivate myself do (something)
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;hellip; where something may be eat healthier, go to the gym, work on some project, study hard, &amp;amp;c.&lt;/p&gt;
&lt;p&gt;This idea of motivation is interesting. I think it in part comes from the school system, where teachers and parents often talk about motivating the children to study, perhaps with some sort of reward system. I haven&amp;rsquo;t been able to pinpoint exactly who introduced the idea, but my hunch is based on never seeing the particular usage of the word in a book printed before the late 20th century.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 27 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/16-cursed-motivation/</guid></item><item><title>Dreams by Langston Hughes</title><link>https://ho.dges.online/words/commonplace/dreams-by-langston-hughes/</link><description>&lt;p&gt;Hold fast to dreams&lt;br /&gt;
For if dreams die&lt;br /&gt;
Life is a broken-winged bird&lt;br /&gt;
That cannot fly.&lt;br /&gt;
Hold fast to dreams&lt;br /&gt;
For when dreams go&lt;br /&gt;
Life is a barren field&lt;br /&gt;
Frozen with snow.&lt;/p&gt;</description><author>ho.dges.online</author><pubDate>Fri, 27 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/dreams-by-langston-hughes/</guid></item><item><title>Herding Gits</title><link>https://nathanfriend.com/2021/08/26/herding-gits.html</link><description>Juggling multiple Git identities can be tricky.</description><author>Nathan Friend</author><pubDate>Fri, 27 Aug 2021 02:42:44 GMT</pubDate><guid isPermaLink="true">https://nathanfriend.com/2021/08/26/herding-gits.html</guid></item><item><title>The Kingpin of Shanghai</title><link>https://www.damninteresting.com/the-kingpin-of-shanghai/</link><description>&lt;p&gt;&lt;img alt="" height="264" src="https://www.damninteresting.com/wp-content/uploads/2021/08/cover-003b-400x264.jpg" style="width: 400px;" title="" width="400" /&gt;Respectable heads of state rarely admit to keeping company with gangsters. But in April 1927, about 15 years after the collapse of the last imperial dynasty, Chiang Kai-shek and China were at a crossroads. Chiang had followed a murky path to leadership of the Chinese Nationalist Party, the Kuomintang. Although the Kuomintang was rivaled by an assortment of warlords who ruled the provinces as their personal fiefdoms, in Chiang&amp;#8217;s mind the greatest obstacle between him and control of that vast and war-torn country was a young Communist Party which, he believed, would soon be nothing but lethal trouble.&lt;/p&gt;
&lt;p&gt;So generalissimo Chiang turned to Du Yuesheng of the infamous Green Gang of Shanghai, a criminal brotherhood rooted in equal parts menace and murk. Du was the leader of this criminal enterprise, and the bloated, gleaming international city lived and died by his word. It was the power of death which most interested Chiang that spring. He wanted nothing less than complete power over all of China, and to get it, he was willing to trade the lives of thousands and allow the establishment of a vast narcotics empire. Others might have balked at trading the murder of a few thousand political opponents for this goal, but neither Du nor Chiang felt any such hesitation.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.damninteresting.com/the-kingpin-of-shanghai/"&gt;Continue reading &amp;#x25b6;&lt;/a&gt;&lt;/p&gt;</description><author>Damn Interesting</author><pubDate>Thu, 26 Aug 2021 21:40:55 GMT</pubDate><guid isPermaLink="true">https://www.damninteresting.com/the-kingpin-of-shanghai/</guid></item><item><title>Gadget - Copy search results</title><link>https://river.me/blog/copy-search/</link><description>Similar to the copy category members gadget, but for search results - and more!</description><author>River Writes - A MediaWiki Blog</author><pubDate>Thu, 26 Aug 2021 16:33:46 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/copy-search/</guid></item><item><title>Data shows the true productivity GAIN of the city exodus</title><link>https://stop.zona-m.net/2021/08/data-shows-the-true-productivity-gain-of-the-city-exodus/</link><description>&lt;p&gt;What is productivity, again?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 26 Aug 2021 11:30:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/data-shows-the-true-productivity-gain-of-the-city-exodus/</guid></item><item><title>Writing a simple JSON library from scratch: a tour through modern C++</title><link>http://notes.eatonphil.com/writing-a-simple-json-library-in-modern-cpp.html</link><description>&lt;p&gt;Modern C++ has a lot of cool features. Move semantics means passing
around structs in functions is cheap. &lt;code&gt;std::shared_ptr&lt;/code&gt;
means I don't have to manage any memory; no
more &lt;code&gt;new&lt;/code&gt;/&lt;code&gt;delete&lt;/code&gt;! (But try as I might to
understand &lt;code&gt;std::unique_ptr&lt;/code&gt;, I'm just not there yet.)&lt;/p&gt;
&lt;p&gt;The syntax has also gotten some treatment with &lt;code&gt;auto&lt;/code&gt; and
tuple destructuring.&lt;/p&gt;
&lt;p&gt;In order to test out this modern C++ I wanted a small but meaningful
project that operates on very dynamic data. The two that always come
to mind are JSON parsers or Lisp interpreters.&lt;/p&gt;
&lt;p&gt;This post walks through
writing a basic JSON library from scratch using only the standard
library. The source code for the resulting library is available &lt;a href="https://github.com/eatonphil/cpp-json"&gt;on
Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The biggest simplification we'll make is that rather than full JSON
numbers, we'll only allow integers.&lt;/p&gt;
&lt;p class="note"&gt;
  Big caveat! I couldn't be farther from a C++ expert! Email or tweet
  me as you see mistakes, madness, lies.
&lt;/p&gt;&lt;h3 id="api"&gt;API&lt;/h3&gt;&lt;p&gt;The two big parts of the API will be about lexing (turning a string
into an array of tokens) and parsing (turning an array of tokens into
a JSON object-tree). A better implementation would implement the lexer
as taking a character stream rather than a string, but taking a string
is simpler. So we'll stick with that.&lt;/p&gt;
&lt;p&gt;Both of these functions can fail so we'll return a tuple in both cases
with a string containing a possibly blank error message.&lt;/p&gt;
&lt;p&gt;We will define the header in &lt;code&gt;./include/json.hpp&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#ifndef JSON_H&lt;/span&gt;
&lt;span class="cp"&gt;#define JSON_H&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;tuple&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// namespace json&lt;/span&gt;

&lt;span class="cp"&gt;#endif&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The token returned by &lt;code&gt;lex&lt;/code&gt; will need to contain the
token's string value, the location (offset) in the original source, a
pointer to the full source (for debugging), and the token's type. The
token type itself will be an enum of either string, number, syntax
(colon, bracket, etc.), boolean, or null.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;memory&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;JSONTokenType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;JSONToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;full_source&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// namespace json&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the only place in the entire code we'll pass around a
pointer. Using &lt;code&gt;std::shared_ptr&lt;/code&gt; means we don't have to do
any manual memory management either. No &lt;code&gt;new&lt;/code&gt; or
&lt;code&gt;delete&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, &lt;code&gt;JSONValue&lt;/code&gt; is a struct containing optional string,
boolean, number, array, and object fields with a type num to
differentiate.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;map&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;optional&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;JSONValueType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;JSONValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;JSONTokenType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thanks to &lt;code&gt;std::optional&lt;/code&gt; we can avoid using pointers to
describe these fields. I did take a look at &lt;code&gt;std::variant&lt;/code&gt;
but it seemed like its API was overly complex.&lt;/p&gt;
&lt;p&gt;Finally, we'll add two more functions: a high level &lt;code&gt;parse&lt;/code&gt;
function that combines the job of lexing and parsing, and a
&lt;code&gt;deparse&lt;/code&gt; function for printing a &lt;code&gt;JSONValue&lt;/code&gt; as
a JSON string.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// namespace json&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we're ready to start on the implementation.&lt;/p&gt;
&lt;h3 id="lexing"&gt;Lexing&lt;/h3&gt;&lt;p&gt;First up is lexing; turning a JSON string into an array of tokens: a
number, string, null keyword, boolean keyword, or syntax like comma or
colon.&lt;/p&gt;
&lt;p&gt;The main lex loop skips whitespace and calls helper functions for each
kind of token. If a token is found, we accumulate it and move to the
end of that token (some tokens like &lt;code&gt;:&lt;/code&gt; are a single
character, some tokens like &lt;code&gt;"my great string"&lt;/code&gt; are
multiple characters.)&lt;/p&gt;
&lt;p&gt;Each token we find gets a pointer to the original JSON source for use
in error messages if parsing fails. Again this will be the only time
we explicitly pass around pointers in this implementation. We don't do
any manual management because we're going to use
&lt;code&gt;std::shared_ptr&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;json.hpp&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// All tokens will embed a pointer to the raw JSON for debugging purposes&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generic_lexers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lex_syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_false&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Skip past whitespace&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_whitespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generic_lexers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Error while lexing, return early&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Store reference to the original source&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_copy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;format_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unable to lex&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// namespace json&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Two neat things you'll notice in there are tuple literal syntax
(&lt;code&gt;{tokens, ""}&lt;/code&gt;) and how easy it is to type a value
containing an array of function pointers using auto
(&lt;code&gt;generic_lexers&lt;/code&gt;).&lt;/p&gt;
&lt;h4 id="format_error"&gt;format_error&lt;/h4&gt;&lt;p&gt;Since we referenced &lt;code&gt;format_error&lt;/code&gt;, let's define it. This
needs to accept a message prefix, the full JSON string, and the index
offset where the error should point to.&lt;/p&gt;
&lt;p&gt;Inside the function we'll iterate over the string until we find the
entire line containing this index offset. We'll display that line and
a pointer to the character that is causing/starting the error.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;sstream&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;format_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ostringstream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lastline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\n'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;lastline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\t'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;lastline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;lastline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Continue accumulating the lastline for debugging&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'\n'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;lastline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot; at line &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, column &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lastline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;^&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;printf&lt;/code&gt; API is annoying and Clang 12 (latest Clang on
latest Fedora) doesn't seem to support &lt;code&gt;std::format&lt;/code&gt;. So we
just use
&lt;code&gt;std::sstream&lt;/code&gt; to do string "formatting".&lt;/p&gt;
&lt;p&gt;But ok, back to lexing! Next up: whitespace.&lt;/p&gt;
&lt;h4 id="lex_whitespace"&gt;lex_whitespace&lt;/h4&gt;&lt;p&gt;This function's job is to skip past whitespace. Thankfully we've got
&lt;code&gt;std::isspace&lt;/code&gt; to help.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;lex_whitespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;isspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's very simple!&lt;/p&gt;
&lt;h4 id="lex_syntax"&gt;lex_syntax&lt;/h4&gt;&lt;p&gt;All of the generic lexers follow the same pattern. They return either
a valid token and the index where the token ends, or they return an
error string.&lt;/p&gt;
&lt;p&gt;Since all the syntax elements in JSON (&lt;code&gt;,&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;,
&lt;code&gt;{&lt;/code&gt;, &lt;code&gt;}&lt;/code&gt;, &lt;code&gt;[&lt;/code&gt; and , &lt;code&gt;]&lt;/code&gt;)
are single characters, we don't need to write a "longest substring"
helper function. We simply check if the current character is one of
these characters and return a syntax token if so.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_syntax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'['&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;']'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'{'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;':'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="lex_string"&gt;lex_string&lt;/h3&gt;&lt;p&gt;This one manages state so it's a little more complex. We need to check
if the current character is a double quote, then iterate over
characters until we find the ending quote.&lt;/p&gt;
&lt;p&gt;It's possible to hit EOF here so we need to handle that case. And
handling nested quotes is left as an exercise for the reader. :)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                   &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// TODO: handle nested quotes&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'&amp;quot;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;format_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unexpected EOF while lexing string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nothing too special to discuss here. So on to lexing numbers.&lt;/p&gt;
&lt;h3 id="lex_number"&gt;lex_number&lt;/h3&gt;&lt;p&gt;Since we're only supporting integers, this one has no internal
state. We check characters until we stop seeing digits.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                   &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// TODO: handle not just integers&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'0'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;'9'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Done. On to keywords: &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="lex_keyword"&gt;lex_keyword&lt;/h3&gt;&lt;p&gt;This is a helper function that will check for a literal keyword.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                    &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;original_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this defined we can now implement &lt;code&gt;lex_false&lt;/code&gt;,
&lt;code&gt;lex_true&lt;/code&gt;, and &lt;code&gt;lex_null&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                 &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;null&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_true&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                 &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_false&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lex_keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's it for lexing! And although we defined all of these
top-down, you'll want to write them mostly in reverse order or put in
forward declarations.&lt;/p&gt;
&lt;p&gt;If you wanted to you could now write a simple &lt;code&gt;main.cpp&lt;/code&gt;
like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;json.hpp&amp;quot;&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected JSON input argument to parse&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]};&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Set up a Makefile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;*.&lt;span class="n"&gt;cpp&lt;/span&gt; ./&lt;span class="n"&gt;include&lt;/span&gt;/*.&lt;span class="n"&gt;hpp&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;clang++&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;-Wall&lt;span class="w"&gt; &lt;/span&gt;-std&lt;span class="o"&gt;=&lt;/span&gt;c++2a&lt;span class="w"&gt; &lt;/span&gt;-I./include&lt;span class="w"&gt; &lt;/span&gt;*.cpp&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Build with &lt;code&gt;make&lt;/code&gt; and run &lt;code&gt;./main '{"a": 1}'&lt;/code&gt;
to see the list of tokens printed out.&lt;/p&gt;
&lt;p&gt;Now let's move on to parsing from the array of tokens.&lt;/p&gt;
&lt;h3 id="parsing"&gt;Parsing&lt;/h3&gt;&lt;p&gt;This process takes the array of tokens and turns them into a tree
structure. The tree develops children as we spot &lt;code&gt;[&lt;/code&gt; or
&lt;code&gt;{&lt;/code&gt; tokens. The tree child ends when we spot &lt;code&gt;]&lt;/code&gt;
or &lt;code&gt;}&lt;/code&gt; tokens.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                              &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;stod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;[&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Failed to parse&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This in turn reference &lt;code&gt;format_parse_error&lt;/code&gt; on failure
which is an error-string-maker similar to
&lt;code&gt;format_error&lt;/code&gt;. It actually calls &lt;code&gt;format_error&lt;/code&gt;
with more details specific to parsing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;JSONTokenType_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jtt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jtt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;String&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Number&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Syntax&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Boolean&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Null&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ostringstream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unexpected token '&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;', type '&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;', index &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;format_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p class="note"&gt;
  This function depended on a helper for turning the
  &lt;code&gt;JSONTokenType&lt;/code&gt; enum into a string. As a user it's very
  annoying when langauges doesn't give you stringifier methods for enums
  by default for debugging. I know there's some ways to do this with
  reflection in C++ but it seemed hairy.

  But I digest.
&lt;/p&gt;&lt;h4 id="parse_array"&gt;parse_array&lt;/h4&gt;&lt;p&gt;This function was called by &lt;code&gt;parse&lt;/code&gt; when we found an
opening bracket. This function needs to recursively call parse and
then check for a comma and call parse again ... until it finds the
closing bracket.&lt;/p&gt;
&lt;p&gt;It will fail if it every finds something other than a comma or closing
bracket following a succesful call to &lt;code&gt;parse&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;parse_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected comma after element in array&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Unexpected EOF while parsing array&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally we need to implement &lt;code&gt;parse_object&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="parse_object"&gt;parse_object&lt;/h4&gt;&lt;p&gt;This function is similar to &lt;code&gt;parse_array&lt;/code&gt; but it needs to
find &lt;code&gt;$string COLON $parse() COMMA&lt;/code&gt; pattern pairs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;parse_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONToken&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected comma after element in object&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected key-value pair or closing brace in object&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected string key in object&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONTokenType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="n"&gt;format_parse_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected colon after key in object&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_index1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These parse functions are all slightly tedious but still very
simple. And thankfully, we're done!&lt;/p&gt;
&lt;p&gt;We can now implement the variation of &lt;code&gt;parse&lt;/code&gt; that ties
together lexing and parsing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we're completely done the string to &lt;code&gt;JSONValue&lt;/code&gt; code.&lt;/p&gt;
&lt;h3 id="deparse"&gt;deparse&lt;/h3&gt;&lt;p&gt;The very last piece of the implementation is to do the reverse of the
past operations: generate a string from a &lt;code&gt;JSONValue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is a recursive function and the only mildly tricky part is
deciding how to do whitespace if we want a prettier output.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSONValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;null&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;[&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;JSONValueType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;: &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Done. Done. Done.&lt;/p&gt;
&lt;h3 id="main.cpp"&gt;main.cpp&lt;/h3&gt;&lt;p&gt;This program will simply accept a JSON input, parse it, and pretty
print it right back out. Kind of like a simplified &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;json.hpp&amp;quot;&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Expected JSON input argument to parse&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]};&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cerr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;deparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Build it with &lt;code&gt;make&lt;/code&gt; that we already defined, and run it against
something big like
&lt;a href="https://github.com/eatonphil/cpp-json/blob/main/test/glossary.json"&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cpp-json
$&lt;span class="w"&gt; &lt;/span&gt;make
$&lt;span class="w"&gt; &lt;/span&gt;./main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;./test/glossary.json&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;glossary&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossDiv&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossList&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossEntry&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Abbrev&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ISO 8879:1986&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Acronym&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SGML&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossDef&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossSeeAlso&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GML&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;XML&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;para&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A meta-markup language, used to create markup languages such as DocBook.&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossSee&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;markup&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GlossTerm&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Standard Generalized Markup Language&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ID&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SGML&amp;quot;&lt;/span&gt;,
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SortAs&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SGML&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;S&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example glossary&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or something incorrect like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{&amp;quot;foo&amp;quot;: [{ 1: 2 }]}'&lt;/span&gt;
Unexpected&lt;span class="w"&gt; &lt;/span&gt;token&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'1'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Number'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;index
Expected&lt;span class="w"&gt; &lt;/span&gt;string&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;object&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;column&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;}]}&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;^
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And give Valgrind the old try:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;valgrind&lt;span class="w"&gt; &lt;/span&gt;./main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{&amp;quot;a&amp;quot;: [1, 2, null, { &amp;quot;c&amp;quot;: 129 }]}'&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Memcheck,&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;memory&lt;span class="w"&gt; &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;detector&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2002&lt;/span&gt;-2017,&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;GNU&lt;span class="w"&gt; &lt;/span&gt;GPL&lt;span class="err"&gt;'&lt;/span&gt;d,&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;Julian&lt;span class="w"&gt; &lt;/span&gt;Seward&lt;span class="w"&gt; &lt;/span&gt;et&lt;span class="w"&gt; &lt;/span&gt;al.
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Using&lt;span class="w"&gt; &lt;/span&gt;Valgrind-3.17.0&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;LibVEX&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rerun&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;info&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Command:&lt;span class="w"&gt; &lt;/span&gt;./main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;:&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="se"&gt;\ &lt;/span&gt;null,&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;c&amp;quot;&lt;/span&gt;:&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="m"&gt;129&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;}]}&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.000000,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.000000,
&lt;span class="w"&gt;    &lt;/span&gt;null,
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;c&amp;quot;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;129&lt;/span&gt;.000000
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;HEAP&lt;span class="w"&gt; &lt;/span&gt;SUMMARY:
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;use&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;exit:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bytes&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;blocks&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;total&lt;span class="w"&gt; &lt;/span&gt;heap&lt;span class="w"&gt; &lt;/span&gt;usage:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;allocs,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;frees,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;105&lt;/span&gt;,386&lt;span class="w"&gt; &lt;/span&gt;bytes&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;allocated&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;All&lt;span class="w"&gt; &lt;/span&gt;heap&lt;span class="w"&gt; &lt;/span&gt;blocks&lt;span class="w"&gt; &lt;/span&gt;were&lt;span class="w"&gt; &lt;/span&gt;freed&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;no&lt;span class="w"&gt; &lt;/span&gt;leaks&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;possible&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;For&lt;span class="w"&gt; &lt;/span&gt;lists&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;detected&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;suppressed&lt;span class="w"&gt; &lt;/span&gt;errors,&lt;span class="w"&gt; &lt;/span&gt;rerun&lt;span class="w"&gt; &lt;/span&gt;with:&lt;span class="w"&gt; &lt;/span&gt;-s
&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nv"&gt;153027&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ERROR&lt;span class="w"&gt; &lt;/span&gt;SUMMARY:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;errors&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;contexts&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;suppressed:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pretty sweet. Modern C++, I like it!&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;I don't do a lot of C++ so I wanted to get a sense for what it can look like today.&lt;br /&gt;&lt;br /&gt;This post walks through a number of new-ish C++ features as we build a handwritten recursive descent parser for JSON using only the standard library.&lt;a href="https://t.co/cCN6nP0pDi"&gt;https://t.co/cCN6nP0pDi&lt;/a&gt; &lt;a href="https://t.co/0AZNEZv4Ss"&gt;pic.twitter.com/0AZNEZv4Ss&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1431000902710796292?ref_src=twsrc%5Etfw"&gt;August 26, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Thu, 26 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/writing-a-simple-json-library-in-modern-cpp.html</guid></item><item><title>Nine posts that changed lives</title><link>https://stop.zona-m.net/2021/08/nine-posts-that-changed-lives/</link><description>&lt;p&gt;Hoping that you too will read them.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 25 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/nine-posts-that-changed-lives/</guid></item><item><title>How to restore a single Postgres database from a pg_dumpall dump</title><link>https://nicolaiarocci.com/how-to-restore-a-single-postgres-database-from-a-pg_dumpall-dump/</link><description>&lt;p&gt;Today I learned how to restore a single Postgres database from a global dump
generated with &lt;a href="https://www.postgresql.org/docs/current/app-pg-dumpall.html"&gt;&lt;code&gt;pg_dumpall&lt;/code&gt;&lt;/a&gt;. Now, &lt;code&gt;pg_dumpall&lt;/code&gt; is handy when you want to back
up an entire Postgres cluster. It will dump all databases and global objects in
a single text file. In contrast, &lt;a href="https://www.postgresql.org/docs/current/app-pgdump.html"&gt;&lt;code&gt;pg_dump&lt;/code&gt;&lt;/a&gt;, the go-to tool for Postgres
backups, offers more control but only works with a single database and doesn&amp;rsquo;t
dump global objects, such as the roles/users linked to the database.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 25 Aug 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/how-to-restore-a-single-postgres-database-from-a-pg_dumpall-dump/</guid></item><item><title>Artificial Intelligence just made Dungeons and Dragons too predictable</title><link>https://stop.zona-m.net/2021/08/artificial-intelligence-just-made-dungeons-and-dragons-too-predictable/</link><description>&lt;p&gt;Almost boring, that is. Without easy way out.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 24 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/artificial-intelligence-just-made-dungeons-and-dragons-too-predictable/</guid></item><item><title>Why I selfhost?</title><link>https://apurva-shukla.me/blog/why-i-selfhost/</link><description>What if I told you there was a way out from having your online movements tracked; precious personal data harvested; and locked in to feeding…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Tue, 24 Aug 2021 07:54:01 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/blog/why-i-selfhost/</guid></item><item><title>It’s not the industry that sucks</title><link>https://nutcroft.mataroa.blog/blog/its-not-the-industry-that-sucks/</link><description>&lt;p&gt;&lt;strong&gt;I.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Technology.&lt;/p&gt;
&lt;p&gt;I love it as essence, as the way of making things that on the surface seem magical. I hate it as an industry, of what “big tech” has come to mean the last decade and how technology companies affect the world.&lt;/p&gt;
&lt;p&gt;I considered these two as separate concepts in my mind, yet as time passes they have converged. How can one love technology if the only way they can interact with it is through companies that not only do not care for societal betterment but also actively contribute to its detriment?&lt;/p&gt;
&lt;p&gt;Google invades our privacy to sell ads. Amazon exploits warehouse workers for next day deliveries. Facebook sells ads to anyone—however malicious—that buys them. The examples are many.&lt;/p&gt;
&lt;p&gt;The majority of my friends in the domain agree. They are sad about their work and our industry. Many are worried that maybe they are actively making the world worse, after all. Most of the time it’s hard to know; things can spiral out of control even if in the beginning everything seems to have the right moral compass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;II.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But I think the problem is not the industry nor technology as essence. It’s the context it exists within. And this context is the same for all industries: our societal and economic system.&lt;/p&gt;
&lt;p&gt;Ask anyone; how do they feel about their industry? The responses I got were almost always negative. Technology? Silicon Valley thinks it’s saving the world. Healthcare? Nurses (and/or doctors) being paid meagre wages while doing an extremely hard and valuable job. Art? Millions of artists unappreciated and unable to live off their work and a small elite controlling the industry. Hospitality? Low wages for tiring and intellectually non-stimulating work. Transportation? Delivery? E-commerce? Energy? I think the reader can fill in the answers from their own experience.&lt;/p&gt;
&lt;p&gt;All of these problems have one holy mantra as common denominator: &lt;em&gt;we have to maximise our profits&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Everything else trickles down from there. Google and Amazon—and every other company—have to increase their profits quarter to quarter, however ludicrous they already are. They have to find that 0.001% optimisation that will save them millions. This could mean shortening a worker’s 23-minute break to 21 minutes, or tracking users in this new, slightly more creepy way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;III.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;These companies did not start evil. They began fresh, as the underdogs on a mission to make the world a better place. Google’s mission was &lt;em&gt;to organize the world’s information and make it universally accessible and useful&lt;/em&gt;. They did pretty well, dare I say.&lt;/p&gt;
&lt;p&gt;&lt;img alt="feel-good-jamie-sm.jpg" src="https://nutcroft.mataroa.blog/images/9ee8154f.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;Amazon’s was to &lt;em&gt;offer customers the lowest possible prices, the best available selection, and the utmost convenience&lt;/em&gt;. Pretty good result for customers. Ordering anything and having it tomorrow on my doorstep is pretty fucking utopian if you ask me, the customer. But what about the workers?&lt;/p&gt;
&lt;p&gt;This doesn’t mean—of course—that this is an argument against progress. I’m only against that which exploits others to achieve its end. In this case we exploit many to have next-day delivery and we even exploit ourselves&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; to have a web search engine and free email. We can progress without exploiting others and we should do that even if it takes more time and effort to do so.&lt;/p&gt;
&lt;p&gt;All this, in the end, seems like an instance of instrumental convergence. Nick Bostrom’s &lt;a href="https://en.wikipedia.org/wiki/Instrumental_convergence#Paperclip_maximizer"&gt;paperclip maximiser&lt;/a&gt; is the thought experiment that comes to mind:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Suppose we have an AI whose only goal is to make as many paper clips as possible. The AI will realize quickly that it would be much better if there were no humans because humans might decide to switch it off. Because if humans do so, there would be fewer paper clips. Also, human bodies contain a lot of atoms that could be made into paper clips. The future that the AI would be trying to gear towards would be one in which there were a lot of paper clips but no humans.&lt;/p&gt;
&lt;p&gt;— Nick Bostrom&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For our socio-economic system, the paperclip is the GDP or a company’s quarterly revenue. Everything at the altar of maximising profits—whatever it is, it’s less important than profit anyway. It &lt;a href="/blog/the-imaginary-as-social-foundation/"&gt;doesn’t have to be&lt;/a&gt; this way, though. We can all agree to designate human wellbeing as the paperclip, rather than GDP or quarterly revenue. Hopefully such a policy change seems more easily achievable than next-day delivery of all items humanity produces.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;We exploit ourselves by allowing ourselves to be spied by relinquishing control of our own data.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>nutcroft</author><pubDate>Tue, 24 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nutcroft.mataroa.blog/blog/its-not-the-industry-that-sucks/</guid></item><item><title>Printer business</title><link>https://yasha.solutions/posts/printers-business/</link><description>We have a bone to pick with the printer industry.
Today, you can litteraly print anything&amp;hellip;
&amp;hellip; as long as it is not on paper.
You can 3D print your own laptop parts or even a 3D chocolate cake.
But If you dare to print a black and white A4, then, it&amp;rsquo;s suddenly it&amp;rsquo;s super high tech&amp;hellip;
Ink Cartridges Are A Scam   An ink cartridge cost about 1$ but is sold up to 100 time its original cost.</description><author>Yasha Solutions</author><pubDate>Tue, 24 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/printers-business/</guid></item><item><title>Good news about women and ID cards in Afghanistan</title><link>https://stop.zona-m.net/2021/08/good-news-about-women-and-id-cards-in-afghanistan/</link><description>&lt;p&gt;&amp;ldquo;Good&amp;rdquo; in the worst way, of course.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 23 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/good-news-about-women-and-id-cards-in-afghanistan/</guid></item><item><title>Interviewing Questions</title><link>https://ztoz.blog/posts/interviewing-questions/</link><description>&lt;p&gt;&lt;a href="https://news.ycombinator.com"&gt;Hacker News&lt;/a&gt; likes to complain about interviewing, and, well, there is a lot to complain about.
One of the recent &lt;a href="https://news.ycombinator.com/item?id=28274310"&gt;links&lt;/a&gt; on the matter was from Kislay Verma
&lt;a href="https://kislayverma.com/organizations/competitive-programming-is-useless/"&gt;&amp;ldquo;Competitive programming is useless&amp;rdquo;&lt;/a&gt;.
Kislay decries an over-emphasis on competitive coding questions informing interview performance &amp;mdash; i.e., asking
increasingly obscure and trivial algorithmic and data structures questions rather than focusing on fundamentals and
questions to engage and measure the breadth of the candidate&amp;rsquo;s experience and talent.&lt;/p&gt;
&lt;p&gt;When I went to college, ACM Programming Competitions were the primary form of competitive competition. In contrast
to today&amp;rsquo;s Hacker Rank and Leetcode, these were team events. Over a period of six hours, a team would share a single
computer and seek to solve the most questions in the least amount of time. Questions were written in a narrative style,
so contestants had to recognize what approach to take; these were not &amp;ldquo;implement methods for a priority queue&amp;rdquo; but solve
a problem where a priority queue is a convenient and appropriate data structure. Questions were graded objectively by
judging software; they were purely about generating the right output (within some processing time limit) rather than about
code quality itself. In the lower competitions, there was usually one question that required graph or dynamic
programming and the other questions were more straight-forward and (in my opinion) could have appeared as homework
for sophomores and juniors. The higher competitions usually required combining multiple algorithms and targeted
those who had mastered a senior-level algorithms course (&lt;a href="https://icpc.global/worldfinals/problems/2018%20ACM-ICPC%20World%20Finals/icpc2018.pdf"&gt;2018 example&lt;/a&gt;).
As I recall, these were usually open-book, although you weren&amp;rsquo;t allowed any digital sources.&lt;/p&gt;
&lt;p&gt;In my experience, these competitions actually encouraged good form. A knowledge of idioms and the standard library
reduced the amount of time writing code due to muscle memory and re-use. Since the team was restricted to a single
computer, some teams used pair programmers or you might have to double up to debug a program, both of which encouraged
good variable naming and functional decomposition.&lt;/p&gt;
&lt;p&gt;That said, I&amp;rsquo;m sympathetic to the notion that tricky algorithmic questions do not yield good signal on candidates.
Instead, I sought signal on how they would handle every day problems.&lt;/p&gt;
&lt;p&gt;At Qualtrics, I&amp;rsquo;ve conducted over 300 interviews with candidates that ranged from interns to experienced (20+ years)
hires. In the early days, I usually asked questions that required dynamic programming, but I eventually switched to questions
drawn from my day-to-day experiences. (The &lt;a href="https://www.qualtrics.com/eng/how-we-interview-in-engineering-at-qualtrics/"&gt;Qualtrics interviewing philosophy&lt;/a&gt;
is to ask &amp;ldquo;straight-forward&amp;rdquo; questions. This does not mean easy questions, but rather questions that do not involve
any tricks or obscure knowledge. The blog post references an example clock-hand question; this question was a typical
starter problem at the lower-level ACM competitions. Personally, I think both of the example questions provide too
few opportunities for signal within a software developer interview.)&lt;/p&gt;
&lt;p&gt;To provide good signal, a problem requires some algorithm applied to a non-trivial data structure or data object. I tried to target problems
that required a screens worth of code in order to provide opportunities for the candidate to show how they would structure
a solution (functional decomposition). As an example, one of my preferred questions required modeling a color value in
rgb space. Since this was an internal data object, candidates had a lot of freedom in their choice. Their choice provided
good signal on how they approached trade-offs between convenience, efficiency, and legibility.&lt;/p&gt;
&lt;p&gt;I ran the interviews as open-book as this was also a source of signal. A typical sign of a poor candidate was using
examples of code and attempting to copy-paste the code into something that worked; positive signal was a candidate
that used the official API documentation and understood it. One candidate provided very positive signal after first
following some code from StackOverflow, pausing, and then discussing why the code was bad and then changing their
approach.&lt;/p&gt;
&lt;p&gt;I preferred problems where the candidate had to fully run their code because it provided signal in how they
debugged problems and dealt with compiler errors.&lt;/p&gt;
&lt;p&gt;Once candidates completed the exercise (I tried to push candidates to a successful completion), I asked them to
perform a code review of their code, and then I asked about the scaling characteristics of the code. I think the
code review is an important part of the process as it 1) allowed candidates to acknowledge and suggest improvements
to any bad code they might have written and 2) reveal signal about what they expect from good code. In some cases,
I had to suggest a change in the code to obtain signal. Most candidates noted a lack of comments in their samples
and thought naming could be improved (although rarely had any specific recommendations). This was weak signal. There
was better signal through discussions of idiomatic compliance, memory lifecycles, and alternative approaches to
data modeling and functional decomposition. Memorably, a few candidates provided excellent signal by describing how
the compiler would interpret various approaches or compared solutions in multiple languages. (The best candidates had
fun with the problem.)&lt;/p&gt;
&lt;p&gt;My intent for asking about the scaling characteristics of their code was not to get a recital of Big-O (although many
candidates provided that data anyway without me prompting) but rather a discussion on what to do if the inputs were
millions or billions larger. I sought two different kinds of signal: 1) at what level of scale do candidates feel they
need to complicate the solution 2) what range of options do candidates consider for scaling? For 1), poor candidates
would reach for a distributed map-reduce solution when the number of inputs was in the hundreds of thousands
(processing time per record was less than a millisecond). Better candidates would probe the use case (turning this coding exercise into a mini design exercise)
or discuss the point where the gain from parallelization exceeded the required overhead. For 2), I found candidates
could rarely differentiate themselves in discussing technical options but the &amp;ldquo;median&amp;rdquo; candidate could suggest one
appropriate strategy or technology.&lt;/p&gt;
&lt;p&gt;Ultimately, the pass-fail was based on a mixture of these various signals, rather than a single measure. For instance,
I passed a number of candidates that failed to pass all tests within the interview time, if there was sufficient
positive signal elsewhere. Good problems provide opportunities to gather a broad range of signal.&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Mon, 23 Aug 2021 19:47:28 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/interviewing-questions/</guid></item><item><title>Quit Your Job Like Andrew Kelley</title><link>http://catswhisker.xyz/log/2021/8/23/quit_your_job_like_andrew_kelley/</link><description>An excerpt from a recent interview with Andrew Kelley, creator of the Zig programming language, about quitting his job.</description><author>The Cat's Whisker</author><pubDate>Mon, 23 Aug 2021 17:11:59 GMT</pubDate><guid isPermaLink="true">http://catswhisker.xyz/log/2021/8/23/quit_your_job_like_andrew_kelley/</guid></item><item><title>Why Isn't Usage Based Billing A Bigger Category?</title><link>https://www.swyx.io/why-usage-billing</link><description>Usage billing is the new hotness for SaaS, and I have personally seen the pain it caused, but I was ultimately scared off from investing in it.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 23 Aug 2021 11:28:32 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/why-usage-billing</guid></item><item><title>Travel is no cure for the mind</title><link>https://nicolaiarocci.com/travel-is-no-cure-for-the-mind/</link><description>&lt;p&gt;I stumbled upon a personal growth article this weekend, and that&amp;rsquo;s odd because
I tend to stay clear from such things. Yet I found it quite relevant, so much
that I thought I would &lt;a href="https://medium.com/personal-growth/travel-is-no-cure-for-the-mind-e449d3109d71"&gt;share it&lt;/a&gt; (the delivery is also amusing, which is
something new for this kind of content).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s just another day… and you’re just doing what you need to do. You’re
getting things done, and the day moves forward in this continuous sequence of
checklists, actions, and respites. But at various moments of your routine,
you pause and take a good look at your surroundings. The scenes of your
everyday life. The blur of this all-too-familiar film. And you can’t help but
to wonder… If there is more to it all. For some reason — this country, this
city, this neighborhood, this particular street — is the place you are living
a majority of your life in. And it is this thought that allows a daydream to
seep in. You start thinking of all the other places you could be in this
world. Or more accurately, all the places you’d rather be in. Somewhere more
exciting. Somewhere new. Somewhere that can provide experiences that are
foreign to you.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 23 Aug 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/travel-is-no-cure-for-the-mind/</guid></item><item><title>Stages of Being</title><link>https://www.marginalia.nu/log/15-stages-of-being/</link><description>&lt;p&gt;@sdfgeoff asked an interesting question on station just a while ago&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt; How much of your lives do you spend living (or watching) someone elses?
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;It reminded me of an interesting tool kit for understanding being.&lt;/p&gt;
&lt;p&gt;The neoplatonists describe a hierarchy of being. This is sometimes attributed to Renaissance enfant terrible Pico della Mirandola&amp;rsquo;s oration of the dignity of man, but it&amp;rsquo;s given the most cursory mention. I&amp;rsquo;ve attached the quote at the bottom of the post. Plato&amp;rsquo;s Republic is probably a better source, even if it doesn&amp;rsquo;t draw up the hierarcy quite in this fashion.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Mon, 23 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/15-stages-of-being/</guid></item><item><title>Stranded • Art</title><link>https://muffinman.io/art/stranded-green-purple/</link><description>&lt;div class="container"&gt;&lt;div class="art-page__content"&gt;&lt;/div&gt;&lt;table class="text-sm art-page__info"&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Created: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;August 2021&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Size: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;30x21cm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Paper: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;Fabriano Bristol 250gsm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Pens: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;Pigma Micron, Sakura Gelly Rolls, Mechanical Pencil&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="art-gallery photoswipe-gallery art-gallery--wide"&gt;&lt;div class="art-gallery__cover"&gt;&lt;a class="gallery__link outline" href="./0.jpg"&gt;&lt;img alt="" class="gallery__image" height="1606" src="./0.jpg" width="2600" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="art-gallery__items"&gt;&lt;a class="gallery__link outline" href="./1.jpg"&gt;&lt;img alt="" class="gallery__image" height="1875" src="./w_500-h_500--1.jpg" width="2500" /&gt;&lt;/a&gt;&lt;a class="gallery__link outline" href="./2.jpg"&gt;&lt;img alt="" class="gallery__image" height="1875" src="./w_500-h_500--2.jpg" width="2500" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;</description><author>Muffin Man</author><pubDate>Mon, 23 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://muffinman.io/art/stranded-green-purple/</guid></item><item><title>The National Broadband Network - A Real Ending</title><link>https://boyter.org/posts/national-broadband-network-a-real-end/</link><description>&lt;p&gt;A little over a year ago I wrote about &lt;a href="https://boyter.org/posts/national-broadband-network-a-massive-dissapointment/"&gt;my experience getting the NBN&lt;/a&gt; and concluding that it was a massive disappointment. I ended with&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Anyway thats the end of it. A 7 years wait for internet that I guess might be the envy of people living in Libya.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Obviously since this post I have something more to say. NBN changed their policy allowing you to get a TCP (technology upgrade) quote without having to pay. Of course I tried it. The dollar amount was not small but with the WFH situation continuing to be a thing, the constant slowdowns I got from FTTN due to I think interference with other devices outside my control, and that I was less than happy with a 75/20 connection I pulled the trigger and started the TCP process.&lt;/p&gt;</description><author>Ben E. C. Boyter</author><pubDate>Mon, 23 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/national-broadband-network-a-real-end/</guid></item><item><title>Rhubarb Ice Cream</title><link>https://dustin.lammiman.ca/icecream/rhubarb/</link><description>&lt;h2 id="rhubarb-ice-cream" tabindex="-1"&gt;Rhubarb Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/rhubarb/#rhubarb-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/rhubarb/#ingredients"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1 1/2 Tbsp Granulated Sugar&lt;/li&gt;
&lt;li&gt;3 Tbsp Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;2/3 Cup Ikea DRYCK RABARBER Rhubarb syrup&lt;/li&gt;
&lt;li&gt;1 Cup Milk (I used 2%)&lt;/li&gt;
&lt;li&gt;2 Tbsp Light Corn Syrup&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/rhubarb/#instructions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;In a large bowl whisk together sugar, milk powder, and xanthan gum.&lt;/li&gt;
&lt;li&gt;Add milk, rhubarb syrup, and corn syrup. Mix very wwell.&lt;/li&gt;
&lt;li&gt;Add cream and mix well again.&lt;/li&gt;
&lt;li&gt;Place mixture in a sealed contained and refridgerate for 12+ hours.&lt;/li&gt;
&lt;li&gt;Churn according to your ice cream maker's instructions. Place in a sealed container and freeze for 4+ hours before serving.&lt;/li&gt;
&lt;/ol&gt;</description><author>Dustin.Lammiman</author><pubDate>Mon, 23 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/icecream/rhubarb/</guid></item><item><title>How to make a website reachable over IPv6 on Linux</title><link>https://hartenfeller.dev/blog/make-website-ipv6-ready</link><description>I made made my website IPv6 ready on Linux in a few simple steps.</description><author>Philipp Hartenfeller Blog RSS Feed</author><pubDate>Mon, 23 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://hartenfeller.dev/blog/make-website-ipv6-ready</guid></item><item><title>Parking in Golden</title><link>https://josh.works/parking-in-golden</link><description>&lt;p&gt;Parking in Golden is broken.&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;deeply&lt;/em&gt; broken parking situation causes vehicle and pedestrian traffic in Golden to break, in the same way that if a machine on a manufacturing line breaks, adjacent components need to stop, or it will also malfunction.&lt;/p&gt;

&lt;p&gt;The topic of parking (at least in the way I’ll be talking about it) cannot be detangled from other components of physical and social characteristics of our lives. The system &lt;em&gt;of&lt;/em&gt; parking, and the system in which parking is only a component, is a complex, reactive system. &lt;sup id="fnref:complex-reactive-situation"&gt;&lt;a class="footnote" href="#fn:complex-reactive-situation" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;As is true for many complex, reactive systems, it’s important to obtain a general understanding of the “ground truth”, and if possible a wide understanding of the topic.&lt;/p&gt;

&lt;p&gt;I’m defining parking as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;That which relates to the storage of vehicles&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it relates to the following “kinds” of parking:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;short-term parking like how you park your private vehicle when you travel to a grocery store or a park, or a nice restaurant in a big city&lt;/li&gt;
  &lt;li&gt;long-term parking like how you might park your vehicle in a garage, driveway, or street, at night or for a day, week, or month&lt;/li&gt;
  &lt;li&gt;commercial parking like how commercial vehicles (semi-trailers, heavy commercial vehicles, light commercial vehicles, company cars, company trucks, &lt;em&gt;delivery vehicles&lt;/em&gt;, and more) fulfill their purpose, be it transporting materials, tools, people, parcels to any specific place for any particular purpose for any particular length of time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parking has different degrees of &lt;em&gt;urgency&lt;/em&gt; across different degrees of &lt;em&gt;time&lt;/em&gt;. More on that later.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;Wait. Who are you? Why are you talking about parking?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m Josh. I live in Golden, Colorado, and my life, and the lives of everyone who lives in Golden, Denver, the front range, a city, the country, and the world, are being ruined by mismanaged parking.&lt;sup id="fnref:more-than-just-parking"&gt;&lt;a class="footnote" href="#fn:more-than-just-parking" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I talk about parking (and other things related to cities) a lot. If you’ve spent any time with me, you’ve probably heard me talk (or rant. Sorry.) about things related to cities. I’m also extremely invested in the city of Golden, and am figuring out how to invest &lt;em&gt;even more&lt;/em&gt; in the city.&lt;/p&gt;

&lt;p&gt;Golden’s mismanagement of parking is ruining the lives of the entire city, and everyone who travels to the city, and affects not just persons who drive, but persons who &lt;em&gt;cannot&lt;/em&gt; drive, even if they wished to, be they too young (children), too elderly, &lt;em&gt;and&lt;/em&gt; persons who would &lt;em&gt;rather not&lt;/em&gt; drive, for any particular reason.&lt;/p&gt;

&lt;p&gt;If you summed up all the people who could fit into each of the above categories, there would be tens of thousands of people who’s wellbeing has been diminished &lt;em&gt;in just the last year&lt;/em&gt; by the City of Golden failing &lt;em&gt;for another year&lt;/em&gt; to manage parking correctly.&lt;/p&gt;

&lt;p&gt;I’ve wanted a spot where I could direct people to get several things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;a broad overview of how parking should be approached (from an academic &amp;amp; operational point of view)&lt;/li&gt;
  &lt;li&gt;a broad overview of evidence that parking is indeed being quite mishandled&lt;/li&gt;
  &lt;li&gt;A few heuristics you can take forward to evaluate the parking situation in your own day-to-day (warning, you might not be able to un-read any of what you’re about to read)&lt;/li&gt;
  &lt;li&gt;an explanation of how to fix it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eventually I’m going to be adding a TON of pictures, videos, timelapses, and more. For now, know that “parking” (that complex, reactive system) is one of the largest differences between the following pictures:&lt;/p&gt;

&lt;p&gt;Compare these two images:&lt;/p&gt;

&lt;p&gt;&lt;img alt="subsidized and ugly" src="/images_2020/subsidized.jpeg" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img alt="illegal but beautiful" src="/images_2020/illegal.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;What’s true in Charleston, South Carolina is true in Golden, Colorado.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id="the-fix-for-parking-problems"&gt;The Fix For Parking Problems&lt;/h2&gt;

&lt;p&gt;These two books contain 100% of the solution to the following problems in Golden:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The parking problems&lt;/li&gt;
  &lt;li&gt;the traffic problems&lt;/li&gt;
  &lt;li&gt;the “unaffordable housing” problems&lt;/li&gt;
  &lt;li&gt;the “unaffordable commercial space” problems&lt;/li&gt;
  &lt;li&gt;the homelessness problems&lt;/li&gt;
  &lt;li&gt;and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img alt="Shoup and Bertraud" src="images/shoup-and-bertaud.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Here’s the summary of &lt;a href="https://www.amazon.com/High-Cost-Free-Parking-Updated/dp/193236496X"&gt;The High Cost of Free Parking&lt;/a&gt;, by Donald Shoup.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Donald Shoup is Distinguished Research Professor in the Department of Urban Planning at the University of California, Los Angeles. His research has focused on how parking policies affect cities, the economy, and the environment.&lt;/p&gt;

  &lt;p&gt;In &lt;em&gt;The High Cost of Free Parking&lt;/em&gt;, published in 2005 and updated in 2011, Shoup recommends that cities should:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;Charge fair market prices for on-street parking&lt;/li&gt;
    &lt;li&gt;Spend the revenue to benefit the metered neighborhoods&lt;/li&gt;
    &lt;li&gt;Remove off-street parking requirements.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id="examining-golden-specific-parking-problems"&gt;Examining Golden-specific parking problems&lt;/h3&gt;

&lt;p&gt;Golden proudly proclaims that it has lots of free parking. Look at Golden in Google Earth. Look at how much parking it has!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://earth.google.com/web/@39.75666271,-105.21921333,1721.32852233a,1534.51802287d,35y,-129.74290813h,57.74636974t,0r"&gt;open google earth (in your web browser) to Golden&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s what you might see:&lt;/p&gt;

&lt;div style="height: 0;"&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Multiple parking garages&lt;/li&gt;
  &lt;li&gt;multiple surface parking lots&lt;/li&gt;
  &lt;li&gt;lots of curb space&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MOST of the time, this parking is empty.&lt;/p&gt;

&lt;p&gt;Then, on weekends, (and often during the week) Golden gets tons of visitors. I love that Golden gets visitors. Except it sucks for everyone (visitor and resident alike) when the parking is a disaster.&lt;/p&gt;

&lt;p&gt;One downtown neighborhood puts “closed to non-resident” signs up, and has employees of a security company sitting in their cars at the vehicle entrances.&lt;/p&gt;

&lt;p&gt;The remaining parking is full. stuffed to the gills.&lt;/p&gt;

&lt;p&gt;Golden’s parking minimums (and all sorts of other anti-economic, anti-poor, originally anti-ethnic-group ‘housing minimum’ standards) kill, before pen hits paper, the concept of cheap housing.&lt;/p&gt;

&lt;p&gt;It’s illegal to build a space for a human &lt;em&gt;without&lt;/em&gt; building a space for a car alongside! Like, you’ll actually go to jail if you tried to do that!&lt;/p&gt;

&lt;p&gt;If we didn’t have parking minimums, we could have small dense development, that’s cheap, &lt;em&gt;and small&lt;/em&gt;, unlike the hulking monstrosities required to house cars.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.sightline.org/2016/09/06/how-seattle-killed-micro-housing/"&gt;How Seattle Killed Micro-Housing: One bad policy at a time, Seattle outlawed a smart, affordable housing option for thousands of its residents.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Golden’s “housing problems” and “parking problems” comes from authoritarian &lt;a href="https://en.wikipedia.org/wiki/High_modernism"&gt;high modernists&lt;/a&gt; who believe that political control of parking and housing is acceptable or adequate. It’s jointly some of ‘the people’ and ‘the planners’ who, if enough of them are pro-authoritarian high modernism, ensure this particular outcome.&lt;/p&gt;

&lt;h2 id="resources-around-the-harms-of-parking-minimums"&gt;Resources around the harms of parking minimums&lt;/h2&gt;

&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Parking minimums, a thread.&lt;br /&gt;&lt;br /&gt;Why? Because this is something many continue to approach from the perspective of &amp;quot;well I drive and so everywhere I go needs to have parking.&amp;quot;&lt;br /&gt;&lt;br /&gt;Warning: This was the subject of my master's thesis, so...&lt;br /&gt;(1/16) &lt;a href="https://t.co/scC4PqW3oD"&gt;pic.twitter.com/scC4PqW3oD&lt;/a&gt;&lt;/p&gt;&amp;mdash; Luke Klipp 🏳️‍🌈 (@lukehklipp) &lt;a href="https://twitter.com/lukehklipp/status/1339385302729691136?ref_src=twsrc%5Etfw"&gt;December 17, 2020&lt;/a&gt;&lt;/blockquote&gt;


&lt;h3 id="further-reading"&gt;Further reading&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.brookings.edu/research/parking-requirements-and-foundations-are-driving-up-the-cost-of-multifamily-housing/"&gt;Parking requirements and foundations are driving up the cost of multifamily housing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.sightline.org/2020/12/14/oregon-big-parking-reform/"&gt;Oregon Just Ended Excessive Parking Mandates On Most Urban Lots&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.cpr.org/2020/12/17/there-are-too-many-unused-parking-spots-near-transit-stations-and-its-pushing-up-rent-rtd-report-says/"&gt;There Are Too Many Unused Parking Spots Near Transit Stations And It’s Pushing Up Rent, RTD Report Says (Colorado Public Radio)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.fastcompany.com/90202222/heres-how-much-space-u-s-cities-waste-on-parking"&gt;Here’s how much space U.S. cities waste on parking&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://parkingmill.com/"&gt;Parkingmill: X-ray vision for parking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo:&lt;/p&gt;

&lt;p&gt;https://twitter.com/josh_works/status/1295034724658737152&lt;/p&gt;
&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:complex-reactive-situation"&gt;
      &lt;p&gt;For a primer on this, read &lt;a href="https://fs.blog/2014/04/mental-model-complex-adaptive-systems/"&gt;An Introduction to Complex Adaptive Systems&lt;/a&gt;.&lt;/p&gt;

      &lt;p&gt;A complex, adaptive/reactive system changes in complicated ways when you chage the inputs. Also, take a look at &lt;a href="https://how.complexsystems.fail/"&gt;How Complex Systems Fail&lt;/a&gt; &lt;a class="reversefootnote" href="#fnref:complex-reactive-situation"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:more-than-just-parking"&gt;
      &lt;p&gt;Parking is just one portion of the problem. A big one. And our broken parking regime isn’t just the &lt;em&gt;cause&lt;/em&gt; of many problems, it’s also a &lt;em&gt;result&lt;/em&gt; of other broken systems. See my &lt;a href="/robert-moses"&gt;explainer about Robert Moses&lt;/a&gt; for more. &lt;a class="reversefootnote" href="#fnref:more-than-just-parking"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Sun, 22 Aug 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/parking-in-golden</guid></item><item><title>Elon Musk’s How To Run A Business</title><link>https://savraj.co/2021/08/22/elon-musk's-how-to-run-a-business.html</link><description>In a recent interview with EverydayAstronaut, Elon Musk revealed his 5-step plan for getting things done. It resonated with me, and I imagine it resonates with many of us in tech. The five points are…</description><pubDate>Sun, 22 Aug 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://savraj.co/2021/08/22/elon-musk's-how-to-run-a-business.html</guid></item><item><title>Everything Is Going To Be All Right by Derek Mahon</title><link>https://ho.dges.online/words/commonplace/everything-is-going-to-be-all-right-by-derek-mahon/</link><description>&lt;p&gt;How should I not be glad to contemplate&lt;br /&gt;
the clouds clearing beyond the dormer window&lt;br /&gt;
and a high tide reflected on the ceiling?&lt;br /&gt;
There will be dying, there will be dying,&lt;br /&gt;
but there is no need to go into that.&lt;br /&gt;
The lines flow from the hand unbidden&lt;br /&gt;
and the hidden source is the watchful heart;&lt;br /&gt;
the sun rises in spite of everything&lt;br /&gt;
and the far cities are beautiful and bright.&lt;br /&gt;
I lie here in a riot of sunlight&lt;br /&gt;
watching the day break and the clouds flying.&lt;br /&gt;
Everything is going to be all right.&lt;/p&gt;</description><author>ho.dges.online</author><pubDate>Sun, 22 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/everything-is-going-to-be-all-right-by-derek-mahon/</guid></item><item><title>Python and combinatory</title><link>https://yasha.solutions/posts/python-and-combinatory-py-oncall-team/</link><description>Problem Find all the combination of two in a team
Solution 1 2 3 4 5 6 7  from itertools import combinations team = ["john", "joe", "bob", "al", "tony", "mark"] comb = combinations(team, 2) for i in list(comb): print(i)   Result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  ('john', 'joe') ('john', 'bob') ('john', 'al') ('john', 'tony') ('john', 'mark') ('joe', 'bob') ('joe', 'al') ('joe', 'tony') ('joe', 'mark') ('bob', 'al') ('bob', 'tony') ('bob', 'mark') ('al', 'tony') ('al', 'mark') ('tony', 'mark')   Let&amp;rsquo;s up the game now we want to arrange the combinations results in a way that none of these guys are on call twice in a row</description><author>Yasha Solutions</author><pubDate>Sun, 22 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/python-and-combinatory-py-oncall-team/</guid></item><item><title>Turning leftover PC parts into a decent gaming PC</title><link>https://ounapuu.ee/posts/2021/08/21/turning-leftover-parts-into-a-decent-gaming-pc/</link><description>Putting the 'reuse' in 'Reduce, reuse, recycle'.</description><author>./techtipsy</author><pubDate>Sat, 21 Aug 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/08/21/turning-leftover-parts-into-a-decent-gaming-pc/</guid></item><item><title>Absurdest Academia (A &amp;amp;lsquo;Darkest Dungeon&amp;amp;rsquo; Parody)</title><link>https://bastian.rieck.me/blog/2021/absurdest_academia/</link><description>&lt;p&gt;Staying in academia requires, among other things, a healthy dose of
humour. Its is therefore not surprising that many academical aspects are
likened to more whimsical pursuits. The great &lt;a href="https://matt.might.net/"&gt;Matt Might&lt;/a&gt;,
for instance, has a roaringly funny series of posts, describing both &lt;a href="https://matt.might.net/articles/nine-kinds-of-students/"&gt;the
nine kinds of students you will meet in the
classroom&lt;/a&gt; and
the &lt;a href="https://matt.might.net/articles/peer-fortress/"&gt;types of reviewers you will face&lt;/a&gt;
in terms of characters from the game &lt;a href="https://www.teamfortress.com/"&gt;Team Fortress 2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While there would be many ways to update such typologies based on other
games,&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; I want to focus on &lt;em&gt;how&lt;/em&gt; we are playing in academia. To wit, I think
that modern research efforts are almost perfectly captured by &lt;a href="https://www.darkestdungeon.com/"&gt;Darkest
Dungeon&lt;/a&gt;, a challenging
&lt;a href="https://en.wikipedia.org/wiki/Roguelike"&gt;&amp;lsquo;roguelike&amp;rsquo;&lt;/a&gt; game about
discovery, stress, and resource management. In this post, I hope to convince you that
Darkest Dungeon can be used to learn valuable lessons about many aspects of research.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
  &lt;figure&gt;&lt;img alt="Darkest Dungeon" src="https://bastian.rieck.me/images/darkest_dungeon_header.png" /&gt;
  &lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;Before we go: I named this post &amp;lsquo;Absurdest Academia&amp;rsquo; not only because of
the nice alliteration but also because certain efforts in academia
appear absurd to outsiders; or maybe they are &lt;em&gt;objectively absurd&lt;/em&gt; and we
just have convinced ourselves that they are not? For instance, I have
written a lengthy post on how &lt;a href="https://bastian.rieck.me/blog/2020/middlemen/"&gt;weird our relationship with for-profit publishing
companies is&lt;/a&gt;, and in every conference cycle,
Twitter provides boundless examples of strange situations in the review
process&amp;hellip; But enough about this&amp;mdash;onwards we go!&lt;/p&gt;
&lt;h1 id="entering-the-dungeon"&gt;Entering the dungeon&lt;/h1&gt;
&lt;p&gt;Even though it features elements of eldritch horror, Darkest Dungeon may
strike you at first as a run-of-the-mill role-playing game: you lead
your party on quests, trying to gain more knowledge and re-establish
your family&amp;rsquo;s estate. However, this is not all there is; the game
quickly exhibits more complexity than many of its genre:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Your heroes don&amp;rsquo;t just have life points, but also a &lt;em&gt;stress&lt;/em&gt; level.
Certain actions&amp;mdash;landing a devastating hit, vanquishing a foe, and
so on&amp;mdash;may &lt;em&gt;decrease&lt;/em&gt; their stress, while others&amp;mdash;activating
a trap or having insufficient light available, for instance&amp;mdash;may
&lt;em&gt;increase&lt;/em&gt; stress, and often quite rapidly so. If their stress
level is too high, you risk losing them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your heroes all have different traits and quirks. A hero might prefer
fighting in a specific area, for instance, or might experience more
stress when fighting against a certain type of enemy. While the game
gives you some control over these traits and quirks in exchange for
in-game currency, which you must first acquire through quests, there
is a random element to this: essentially, you can recruit new heroes
in every round, but it&amp;rsquo;s a &amp;rsquo;take it-or leave it&amp;rsquo; type of
situation (plus, the number of heroes you are allowed to have on
your payroll is limited).
In contrast to other role-playing games, Darkest Dungeon does not offer
you the option to create your own perfect hero.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Expeditions&amp;mdash;quests&amp;mdash;vary in difficulty and rewards, and your
heroes have to be &lt;em&gt;provisioned&lt;/em&gt; accordingly before embarking on their
journey. Provisions, however, cost money. This provides a certain
conundrum: you need to spend money to make money! In addition, any afflictions your
heroes suffer on an adventure &lt;em&gt;remain&lt;/em&gt; with them afterwards. There
is no &amp;lsquo;instant healing&amp;rsquo; available after returning from a quest. Your estate offers
some options for heroes to be cured or reduce their stress level, but
all these options cost money and make your heroes be unavailable for
a time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next to these novel ingredients, there are also the standard ingredients
you might expect: heroes come in multiple classes whose abilities are
ideally very much complementary.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; You have your regular &amp;rsquo;tanks,&amp;rsquo;
i.e. heroes that are primarily about dealing damage to enemies, but
you also have some more &amp;lsquo;supporting&amp;rsquo; characters that can increase the
abilities of their comrades or weaken the enemies.
Here&amp;rsquo;s an example of such a hero, the &amp;lsquo;Jester.&amp;rsquo; Jesters are
one of the most versatile character classes in Darkest Dungeon. They can
change positions within the party easily, being both capable of
&amp;lsquo;buffing&amp;rsquo; your heroes and inflicting damage on the enemy. This
particular hero prefers fighting in the &amp;lsquo;Warrens&amp;rsquo; area, but suffers from
more stress when fighting against eldritch monsters.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
  &lt;figure&gt;&lt;img alt="An example of the 'Jester' class of Darkest Dungeon, highlighting the versatility of character classes in this game." src="https://bastian.rieck.me/images/darkest_dungeon_character_example.png" /&gt;
  &lt;/figure&gt;

&lt;/div&gt;
&lt;h1 id="lessons-to-be-learned"&gt;Lessons to be learned&lt;/h1&gt;
&lt;p&gt;I first played Darkest Dungeon in 2016 while I was writing my
dissertation.&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; Already at this point, it occurred to me that the game had
quite a lot of lessons to teach me, and over the years, I found many
things to add to the list.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The first lesson is that academia&amp;mdash;like life itself&amp;mdash;is
fundamentally about marshalling your resources in a &lt;em&gt;balanced&lt;/em&gt;
fashion. I lost many a party because I wanted the same heroes to grow
too quickly, or because I wanted to beat a certain boss in the game.
Similarly, if you have too many projects at the same time, you may
not make significant progress on any of them.&lt;br /&gt;
&lt;strong&gt;Lesson: take your time and don&amp;rsquo;t overextend yourself.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Another important lesson is that the stress level of your heroes
needs to be managed. Going on too many quests in quick succession is
the fastest way to lose your party. They will either succumb to
stress or to disease. The same is true in academia: especially if you
are in the position to lead teams, you should ensure that not
everyone is &amp;lsquo;on fire&amp;rsquo; or &amp;lsquo;on call&amp;rsquo; all the time.&lt;br /&gt;
&lt;strong&gt;Lesson: resting and reflecting is crucial.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As you progress through different areas of the game, you will also
learn more about which combinations of heroes &amp;lsquo;work&amp;rsquo; and which don&amp;rsquo;t
work. You cannot expect to win against &lt;em&gt;all&lt;/em&gt; foes with the same party of heroes.
Moreover, the best parties are complementary&amp;mdash;a support role is as
important as a damage dealer. This is a valuable lesson for academic projects as well:
certain projects require a certain combination of authors; everyone
needs to contribute so that everyone may win!&lt;br /&gt;
&lt;strong&gt;Lesson: knowledge of self and knowledge of others makes for excellent science.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div style="text-align: center;"&gt;
  &lt;figure&gt;&lt;img alt="An example of a victorious party in Darkest Dungeon, stressing the need to pick your team correctly." src="https://bastian.rieck.me/images/darkest_dungeon_party_example.png" /&gt;
  &lt;/figure&gt;

&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;
&lt;p&gt;A secondary aspect of the previous lesson is that you should also
know which combination of heroes works against which foe. You may
have the best group with highly complementary skills, but it might be
completely ineffective in some environment! The same applies to
your academic projects: writing conference papers, grant proposals,
or white papers&amp;mdash;each specific type of project has different
challenges, necessitating a team that is well-equipped to address
them.&lt;br /&gt;
&lt;strong&gt;Lesson: know the domain and pick your team accordingly.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last lesson, and potentially the hardest one, is about &lt;em&gt;losing&lt;/em&gt;.
Darkest Dungeon is merciless in that regard: you can pick the best
party of your veteran heroes, provision them properly, and you may
nevertheless lose the fight. In mathematical terms, your choices and
preparations are only a necessary condition for victory, but not
a sufficient one. &lt;a href="https://en.wikipedia.org/wiki/Robert_Burns"&gt;Robert
Burns&lt;/a&gt; expresses this
beautifully in his poem &lt;a href="https://en.wikipedia.org/wiki/To_a_Mouse"&gt;To
a Mouse&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The best-laid schemes of mice and men&lt;br /&gt;
Go oft awry,&lt;br /&gt;
And leave us nothing but grief and pain,&lt;br /&gt;
For promised joy!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The same goes for your papers, grants, and other things: you can pick
the best team, write the best text, perform the best experiments&amp;mdash;and
nevertheless face rejection. Darkest Dungeon teaches us that such
setbacks are to be expected and that they are not the end but only
temporary.&lt;br /&gt;
&lt;strong&gt;Lesson: preparation is key, but there&amp;rsquo;s only so much you can do.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The positive aspect of the last lesson is that you should celebrate your
victories, though they may seem trifling to you.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; Quest by quest,
room by room, paper by paper, grant by grant, you will make some
progress. In this, Darkest Dungeon can almost be seen as a metaphor for
anything one might undertake in life. The fact that it teaches you these
lessons in a low-stakes environment is great and, reflecting back on
this game, I gained much more than a few enjoyable hours!&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="not-everything-is-a-metaphor"&gt;Not everything is a metaphor&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Before you go off, let me also briefly outline to what extent this
comparison fails. I do not think that treating academia as
a &amp;lsquo;fight&amp;rsquo; is a good idea. It might help in the short run to consider
every rebuttal a fight against truculent or slothful reviewers, but in
the end, academia is made up of regular people, not sinners and saints.
If we must use warlike language, let us rather consider that academia is
a battle &lt;em&gt;against&lt;/em&gt; ignorance or an epic quest &lt;em&gt;for&lt;/em&gt; knowledge. That,
I believe, will be far better for all of us in the long run.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Nevertheless, ignoring the interpretation of what fights mean in an
academic context, Darkest Dungeon has taught me much. If you only
take one bite-sized quote from it, let it be this one, spoken by the narrator of
the game when you fail in an expedition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You will endure this loss, and learn from it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Good luck on all your expeditions and quests, may they be crowned with
success!&lt;/p&gt;
&lt;small&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: All illustrations are taken from the official &lt;a href="https://www.darkestdungeon.com/contact/"&gt;Darkest
Dungeon Press Kit&lt;/a&gt;. The images
constitute copyrighted material by Red Hook Studios, Inc. This blog post
has no commercial interests and adheres to the official Fan Creation
Usage Policy. If you like my description of the game, consider buying it &lt;a href="https://store.steampowered.com/app/262060/Darkest_Dungeon/"&gt;on
Steam&lt;/a&gt; or
&lt;a href="https://www.gog.com/game/darkest_dungeon"&gt;gog.com&lt;/a&gt;. I am not affiliated
with any of these links and will not gain any money from your purchase.&lt;/p&gt;
&lt;/small&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Some wag on reddit, for instance, created an &lt;a href="https://www.reddit.com/r/ProgrammerHumor/comments/aku0ak/machine_learning_algorithm_alignment_chart"&gt;&amp;lsquo;alignment chart&amp;rsquo; for machine learning algorithms&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I would love to see a post on linking certain classes to
well-known scientists&amp;hellip;&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I am what some people would call a &amp;lsquo;filthy casual&amp;rsquo; player, but
even I could enjoy battling some Lovecraftian horrors after work. One
meta-lesson to be learned here: one does not have to be good at
something to enjoy it. (The comparison to academia is quite obvious
here.)&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Readers familiar with Darkest Dungeon might enjoy the vernacular
I am adopting here. Others might want to check out the game at some
point and re-read the post afterwards.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Even though this would have been a sufficient reward on its own.&amp;#160;&lt;a class="footnote-backref" href="#fnref:5"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Sat, 21 Aug 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/absurdest_academia/</guid></item><item><title>Whomst styles the whostyles?</title><link>http://evantravers.com/articles/2021/08/21/whomst-styles-the-whostyles/</link><description>&lt;p&gt;I &lt;em&gt;love&lt;/em&gt; this idea… and who better to introduce it to me than &lt;a href="https://www.robinsloan.com/"&gt;Robin Sloan&lt;/a&gt;, &lt;a href="https://www.robinsloan.com/books/"&gt;writer&lt;/a&gt;, &lt;a href="https://every.to/superorganizers/tasting-notes-with-robin-sloan-25629085"&gt;note-maker&lt;/a&gt;, and ambassador of Keep the Blogosphere Weird in his note &lt;a href="https://www.robinsloan.com/notes/whomst-styles/"&gt;Whomst styles?&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote class="robin"&gt;This is a “whostyle”: an attempt to carry the ~timbre~ of an author’s voice, in the form of their design sensibility, through into a quotation. It’s the author who defines their whostyle; the quoting site just honors it, a frame around their words.&lt;/blockquote&gt;
&lt;p&gt;The idea of giving each personality a little speech bubble to represent their creative identity is lovely, and reminds me of &lt;a href="https://en.wikipedia.org/wiki/Microsoft_Comic_Chat"&gt;Microsoft Comic Chat&lt;/a&gt;. Let's keelhaul tiktok and &lt;a href="https://chrisgliddon.com/a-trip-down-memory-lane-microsoft-comic-chat-6f23b8a64295"&gt;bring back altcomix-inspired IRC chats&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img alt="image from Chris Gliddon's article" src="https://images.evantravers.com/articles/2021/08/comic.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Robin frankly didn't make this easy &lt;strong&gt;at all&lt;/strong&gt;, with his subtly changing background and header colors&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-colors" id="fnref-colors"&gt;1&lt;/a&gt;&lt;/sup&gt;. Jacob Hall made this easier with a lovely reference to &lt;code&gt;all: revert&lt;/code&gt;, and &lt;a href="https://jacobhall.net/2021/08/horton-hears-a-whostyle/"&gt;the resulting conversation&lt;/a&gt; in his indie-web comments is a sort of &lt;em&gt;charming&lt;/em&gt; internet House of Leaves, which Robin echoes:&lt;/p&gt;
&lt;blockquote class="robin"&gt;I’ll add that even this glancing encounter with whostyles sharpens my annoyance with Twitter, the way it “smooshes” contributions into uniform, stackable blocks. But a timeline demands this, right? Maybe; I think a timeline of tweets brimming over with typographic variation would be tons of fun. Give us a miniature grid, let us play Müller-Brockmann!&lt;/blockquote&gt;
&lt;p&gt;Hear hear! I may very well try to make my own whomstyle.css... just for giggles.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-colors"&gt;
&lt;p&gt;I gave up Robin... I'll try again later. :P &lt;a class="footnote-backref" href="#fnref-colors"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;p&gt;&lt;a class="u-in-reply-to  is-reply" href="https://www.robinsloan.com/notes/whomst-styles/"&gt;Whomst styles? - www.robinsloan.com&lt;/a&gt;&lt;/p&gt;</description><author>trv.rs</author><pubDate>Sat, 21 Aug 2021 12:10:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/08/21/whomst-styles-the-whostyles/</guid></item><item><title>Book Review: Nomadland</title><link>https://nicolaiarocci.com/book-review-nomadland/</link><description>&lt;blockquote&gt;
&lt;p&gt;Some call them homeless. The new nomads refer to themselves as &amp;lsquo;houseless&amp;rsquo;.
Many took to the road after their savings were obliterated by the Great
Recession. To keep their gas tanks and bellies full, they work long hours at
hard, physical jobs. In a time of flat wages and rising housing costs, they
have unshackled themselves from rent and mortgages as a way to get by. They
are surviving America.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 21 Aug 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-nomadland/</guid></item><item><title>In praise of serendipity, again</title><link>https://stop.zona-m.net/2021/08/in-praise-of-serendipity-again/</link><description>&lt;p&gt;Many praised it, but apparently not enough. Hence, this post.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 21 Aug 2021 08:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/in-praise-of-serendipity-again/</guid></item><item><title>Rattlesnake Ledge</title><link>https://sam.hooke.me/trip/2021/08/rattlesnake-ledge/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Sat, 21 Aug 2021 05:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/08/rattlesnake-ledge/</guid></item><item><title>Parser generators vs. handwritten parsers: surveying major language implementations in 2021</title><link>http://notes.eatonphil.com/parser-generators-vs-handwritten-parsers-survey-2021.html</link><description>&lt;p&gt;Developers often think parser generators are the sole legit way to
build programming language frontends, possibly because compiler
courses in university teach lex/yacc variants. But do any modern
programming languages actually use parser generators anymore?&lt;/p&gt;
&lt;p&gt;To find out, this post presents a non-definitive survey of the parsing
techniques used by various major programming language implementations.&lt;/p&gt;
&lt;h3 id="cpython:-peg-parser"&gt;CPython: PEG parser&lt;/h3&gt;&lt;p&gt;Until CPython 3.10 (which hasn't been released yet) the default parser
was built using &lt;a href="https://www.python.org/dev/peps/pep-0269/"&gt;pgen&lt;/a&gt;, a
custom parser generator. The team thought the PEG parser was a &lt;a href="https://www.python.org/dev/peps/pep-0617/"&gt;better
fit for expressing the
language&lt;/a&gt;. At the time the
switch from pgen to PEG parser improved speed 10% but increased memory
usage by 10% as well.&lt;/p&gt;
&lt;p&gt;The PEG grammar is defined
&lt;a href="https://github.com/python/cpython/blob/v3.9.6/Grammar/python.gram"&gt;here&lt;/a&gt;. (It
is getting renamed in 3.10 though so check the directory for a file of
a similar name if you browse 3.10+).&lt;/p&gt;
&lt;p class="note"&gt;
  This section was corrected
  by &lt;a href="https://www.reddit.com/r/ProgrammingLanguages/comments/p8vvcs/parser_generators_vs_handwritten_parsers/h9tbuve/?utm_source=reddit&amp;amp;utm_medium=web2x&amp;amp;context=3"&gt;MegaIng&lt;/a&gt; on Reddit. Originally
  I mistakenly claimed the previous parser was
  handwritten. It was not.
  &lt;br /&gt;&lt;br /&gt;
  Thanks &lt;a href="https://twitter.com/jryans"&gt;J. Ryan Stinnett&lt;/a&gt; for
  a correction about the change in speed in the new PEG parser.
&lt;/p&gt;&lt;h3 id="gcc:-handwritten"&gt;GCC: Handwritten&lt;/h3&gt;&lt;p&gt;Source code for the C parser available
&lt;a href="https://github.com/gcc-mirror/gcc/blob/releases/gcc-12.1.0/gcc/c/c-parser.cc"&gt;here&lt;/a&gt;. It
used to use Bison until &lt;a href="https://gcc.gnu.org/gcc-4.1/changes.html"&gt;GCC 4.1 in
2006&lt;/a&gt;. The C++ parser also
switched from Bison to a handwritten parser &lt;a href="https://gcc.gnu.org/gcc-3.4/changes.html"&gt;2 years
earlier&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="clang:-handwritten"&gt;Clang: Handwritten&lt;/h3&gt;&lt;p&gt;Not only handwritten but the same &lt;em&gt;file&lt;/em&gt; handles parsing C,
Objective-C and C++. Source code is available
&lt;a href="https://github.com/llvm/llvm-project/blob/llvmorg-12.0.1/clang/lib/Parse/Parser.cpp"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="ruby:-yacc-like-parser-generator"&gt;Ruby: Yacc-like Parser Generator&lt;/h3&gt;&lt;p&gt;Ruby uses Bison. The
grammar for the language can be found
&lt;a href="https://github.com/ruby/ruby/blob/v3_0_2/parse.y"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="v8-javascript:-handwritten"&gt;V8 JavaScript: Handwritten&lt;/h3&gt;&lt;p&gt;Source code available &lt;a href="https://github.com/v8/v8/blob/9.5.38/src/parsing/parser.cc"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="zend-engine-php:-yacc-like-parser-generator"&gt;Zend Engine PHP: Yacc-like Parser Generator&lt;/h3&gt;&lt;p&gt;Source code available &lt;a href="https://github.com/php/php-src/blob/php-8.0.9/Zend/zend_language_parser.y"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="typescript:-handwritten"&gt;TypeScript: Handwritten&lt;/h3&gt;&lt;p&gt;Source code available &lt;a href="https://github.com/microsoft/TypeScript/blob/v4.3.5/src/compiler/parser.ts"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="bash:-yacc-like-parser-generator"&gt;Bash: Yacc-like Parser Generator&lt;/h3&gt;&lt;p&gt;Source code for the grammar is available
&lt;a href="http://git.savannah.gnu.org/cgit/bash.git/tree/parse.y?h=bash-5.1"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="chromium-css-parser:-handwritten"&gt;Chromium CSS Parser: Handwritten&lt;/h3&gt;&lt;p&gt;Source code available &lt;a href="https://github.com/chromium/chromium/blob/95.0.4617.2/third_party/blink/renderer/core/css/parser/css_parser_impl.cc"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="java-(openjdk):-handwritten"&gt;Java (OpenJDK): Handwritten&lt;/h3&gt;&lt;p&gt;You can find the source code
&lt;a href="https://github.com/openjdk/jdk/blob/jdk-18%2B11/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some &lt;a href="https://openjdk.java.net/projects/compiler-grammar/"&gt;older
commentary&lt;/a&gt; calls
this implementation fragile. But a Java contributor &lt;a href="https://twitter.com/BrianGoetz/status/1429227723055042568"&gt;suggests the
situation has improved since Java
8&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="golang:-handwritten"&gt;Golang: Handwritten&lt;/h3&gt;&lt;p&gt;Until Go 1.6 the compiler used a yacc-based parser. The source code
for that grammar is available
&lt;a href="https://github.com/golang/go/blob/go1.5/src/cmd/compile/internal/gc/y.go"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In Go 1.6 they switched to a handwritten parser. You can find that
change &lt;a href="https://go-review.googlesource.com/c/go/+/16665/"&gt;here&lt;/a&gt;. There
was a reported 18% speed increase when parsing files and a reported 3%
speed increase in building the compiler itself when switching.&lt;/p&gt;
&lt;p&gt;You can find the source code for the compiler's parser
&lt;a href="https://github.com/golang/go/blob/go1.17/src/cmd/compile/internal/syntax/parser.go"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="roslyn:-handwritten"&gt;Roslyn: Handwritten&lt;/h3&gt;&lt;p&gt;The C# parser source code is available
&lt;a href="https://github.com/dotnet/roslyn/blob/Visual-Studio-2019-Version-16.11/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs"&gt;here&lt;/a&gt;. The
Visual Basic parser source code is
&lt;a href="https://github.com/dotnet/roslyn/blob/Visual-Studio-2019-Version-16.11/src/Compilers/VisualBasic/Portable/Parser/Parser.vb"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A C# contributor mentioned a few key reasons for using a handwritten parser &lt;a href="https://news.ycombinator.com/item?id=13915150"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="lua:-handwritten"&gt;Lua: Handwritten&lt;/h3&gt;&lt;p&gt;Source code available &lt;a href="https://github.com/lua/lua/blob/v5.4.3/lparser.c"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="swift:-handwritten"&gt;Swift: Handwritten&lt;/h3&gt;&lt;p&gt;Source code available &lt;a href="https://github.com/apple/swift/blob/swift-5.4.2-RELEASE/lib/Parse/Parser.cpp"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="r:-yacc-like-parser-generator"&gt;R: Yacc-like Parser Generator&lt;/h3&gt;&lt;p&gt;I couldn't find it at first but
&lt;a href="https://www.reddit.com/r/programming/comments/p8vv1l/parser_generators_vs_handwritten_parsers/h9tl763/?utm_source=reddit&amp;amp;utm_medium=web2x&amp;amp;context=3"&gt;Liorithiel&lt;/a&gt;
showed me the parser source code is
&lt;a href="https://github.com/wch/r-source/blob/trunk/src/main/gram.y"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="julia:-handwritten-...-in-scheme"&gt;Julia: Handwritten ... in Scheme&lt;/h3&gt;&lt;p&gt;Julia's parser is handwritten but not in Julia. It's in Scheme! Source code available &lt;a href="https://github.com/JuliaLang/julia/blob/v1.6.2/src/julia-parser.scm"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="postgresql:-yacc-like-parser-generator"&gt;PostgreSQL: Yacc-like Parser Generator&lt;/h3&gt;&lt;p&gt;PostgreSQL uses Bison for parsing queries. Source code for the grammar
available
&lt;a href="https://github.com/postgres/postgres/blob/REL_13_STABLE/src/backend/parser/gram.y"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="mysql:-yacc-parser-generator"&gt;MySQL: Yacc Parser Generator&lt;/h3&gt;&lt;p&gt;Source code for the grammar available
&lt;a href="https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="sqlite:-yacc-like-parser-generator"&gt;SQLite: Yacc-like Parser Generator&lt;/h3&gt;&lt;p&gt;SQLite uses its own parser generator called
&lt;a href="https://www.sqlite.org/lemon.html"&gt;Lemon&lt;/a&gt;. Source code for the
grammary is available
&lt;a href="https://github.com/sqlite/sqlite/blob/version-3.36.0/src/parse.y"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;&lt;p&gt;Of the &lt;a href="https://redmonk.com/sogrady/2021/03/01/language-rankings-1-21/"&gt;2021 Redmonk top 10
languages&lt;/a&gt;,
8 of them have a handwritten parser. Ruby and Python use parser generators.&lt;/p&gt;
&lt;p&gt;Although parser generators are still used in major language
implementations, maybe it's time for universities to start teaching
handwritten parsing?&lt;/p&gt;
&lt;p class="note"&gt;
  This tweet was published before I was corrected about Python's
  parser. It should say 8/10 but I cannot edit the tweet.
&lt;/p&gt;&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Let's actually survey the parsing techniques used by major programming languages in 2021 (with links to code 👾).&lt;br /&gt;&lt;br /&gt;In this post we discover that 9/10 of the top languages by &lt;a href="https://twitter.com/redmonk?ref_src=twsrc%5Etfw"&gt;@redmonk&lt;/a&gt; use a handwritten parser as opposed to a parser generator. 😱&lt;a href="https://t.co/M69TqN78G5"&gt;https://t.co/M69TqN78G5&lt;/a&gt; &lt;a href="https://t.co/sGsdDmwshB"&gt;pic.twitter.com/sGsdDmwshB&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1429137493019045899?ref_src=twsrc%5Etfw"&gt;August 21, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Sat, 21 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/parser-generators-vs-handwritten-parsers-survey-2021.html</guid></item><item><title>A programmable programming language? I'll drink to that!</title><link>https://lambdaland.org/posts/2021-08-21_racket_mug/</link><description>&lt;p&gt;My wife and I got a chance to go to a place that lets you paint pottery and then have it fired. The pottery is all pre-made; you just get to paint it.&lt;/p&gt;
&lt;p&gt;It’s been a very long time since I’ve worked with a physical art medium, so the mug looks kinda dumpy. I did alright with the Racket logo on the bottom-inside of the mug though!&lt;/p&gt;
&lt;p&gt;&lt;img alt="A poorly-painted blue mug" src="https://lambdaland.org/img/racket_mug0.jpeg" /&gt;
&lt;img alt="A passable freehanded Racket logo" src="https://lambdaland.org/img/racket_mug1.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;I’m working on some fun projects with Racket. It’s been a really enjoyable language to work with. If you haven’t given it a whirl, I recommend checking it out. The docs on the &lt;a class="link-external" href="https://racket-lang.org" rel="external"&gt;official Racket site&lt;/a&gt; are very good, and there are some absolutely &lt;em&gt;stellar&lt;/em&gt; gentle introductions to the language like &lt;a class="link-external" href="https://htdp.org" rel="external"&gt;How to Design Programs&lt;/a&gt; that I would recommend.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sat, 21 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-08-21_racket_mug/</guid></item><item><title>Ten years ago, software was eating the world. Now...</title><link>https://stop.zona-m.net/2021/08/ten-years-ago-software-was-eating-the-world.-now.../</link><description>&lt;p&gt;Now, it could get worse. Just faster. Or&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 21 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/ten-years-ago-software-was-eating-the-world.-now.../</guid></item><item><title>Why so negative?</title><link>https://michaelcarrano.com/blog/why-so-negative</link><description>&lt;p&gt;As an engineering manager, one thing I spend a lot of time doing is reviewing code that my team ships. According to &lt;a href="https://pullpanda.com/"&gt;Pull Panda&lt;/a&gt; I am often a top reviewer for the team.&lt;/p&gt;

&lt;p&gt;There are many things I look for when reviewing the code such as does this architecture make sense? Is it flexible for future changes or will we have to redo a lot of it? Does it do what you think it does? Are we already doing something similar elsewhere and we just re-invented the wheel? And much more.&lt;/p&gt;

&lt;p&gt;Then there are the smaller things I look for such as naming (one of the hardest problems) and cognitive load when reading and understanding the code.&lt;/p&gt;

&lt;p&gt;The most common feedback I give is related to using negation &lt;code class="language-plaintext highlighter-rouge"&gt;!&lt;/code&gt; in the code. Is it really needed? We can all be more positive.&lt;/p&gt;

&lt;p&gt;Here are just some basic examples of what I come across and how they can be improved.&lt;/p&gt;

&lt;div class="language-kotlin highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Common example with negation. &lt;/span&gt;
&lt;span class="c1"&gt;// Reading this code would be something like: &lt;/span&gt;
&lt;span class="c1"&gt;// "If colors is NOT empty then doSomething1() else doSomething2()".&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lets see how a small change can improve the cognitive load.&lt;/span&gt;
&lt;span class="c1"&gt;// This will now read as &lt;/span&gt;
&lt;span class="c1"&gt;// "If colors is empty, doSomething2() else doSomething1()". &lt;/span&gt;
&lt;span class="c1"&gt;// We have the same exact outcome as the above without having to think about negation.&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is another example which involves some not so great variable names:&lt;/p&gt;

&lt;div class="language-kotlin highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pared down example from our actual codebase before refactoring it&lt;/span&gt;
&lt;span class="c1"&gt;// Try reading this... "If NOT don't show again, then doSomething1() else doSomething2()"&lt;/span&gt;
&lt;span class="c1"&gt;// Double negatives can hurt your brain&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dontShowAgain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;dontShowAgain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lets see how renaming the variable and removing the negation can clean this code up.&lt;/span&gt;
&lt;span class="c1"&gt;// Now this is much easier to read as "If showAgain, doSomething1() else doSomething2()".&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;showAgain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;showAgain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;doSomething2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// I had to triple check my logic above to make sure I was still right.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last example is real. I worked with our intern this summer to refactor this area of code and we were often on Zoom confusing ourselves because of the double negatives when trying to comprehend the code. Ultimately decided that we should first add tests to this part of the code since we lacked coverage before refactoring to make sure we did not break our logic.&lt;/p&gt;

&lt;p&gt;So try to be more positive with your code, it really helps.&lt;/p&gt;</description><author>Mobile Software Engineer in NYC.</author><pubDate>Fri, 20 Aug 2021 11:06:33 GMT</pubDate><guid isPermaLink="true">https://michaelcarrano.com/blog/why-so-negative</guid></item><item><title>How I bought multiple PlayStation PS5 and Xbox Series X</title><link>https://michaelcarrano.com/blog/how-to-buy-ps5-and-xbox-series-x</link><description>&lt;p&gt;I want to take a moment to share how I have been able to purchase multiple PlayStation PS5 and Xbox Series X without using a bot. Hopefully this will help you get one of the new consoles since they are still hard to get 9 months after they were released.&lt;/p&gt;

&lt;p&gt;The first time I purchased the Xbox Series X was pure luck.&lt;/p&gt;

&lt;p&gt;I happened to be on PTO on a Thursday and I noticed a trend that BestBuy would have them for sale on Thursdays so I just kept checking in periodically during the day to see when they were available to purchase. Eventually, I managed to check the website when they were for sale and raced through the purchase flow to secure my first Xbox Series X.&lt;/p&gt;

&lt;p&gt;Since then I have purchased several more of the new consoles for family and friends.&lt;/p&gt;

&lt;p&gt;Before I get into the details I just want to say that I am fortunate to work in technology which means I am on the computer for 8+ hours a day. So when I get the alert that the PS5 or Xbox is for sale, I am just 2-3 clicks away.&lt;/p&gt;

&lt;p&gt;At first I was using &lt;a href="https://www.stockinformer.com/"&gt;StockInformer&lt;/a&gt; to blast their alarm through my speakers. This helped me get my second Xbox Series X. However, it was pretty loud and disruptive, especially when I was on work calls and in the middle of speaking. Over time, I noticed that the alerts were behind so I missed out on getting any additional consoles.&lt;/p&gt;

&lt;p&gt;I have since switched over to following two different Twitter accounts to be alerted when the consoles are for sale. I have purchased several consoles this way and I plan on following these accounts until my friends list all get their consoles.&lt;/p&gt;

&lt;p&gt;You will want to follow the accounts below because somehow they tend to have insider info and give a heads up before the consoles go on sale:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/mattswider"&gt;mattswider&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/Jake_Randall_YT"&gt;Jake_Randall_YT&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I mentioned earlier that I work in technology, so I do not actually follow these people on Twitter. I have a personal &lt;a href="https://slack.com"&gt;Slack&lt;/a&gt; setup that will pull in their tweets and notify me when this happens.&lt;/p&gt;

&lt;p&gt;So there you have it, hope this helps you get one of the new PlayStation PS5 or Xbox Series X. I have been pretty successul in getting one or more consoles a month.&lt;/p&gt;</description><author>Mobile Software Engineer in NYC.</author><pubDate>Fri, 20 Aug 2021 11:06:33 GMT</pubDate><guid isPermaLink="true">https://michaelcarrano.com/blog/how-to-buy-ps5-and-xbox-series-x</guid></item><item><title>Patterns of Progress</title><link>https://ztoz.blog/posts/patterns-of-progress/</link><description>&lt;p&gt;Modern software systems involve many asynchronous tasks, but reporting status on tasks is often handled poorly.
An asynchronous task is one that processes in the background
and allows the client or caller to work on something else in the meantime (&amp;ldquo;non-blocking&amp;rdquo;). Architecturally, asynchronous processing
is often the right choice for operations and scaling reasons, but it does add complexity. Both the end-users and
the operating teams will need to understand the state of the task and, if the wrong mechanism is chosen, teams will spend their
time solving issues such as:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stuck tasks. A task is not progressing; this could be caused by a missing update/write (the task progressed but
the update message was missed), the task was dropped from the queue, a task that is actually stuck in an infinite loop
or deadlock, or a task&amp;rsquo;s terminal state was not accurately modeled.&lt;/li&gt;
&lt;li&gt;Failed tasks. A task is in a failure state and needs some operator intervention. This is usually for tasks
that have exhausted their retry budget, but &lt;em&gt;must&lt;/em&gt; be completed rather than abandoned. Universally, engineering teams
will need to care about failing tasks, but a poor progress design will amplify the operational pain and impede
automation improvements. End-users generate many complaints and escalations when they cannot differentiate between an
&amp;ldquo;in-progress&amp;rdquo; task and a failed task, as every delay becomes a suspected failure.&lt;/li&gt;
&lt;li&gt;Orchestration limitations. If the client is unable to tell when an asynchronous task is complete nor separate
success from failure, then their ability to orchestrate follow-on tasks that depend on the result of the previous task
will be hampered by data quality and concurrency issues.&lt;/li&gt;
&lt;li&gt;Availability issues. The progress reporting mechanism may be subject to denial-of-service attacks where multiple
clients can be impacted by a single bad client or data issue. If the processing systems cannot update status on tasks,
the updates may be lost.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Many of these issues will only be seen once a system becomes popular, particularly orchestration and availability issues.&lt;/p&gt;
&lt;p&gt;NB: In this article, a end-user is a human while a client is software, operating on the behest of some end-user,
that has an interest in the task.&lt;/p&gt;
&lt;h1 id="problem-statement"&gt;Problem Statement&lt;/h1&gt;
&lt;p&gt;Put simply, something is happening out of sight from the client and the client needs some way to know when that
something has completed. However, this definition can obscure the actual design space. If we ask these three questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why is this task being executed? What is the context of the call?&lt;/li&gt;
&lt;li&gt;What kind of updates does the client need?&lt;/li&gt;
&lt;li&gt;How complex is the client? How does the client receive updates?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;we can design a solution based on the use case, required data model, and use that to find a lower cost technical implementation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id="example-generating-a-report"&gt;Example: Generating a report&lt;/h3&gt;
&lt;p&gt;The end-user (an authorized web user) has requested a report of the past month of activity which will take several
minutes to compile.
This is an adhoc report specific to this user (versus a report production that many clients may be tracking). The
data comes from multiple data sources but a &amp;lsquo;partial&amp;rsquo; report is undesirable, so there are many ways the report
generation may fail and a single data source failing will prevent the report&amp;rsquo;s production. The end-user is using a web
application to retrieve the successful report, so the client can handle some protocol complexity but may not
be able to persist data reliably (user could delete stored data) nor be invoked from the server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h3 id="example-exporting-data-after-transforming-it"&gt;Example: Exporting data after transforming it&lt;/h3&gt;
&lt;p&gt;The end-user is collecting data within an online spreadsheet table and wants to transform the data collected
since the last export and, once that is complete, export it daily (as input to a larger workflow). Internally,
transformations are handled by one service and exports by a separate service. Transformations and exports operate on
the same resource (the table). If the transformation fails, the export is unnecessary. The end-user is automating
this process using shell scripts and a simple scheduler system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="client-context-and-knowledge"&gt;Client Context and Knowledge&lt;/h1&gt;
&lt;h2 id="know-nothing-special-case"&gt;Know Nothing (Special Case)&lt;/h2&gt;
&lt;p&gt;The client has no knowledge of the asynchronous task. This is usually indicative that the task is a &amp;lsquo;side effect&amp;rsquo;
of some other workflow. For example, a log message may eventually be recorded in a store, but the client has no
need to know the state of the log because it will not impact their workflow.&lt;/p&gt;
&lt;h2 id="task-will-eventually-process-special-case"&gt;Task will eventually process (Special Case)&lt;/h2&gt;
&lt;p&gt;The client is told that the request will be eventually handled, but they have no visibility into its state. Email
is an example; after being given confirmation that an email will eventually get to the recipients, the sender has
no way to track the current state or ultimate delivery of their message&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. If the task is modifying or creating
a resource, the clients may be able to determine completion by checking on the resource.&lt;/p&gt;
&lt;h2 id="completed"&gt;Completed&lt;/h2&gt;
&lt;p&gt;The client can tell if a task has completed, but has no further details. For a state machine, this is equivalent to
knowing that a task is in a terminal state.&lt;/p&gt;
&lt;p&gt;If a non-terminal state is treated as &amp;ldquo;complete&amp;rdquo;, clients can see tasks switch back to incomplete, which is
counter to normal expectations. If the clients are using the complete status as a part of a work orchestration,
this can lead to processes becoming unexpectedly parallelized or data seeming inconsistent. If a task can be retried,
the complete status should only be set once all attempts have been exhausted.&lt;/p&gt;
&lt;h2 id="success-or-failure"&gt;Success or Failure&lt;/h2&gt;
&lt;p&gt;The client can tell if the task completed successfully or if it failed. For a state machine, there is generally
only a single successful terminal state but there may be many failure states. The client may be able to extract
details about a failure (e.g. exceptions, status code).&lt;/p&gt;
&lt;p&gt;Failures can reveal security-sensitive information about an infrastructure, so systems will often have a two-tier
reporting mechanism &amp;mdash; a limited error message or opaque code (e.g. transaction id) for outsiders and more detailed
diagnostics for insiders/engineering team.&lt;/p&gt;
&lt;p&gt;Similar to above, if a task can change from a failed state to a successful state, this can confuse clients and
foil orchestration. Conceptually, this model is for tracking &amp;ldquo;permanent failure&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="in-progress-details"&gt;In-progress Details&lt;/h2&gt;
&lt;p&gt;The client has some view into the status of the task as it being executed. This view could be an indicator of:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Activity/liveliness&lt;/li&gt;
&lt;li&gt;Work completed (e.g. percent complete)&lt;/li&gt;
&lt;li&gt;Work left (e.g. distance to destination)&lt;/li&gt;
&lt;li&gt;Estimated time to complete&lt;/li&gt;
&lt;li&gt;Individual steps completed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;or many other variations or combinations. For instance, when I was generating the SSL certificate for this site,
the first attempt failed. The user interface reported that a failure occurred, the time of the failure, and that
there would be an automatic retry. While the data reported was minimal, this was sufficient for the purposes of
the task.&lt;/p&gt;
&lt;h2 id="simulated-in-progress-details-special-case"&gt;Simulated In-progress Details (Special Case)&lt;/h2&gt;
&lt;p&gt;The client has some view into the status of the task, but the status is a simulation. For example, the server might
be projecting an estimated time to complete based on a model of processing times, rather than monitoring actual
progress. The server may report a mixture of real and simulated data; for example, a server may report the real
completion status of the task but may add in a projected percent complete if the infrastructure to report a real
percent complete is missing.&lt;/p&gt;
&lt;h1 id="data-model-and-lifecycle"&gt;Data Model and Lifecycle&lt;/h1&gt;
&lt;p&gt;A progress status may be modeled as its own resource (e.g. with a progress id) or as an attribute of a resource. If the
process does not produce a resource or if a process may be applied multiple times to a resource (perhaps even
concurrently), then each task should have a unique (or unique enough) identifier. If the task is 1:1 to a resource,
then Occam&amp;rsquo;s razor would suggest creating a new entity is unnecessary and instead use the resource identifier.&lt;/p&gt;
&lt;p&gt;If a progress id and resource are connected, but there isn&amp;rsquo;t a way to look up that relationship, you are likely
to run into operational visibility issues where end-users can only report one of the ids. Business reporting on the
state of your system will likely require some join between the two ids, so plan ahead.&lt;/p&gt;
&lt;p&gt;If you need to report in-progress details and the task is indeterminate upon creation (e.g. the task will be scanning
a number of files but the number of files is unknown), then you will want to structure your reporting for
&amp;ldquo;monotonic reporting&amp;rdquo;. Clients expect progress to move forwards, stall, or stop, but not move
backwards. For example, in the scanning example, if you report status as the proportion of the number of files scanned
over the number of total files, it is possible for the percentage to decline over time if finding files and scanning
files are not sequential. Instead, represent the data to make the non-monotonicity clear, perhaps by
differentiating a &amp;ldquo;known, fixed&amp;rdquo; denominator versus an &amp;ldquo;in-progress, changing&amp;rdquo; denominator or using an event history
approach.&lt;/p&gt;
&lt;p&gt;The utility of progress data tends to decline rapidly with time. Based on your operational and client needs, plan to
delete old progress data periodically and automatically&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;. However, if the client can request data for a valid process
that simply has not been registered yet (due to eventual consistency or delays in progress history processing), there
may be an ambiguity between &amp;ldquo;old and likely deleted data&amp;rdquo; and &amp;ldquo;very new data&amp;rdquo; which can cause orchestration issues. A
potential mitigation would be to encode the creation date within the progress id such that the server could return a
410 Gone versus 404 Not Found status.&lt;/p&gt;
&lt;p&gt;Security-wise, authorizing access to a progress status can be tricky if it is not 1:1 with a resource. Additionally,
if the client polls for status, the server could generate a significant number of authorization checks. Thus, from
a risk trade-off perspective, avoiding storing any client or sensitive data as part of the progress and skipping
authorization checks may be acceptable.&lt;/p&gt;
&lt;h1 id="types-of-client-mechanisms"&gt;Types of Client Mechanisms&lt;/h1&gt;
&lt;h2 id="wait--synchronous-call-wrapper-special-case"&gt;Wait / Synchronous Call Wrapper (Special Case)&lt;/h2&gt;
&lt;p&gt;The client makes a synchronous call which handles waiting on an asynchronous task. The clients call may need
to be kept active through keep-alive or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100"&gt;HTTP 100 Continue&lt;/a&gt;
messages. If the asynchronous portion of the call is both a
small portion of the overall workflow and the asynchronous task has a well-bounded time to complete, this pattern
can simplify the client interface. This approach works best if the task and resource are 1:1, such as resource
creation or deletion.&lt;/p&gt;
&lt;h2 id="poll"&gt;Poll&lt;/h2&gt;
&lt;p&gt;Periodically, the client makes a call to get the current status of the task.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Failure Modes&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Since there is a cost to every call, this mechanism can create denial-of-service attacks due to a large number of calls
made in a very short period as well as tying up network bandwidth. Server mitigations include early request rejections
(potentially with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429"&gt;HTTP 429 Too Many Requests&lt;/a&gt; status),
long polling (a request may stay open until some timeout is reached, allowing late updates to be fulfilled
within a single request), and caching. On the client side, the client can switch from a periodic rate to one with
randomness or increase delays between calls to reduce server load.&lt;/p&gt;
&lt;p&gt;For the overall workflow, the delays between calls add latency to the overall processing time. For clients, this is
an incentive to decrease the time between requests. For a server, the worst case would be a client &amp;lsquo;busy wait&amp;rsquo;-ing
where calls are made once the previous call has completed.&lt;/p&gt;
&lt;h2 id="callback"&gt;Callback&lt;/h2&gt;
&lt;p&gt;On either completion or an update to status, the server makes a call to the client. Within the Observer pattern, this
is equivalent to invoking the &lt;code&gt;update&lt;/code&gt; function. Callbacks may be modeled as both 1:1 and 1:n to clients.&lt;/p&gt;
&lt;p&gt;If the clients do not have an addressable location (e.g. web browser), callbacks are not an option.&lt;/p&gt;
&lt;p&gt;Compared to polling, the advantage of this method is that, since the server controls the callback, the server can
minimize wasted &amp;ldquo;no new data&amp;rdquo; calls to the client. The server can maintain an audit trail of clients that were
successfully notified, an important feature for some domains. Callbacks fit very naturally into an orchestration or
workflow management system, allowing efficient &amp;ldquo;wait until this done&amp;rdquo; steps, since the callback can be used to trigger
the next step in the flow.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Failure Modes&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Security-wise, the callback can be unsafe, particularly if it is controlled fully by the client. Servers can be
tricked into participating in denial of service attacks, invoking vulnerabilities, or divulging internal data.&lt;/p&gt;
&lt;p&gt;The callback from the server can fail, for reasons including a bad registration, network failures, or client
availability issues. The onus is placed on the server for retries, and it is possible that the client will never
see the callback. If clients need a way to see missing callbacks or replay previous callbacks, callbacks may be combined
with event streams.&lt;/p&gt;
&lt;h2 id="event-stream"&gt;Event Stream&lt;/h2&gt;
&lt;p&gt;On a change to status, the server writes a new event log. Clients read from the event log. The event log is not
necessarily persisted but usually is with technologies like Kafka. If the event log is
persisted, clients may have the ability to replay or see historical data. The protocol could be
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events"&gt;server side events&lt;/a&gt; or may be custom.&lt;/p&gt;
&lt;p&gt;An event stream may be modeled per progress, per resource, or per client.&lt;/p&gt;
&lt;p&gt;Internally, a system may use an event stream (potentially to decouple async processing from the API layer) but
present the data via a different mechanism. For example, a background may read the event stream and update a
key-value store to represent the latest &amp;ldquo;snapshot&amp;rdquo; of the data.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Failure Modes&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As the most complex mechanism, you can expect more implementation-specific and operational issues than in polling
or callbacks. For instance, storage space is a function of the number of things being tracked multiplied by the
number of updates, while straight-forward implementations of polling and callbacks only require space proportional
to the number of things being tracked.&lt;/p&gt;
&lt;p&gt;Operationally, however, maintaining a history allows replay of events which can help mitigate server and client failure
modes, so the complexity may be worth the cost.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Tracking pixels and other techniques are &amp;ldquo;out of band&amp;rdquo; mechanisms to see if an email is read.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;However, historical data may be interesting for building prediction models or tracking trends. In this case, archive
the data by moving it to another data store.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>ℤ→ℤ</author><pubDate>Fri, 20 Aug 2021 03:04:24 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/patterns-of-progress/</guid></item><item><title>Redirect CodeIgniter logs to container's STDOUT/ERR</title><link>https://shyamjos.com/redirecting-codeigniter-logs-to-container-stdout-stderr/</link><description>&lt;p&gt;As part of migrating applications from EC2 instance to kubernetes I was assigned with a task of containerizing a CodeIgniter application. once the application was containerized I noticed that the application logs were writing to a local file inside the container. so I thought this can be easily fixed by changing the logging path in CodeIgniter config file to &lt;code&gt;php://stdout&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But it was not that easy!!&lt;/p&gt;
&lt;p&gt;






 
 
&lt;figure&gt;&lt;img alt="logging" class="mx-auto my-0 rounded-md" src="https://shyamjos.com/assets/img/logs/giphy.webp" /&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;strangely the log path config in CodeIgniter is a combination of file path + file extension, so if I set the file path as &lt;code&gt;php://stdout&lt;/code&gt; and left the file extension part blank, the application will throw an error &lt;code&gt;'Use a full server path with trailing slash'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After some digging on google. I found out that there is no easy way to set custom static log path in CodeIgniter and I also found an &lt;a href="https://github.com/bcit-ci/CodeIgniter/issues/5648" rel="noreferrer" target="_blank"&gt;open feature request&lt;/a&gt; for this issue which is not yet implemented by CodeIgniter team :(&lt;/p&gt;
&lt;p&gt;Luckily I was able to fix the issue by modifying CodeIgniter logging module which is located at &lt;code&gt;system/core/Log.php&lt;/code&gt;, I am not sure if this is an optimal solution , just in-case if you are stuck with similar issue give it a try!&lt;/p&gt;
&lt;p&gt;Firstly I removed the file path and file extension check conditions from below code block (&lt;code&gt;system/core/Log.php&lt;/code&gt;)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;public function __construct()
	{
		$config =&amp;amp; get_config();

		$this-&amp;gt;_log_path = ($config['log_path'] !== '') ? $config['log_path'] : APPPATH.'logs/';
		$this-&amp;gt;_file_ext = (isset($config['log_file_extension']) &amp;amp;&amp;amp; $config['log_file_extension'] !== '')
			? ltrim($config['log_file_extension'], '.') : 'php';

		file_exists($this-&amp;gt;_log_path) OR mkdir($this-&amp;gt;_log_path, 0755, TRUE);

		if ( ! is_dir($this-&amp;gt;_log_path) OR ! is_really_writable($this-&amp;gt;_log_path))
		{
			$this-&amp;gt;_enabled = FALSE;
		}

		if (is_numeric($config['log_threshold']))
		{
			$this-&amp;gt;_threshold = (int) $config['log_threshold'];
		}
		elseif (is_array($config['log_threshold']))
		{
			$this-&amp;gt;_threshold = 0;
			$this-&amp;gt;_threshold_array = array_flip($config['log_threshold']);
		}

		if ( ! empty($config['log_date_format']))
		{
			$this-&amp;gt;_date_fmt = $config['log_date_format'];
		}

		if ( ! empty($config['log_file_permissions']) &amp;amp;&amp;amp; is_int($config['log_file_permissions']))
		{
			$this-&amp;gt;_file_permissions = $config['log_file_permissions'];
		}
	}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is how the code block looks after removing all the checks&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;	public function __construct()
	{
		$config =&amp;amp; get_config();

			$this-&amp;gt;_enabled = TRUE;
	}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you need to replace below line&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$filepath = $this-&amp;gt;_log_path.'log-'.date('Y-m-d').'.'.$this-&amp;gt;_file_ext;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;with this line (use a static file path instead of a dynamic one)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$filepath = $this-&amp;gt;_log_path;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now find and remove the below block code from the file (used for changing permission of log file, not needed in our case)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;		if (isset($newfile) &amp;amp;&amp;amp; $newfile === TRUE)
		{
			chmod($filepath, $this-&amp;gt;_file_permissions);
		}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And finally make changes to the CodeIgniter main config file (&lt;code&gt;application/config/config.php&lt;/code&gt;) as below&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/*
|--------------------------------------------------------------------------
| Error Logging Threshold
|--------------------------------------------------------------------------
|
| You can enable error logging by setting a threshold over zero. The
| threshold determines what gets logged. Threshold options are:
|
|	0 = Disables logging, Error logging TURNED OFF
|	1 = Error Messages (including PHP errors)
|	2 = Debug Messages
|	3 = Informational Messages
|	4 = All Messages
|
| You can also pass an array with threshold levels to show individual error types
|
| 	array(2) = Debug Messages, without Error Messages
|
| For a live site you'll usually only enable Errors (1) to be logged otherwise
| your log files will fill up very fast.
|
*/
$config['log_threshold'] = 4;

/*
|--------------------------------------------------------------------------
| Error Logging Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/logs/ directory. Use a full server path with trailing slash.
|
*/
$config['log_path'] = 'php://stdout';

/*
|--------------------------------------------------------------------------
| Log File Extension
|--------------------------------------------------------------------------
|
| The default filename extension for log files. The default 'php' allows for
| protecting the log files via basic scripting, when they are to be stored
| under a publicly accessible directory.
|
| Note: Leaving it blank will default to 'php'.
|
*/
$config['log_file_extension'] = '';

/*
|--------------------------------------------------------------------------
| Log File Permissions
|--------------------------------------------------------------------------
|
| The file system permissions to be applied on newly created log files.
|
| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
| integer notation (i.e. 0700, 0644, etc.)
*/
$config['log_file_permissions'] = '';

/*
|--------------------------------------------------------------------------
| Date Format for Logs
|--------------------------------------------------------------------------
|
| Each item that is logged has an associated date. You can use PHP date
| codes to set your own date formatting
|
*/
$config['log_date_format'] = '';

/*
|--------------------------------------------------------------------------
| Error Views Directory Path
|--------------------------------------------------------------------------
|
| Leave this BLANK unless you would like to set something other than the default
| application/views/errors/ directory. Use a full server path with trailing slash.
|
*/
$config['error_views_path'] = '';
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once these changes are deployed you will be able to the see the application logs in container&amp;rsquo;s STDOUT and STDERR logs (docker/kubernetes logs).&lt;/p&gt;</description><author>Shyam Jos</author><pubDate>Fri, 20 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://shyamjos.com/redirecting-codeigniter-logs-to-container-stdout-stderr/</guid></item><item><title>Mark Zuckerberg is Nolan Sorrento</title><link>https://stop.zona-m.net/2021/08/mark-zuckerberg-is-nolan-sorrento/</link><description>&lt;p&gt;He sure is, isn&amp;rsquo;t he?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 20 Aug 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/mark-zuckerberg-is-nolan-sorrento/</guid></item><item><title>Review: Gentle and Lowly by Dane C. Ortlund</title><link>http://evantravers.com/articles/2021/08/19/review-gentle-and-lowly-by-dane-c-ortlund/</link><description>&lt;p&gt;&lt;img alt="" src="https://images.evantravers.com/articles/2021/08/gentle.jpeg" /&gt;&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;the sweep of the New Testament teaching is that it is the sun of Christ's heart, not the cloud of my sins, that now defines me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was encouraged to read this by my dad. I found it lovely both inside and out. I struggle with what Ortlund graciously calls “law-ishness…” an inordinate focus on my own works and worthiness. Mr. Ortlund redirecting my gaze towards the heart of Christ, a heart brimming with compassion that &lt;em&gt;runs&lt;/em&gt; towards the sinner&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-sinner" id="fnref-sinner"&gt;1&lt;/a&gt;&lt;/sup&gt;, has been a &lt;strong&gt;blessing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I found the book’s style to be patterned after its title. It is &lt;em&gt;very&lt;/em&gt; readable and re-visitable. Even the breakdown of Greek words is poetic and joyful rather than academic and snobby. It has a puritan-esque&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-1" id="fnref-1"&gt;2&lt;/a&gt;&lt;/sup&gt; feeling to it in that way.&lt;/p&gt;
&lt;p&gt;I found myself inspired and convicted in equal measure. I did not find it to be inordinately &amp;quot;grace-oriented&amp;quot; or ignoring the importance of God's justice and holiness. I thought it handled the divine dichotomy well. I’ll gladly revisit it again in a book study or in my future study of Christ’s character… and have been doing so regularly since I put the book down.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-sinner"&gt;
&lt;p&gt;Here Ortlund references yet another puritan…&lt;/p&gt;
&lt;pre class="lumis"&gt;&lt;code class="language-plaintext" tabindex="0"&gt;&lt;div class="line"&gt;&amp;gt; whatsoever Christ did… he did it out of love, and graced and mercy… he did it inwardly from his very bowels.
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Richard Sibbes. &lt;em&gt;Works of Richard Sibbes&lt;/em&gt;. Banner of Truth, 2000. &lt;a class="footnote-backref" href="#fnref-sinner"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-1"&gt;
&lt;p&gt;Hardly a surprise given the frequent quoting of John Owen, Thomas Goodwin, Sibbes and others. You are what you eat, I suppose. &lt;a class="footnote-backref" href="#fnref-1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Thu, 19 Aug 2021 10:14:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/08/19/review-gentle-and-lowly-by-dane-c-ortlund/</guid></item><item><title>Org habits on iOS? Check! Tasks, you're next</title><link>https://xenodium.com/org-habits-on-ios-check-tasks-youre-next</link><description>&lt;p&gt;I'm an &lt;a href="https://orgmode.org"&gt;org mode&lt;/a&gt; fan. This blog is powered by org. It's more of an accidental blog that started as a &lt;a href="https://github.com/xenodium/xenodium.github.io/blob/master/index.org"&gt;single org file&lt;/a&gt; keeping notes. I use &lt;a href="https://orgmode.org/worg/org-contrib/babel/intro.html"&gt;org babel&lt;/a&gt; too. Oh and &lt;a href="https://orgmode.org/manual/Tracking-your-habits.html"&gt;org habits&lt;/a&gt;. My never-ending list of TODOs is also powered by org. I manage all of this from Emacs and peek at TODOs using &lt;a href="https://orgmode.org/manual/Agenda-Views.html"&gt;org agenda&lt;/a&gt;. This all works really well while I'm sitting in front of my laptop running Emacs.&lt;/p&gt;
&lt;p&gt;But then I'm away from my laptop… and I need to quickly record habits on the go. I need it to be low-friction. Ssh'ing to an Emacs instance from a smartphone isn't an option. I'm an iPhone user, so whatever the solution, it should play nice with Emacs and org mode. I built &lt;a href="https://flathabits.com"&gt;Flat Habits&lt;/a&gt; for habit tracking and I'm fairly happy with the result. As of today, my longest-tracked habit is on a 452-day streak.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/frictionless-org-habits-on-ios/flat_habits.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Moving on to org tasks/TODOs… I want something fairly frictionless while on the go. With &lt;em&gt;Flat Habits&lt;/em&gt; as a stepping stone, I can now reuse some parts to build &lt;a href="https://reddit.com/r/plainorg"&gt;Plain Org&lt;/a&gt;. This new app should give me quick access to my tasks. The two driving goals are: quickly add new tasks and search existing ones while away from my laptop. Ok, maybe basic editing helps too. Oh and it should sync over cloud, of course.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/org-habits-on-ios-check-tasks-youre-next/plainorgdemo.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I now have an early implementation of sorts, &lt;a href="https://www.reddit.com/r/plainorg/comments/p5bnji/ios_more_improvements_alpha_builds_now_on/"&gt;available on TestFlight&lt;/a&gt;. If you'd like to give it a try, &lt;em&gt;send me an email address&lt;/em&gt; to receive the the invite. Ping me on &lt;a href="https://reddit.com/u/xenodium"&gt;reddit&lt;/a&gt;, &lt;a href="https://twitter.com/xenodium"&gt;twitter&lt;/a&gt;, or email me at &amp;quot;plainorg&amp;quot; + &amp;quot;@&amp;quot; + &amp;quot;xenodium.com&amp;quot;.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Thu, 19 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/org-habits-on-ios-check-tasks-youre-next</guid></item><item><title/><link>https://ztoz.blog/archives/</link><description/><author>ℤ→ℤ</author><pubDate>Thu, 19 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/archives/</guid></item><item><title>Big Tech is the new Walmart</title><link>https://stop.zona-m.net/2021/08/big-tech-is-the-new-walmart/</link><description>&lt;p&gt;Is this Iowa? No, it&amp;rsquo;s Google.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 19 Aug 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/big-tech-is-the-new-walmart/</guid></item><item><title>Visualizing Python modules and dependencies with Neo4j</title><link>https://blog.adnansiddiqi.me/visualizing-python-modules-and-dependencies-with-neo4j/</link><description>&lt;p&gt;I am taking a short break from the Blockchain Programming series and writing this post because I found it pretty interesting. The other day I found a tweet(which unfortunately I forgot to bookmark and can&amp;#8217;t find it anymore) about visualizing python modules in Neo4J. Guido, the Python creator had responded to that tweet. That tweet got stuck in my mind and I thought it a great excuse to explore Neo4j. I had been thinking of exploring some Graph Databases other than Neo4j. For some weird reason, I had been ignoring Neo4j for a long time, most probably because of the Java thing which I do not like at all. I tried Arangodb but it&amp;#8217;s GUI but somehow its GUI is not smooth enough. Anyways, before I move further, this is how it looks like: You can say  that this demo is a stripped version of pip show command. Before I move to the actual work, let&amp;#8217;s discuss in brief Neo4j and Graph Databases. What is Graph Database According to Wikipedia: ..a graph database (GDB) is a database that uses graph structures for semantic queries with nodes, edges, and properties to represent and store data.[1] A key concept of the system is the graph (or edge or relationship). The graph relates the data items in the store to a collection of nodes and edges, the edges representing the relationships between the nodes. Neo4j defines it as: Very simply, a graph database is a database designed to treat the relationships between data as equally important to the data itself. It is intended to hold data without constricting it to a pre-defined model. Instead, the data is stored like we first draw it out &amp;#8211; showing how each individual entity connects with or is related to others. Basically, a graph database is all about telling about data and its relationship. Graph Databases are based on Graph Algorithms which themselves are based on Graph Theory. &amp;#160; Why Graph Databases There could be several reasons but some of them are: Connectedness:- In RDBMS, you usually store data in tables. Often times you do not need a relationship and data is stored as a single entity. For instance list of countries etc. If your project does not require too much relationship then Graph Database is useless. In my case, the same thing could be done in RDBMS too but it goes more efficient too in Graph Database since all the required info is available at the node level and all is just needed to fetch relationships between nodes. Change Frequency:- In the RDBMS world, if you want to add a new piece of info, you would have to alter the table for adding a column. This is not the case in graph databases since information is added in the form of attributes. You can read further about it over the web. Let&amp;#8217;s move forward and set up our development environment. I am using the docker version. Development Setup Start Docker and execute the command docker pull neo4j to pull the Neo4j docker image. In order to run it, execute the following command: docker run --publish=7474:7474 --publish=7687:7687 --volume=$HOME/neo4j/data:/data neo4j and you will see something like the below: And when you visit d it shows something like the below: Neo4j uses Cypher Query Language for different operations. Let&amp;#8217;s discuss a few Cypher queries Creating a node In order to create a node of a certain type you will be executing the following command: Here a node was created of type Module with the attribute name in it. I used this initially but I wanted to make sure that all nodes are unique and do not create if exists so I used this query then Creating Relationship between nodes Creating a relationship between nodes is not difficult either. The query matches a couple of nodes based on name attribute, if found, it creates a directional relationship with the label USED_BY. -&amp;#62; is used to tell that the relationship begins from a to b. Neo4j Python integration OK, we know what are the queries that are needed to create both node and relationship. Our purpose is to communicate with neo4j from Python. For that purpose, I am using the neo4j Python driver. First, we need a list of all installed modules on my machine I imported pkg_resources to get all installed modules. Next, I need to grab an individual installed module information. For that purpose, I will be using pip show &amp;#60;module_name&amp;#62; command. The __main__ function now looks like below: &amp;#160; Now it&amp;#8217;s time to visualize nodes. Here I am visualizing the famous requests library for HTTP. It looks beautiful, No? Conclusion In this post, you learned how easy it is to ingest data in the neo4j graph database and visualize the relationship between entities. I have covered just the gist of neo4j. You should explore it further for the optimized search queries and using the returned result in your apps. Like always, the code is available on Github. &amp;#160; If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/visualizing-python-modules-and-dependencies-with-neo4j/"&gt;Visualizing Python modules and dependencies with Neo4j&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Wed, 18 Aug 2021 12:54:41 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/visualizing-python-modules-and-dependencies-with-neo4j/</guid></item><item><title>Do Wealthy Investors Have an Edge?</title><link>https://whoisnnamdi.com/wealthy-investors-edge/</link><description>The super-rich earn more on their investments than the rest of us. 

Something nefarious, or something else?</description><author>Who is Nnamdi?</author><pubDate>Wed, 18 Aug 2021 10:47:55 GMT</pubDate><guid isPermaLink="true">https://whoisnnamdi.com/wealthy-investors-edge/</guid></item><item><title>The interesting Chinese crackdown on Big (Ed) Tech</title><link>https://stop.zona-m.net/2021/08/the-interesting-chinese-crackdown-on-big-ed-tech/</link><description>&lt;p&gt;What makes it interesting is not &amp;ldquo;WHAT&amp;rdquo;. It&amp;rsquo;s &amp;ldquo;WHY&amp;rdquo;.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 18 Aug 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/the-interesting-chinese-crackdown-on-big-ed-tech/</guid></item><item><title>Go 1.17 泛型尝鲜</title><link>https://www.4async.com/2021/08/golang-117-generics/</link><description>&lt;img alt="Featured image of post Go 1.17 泛型尝鲜" src="https://www.4async.com/2021/08/golang-117-generics/go.png" /&gt;&lt;p&gt;今天，Go的1.17版本终于正式发布，除了带来各种优化和新功能外，1.17正式在程序中提供了尝鲜的泛型支持，这一功能也是为1.18版本泛型正式实装做铺垫。意味着在6个月后，我们就可以正式使用泛型开发了。那在Go 1.18正式实装之前，我们在1.17版本中先尝鲜一下泛型的支持吧。&lt;/p&gt;
&lt;h2 id="泛型有什么作用"&gt;泛型有什么作用？
&lt;/h2&gt;&lt;p&gt;在使用Go没有泛型之前我们怎么实现针对多类型的逻辑实现的呢？有很多方法，比如说使用&lt;code&gt;interface{}&lt;/code&gt;作为变量类型参数，在内部通过类型判断进入对应的处理逻辑；将类型转化为特定表现的鸭子类型，通过接口定义的方法实现逻辑整合；还有人专门编写了Go的&lt;a class="link" href="https://twitter.com/yogthos/status/883058510275149826" rel="noopener" target="_blank"&gt;函数代码生成工具&lt;/a&gt;，通过批量生成不同类型的相同实现函数代替手工实现等等。这些方法多多少少存在一些问题：使用了&lt;code&gt;interface{}&lt;/code&gt;作为参数意味着放弃了编译时检查，作为强类型语言的一个优势就被抹掉了。同样，无论使用代码生成还是手工书写，一旦出现问题，意味着这些方法都需要重复生成或者进行批量修改，工作量反而变得更多了。&lt;/p&gt;
&lt;p&gt;在Go中引入泛型会给程序开发带来很多好处：通过泛型，可以针对多种类型编写一次代码，大大节省了编码时间。你可以充分应用编译器的编译检查，保证程序变量类型的可靠性。借助泛型，你可以减少代码的重复度，也不会出现一处出现问题需要修改多处地方的尴尬问题。这也让很多测试工作变得更简单，借助类型安全，你甚至可以少考虑很多的边缘情况。&lt;/p&gt;
&lt;p&gt;Go语言官方有详细的泛型提案文档可以在&lt;a class="link" href="https://go.googlesource.com/proposal/&amp;#43;/refs/heads/master/design/15292-generics.md" rel="noopener" target="_blank"&gt;这里&lt;/a&gt;和&lt;a class="link" href="https://go.googlesource.com/proposal/&amp;#43;/refs/heads/master/design/15292/2013-12-type-params.md" rel="noopener" target="_blank"&gt;这里&lt;/a&gt;查看详情。&lt;/p&gt;
&lt;h2 id="如何使用泛型"&gt;如何使用泛型
&lt;/h2&gt;&lt;p&gt;前面理论我们仅仅只做介绍，这次尝鲜还是以实践为主。让我们先从一个小例子开始。&lt;/p&gt;
&lt;h3 id="从简单的例子开始"&gt;从简单的例子开始
&lt;/h3&gt;&lt;p&gt;让我们先从一个最简单的例子开始：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-go"&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;package&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #f92672;"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #e6db74;"&gt;"fmt"&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #66d9ef;"&gt;type&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;Addable&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #66d9ef;"&gt;type&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;int&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;int8&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;int16&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;int32&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;int64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #66d9ef;"&gt;uint&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;uint8&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;uint16&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;uint32&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;uint64&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;uintptr&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #66d9ef;"&gt;float32&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;float64&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;complex64&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;complex128&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #66d9ef;"&gt;func&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;add&lt;/span&gt;[&lt;span style="color: #a6e22e;"&gt;T&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;Addable&lt;/span&gt;](&lt;span style="color: #a6e22e;"&gt;a&lt;/span&gt;, &lt;span style="color: #a6e22e;"&gt;b&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;T&lt;/span&gt;) &lt;span style="color: #a6e22e;"&gt;T&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #66d9ef;"&gt;return&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;a&lt;/span&gt; &lt;span style="color: #f92672;"&gt;+&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #66d9ef;"&gt;func&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;fmt&lt;/span&gt;.&lt;span style="color: #a6e22e;"&gt;Println&lt;/span&gt;(&lt;span style="color: #a6e22e;"&gt;add&lt;/span&gt;(&lt;span style="color: #ae81ff;"&gt;1&lt;/span&gt;,&lt;span style="color: #ae81ff;"&gt;2&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;fmt&lt;/span&gt;.&lt;span style="color: #a6e22e;"&gt;Println&lt;/span&gt;(&lt;span style="color: #a6e22e;"&gt;add&lt;/span&gt;(&lt;span style="color: #e6db74;"&gt;"1"&lt;/span&gt;, &lt;span style="color: #e6db74;"&gt;"2"&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个函数可以实现任何需要使用&lt;code&gt;+&lt;/code&gt;符号进行运算的类型，我们通过定义&lt;code&gt;Addable&lt;/code&gt;类型，枚举了所有可能可以使用&lt;code&gt;add&lt;/code&gt;方法的所有的类型。比如我们在&lt;code&gt;main&lt;/code&gt;函数中就使用了&lt;code&gt;int&lt;/code&gt;和&lt;code&gt;string&lt;/code&gt;两种不同类型。&lt;/p&gt;
&lt;p&gt;但是如果这时我们使用简单的&lt;code&gt;go run&lt;/code&gt;命令运行，会发现提示语法错误：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ go version
go version go1.17 darwin/arm64
$ go run ~/main.go
# command-line-arguments
../main.go:8:2: syntax error: unexpected type, expecting method or interface name
../main.go:15:6: missing function body
../main.go:15:9: syntax error: unexpected [, expecting (
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;因为在Go 1.17中，泛型并未默认开启，你需要定义&lt;code&gt;gcflags&lt;/code&gt;方式启用泛型：&lt;/p&gt;</description><author>ipfans's Blog</author><pubDate>Tue, 17 Aug 2021 20:48:00 GMT</pubDate><guid isPermaLink="true">https://www.4async.com/2021/08/golang-117-generics/</guid></item><item><title>One Bag: Dirty Laundry</title><link>http://evantravers.com/articles/2021/08/17/dirty-laundry/</link><description>&lt;p&gt;Having just come back from the beach, I found the following link interesting. For years I've been &lt;a href="http://evantravers.com/articles/2018/12/10/whats-in-my-bag/#overnight-workout"&gt;taking a second empty packing cube&lt;/a&gt; on trips. As clothes get dirty, they move into the second packing cube. when I get home I throw the contents and cube into the laundry.&lt;/p&gt;
&lt;p&gt;I am not surprised to learn that Tom Bihn, king of one bag travel, has an interesting solution: a double sided stuff sack dubbed the &lt;a href="https://www.tombihn.com/collections/instock/products/travel-laundry-stuff-sack?variant=14986380083263"&gt;Travel Laundry Stuff Sack&lt;/a&gt;. It has a movable divider that depletes as you put on clean clothes, making the perfect amount of room for your dirty laundry on the other side. Clever.&lt;/p&gt;
&lt;p&gt;I may give this a whirl… especially &lt;a href="https://outsidebusinessjournal.com/brands/camping-and-hiking/eagle-creek-to-cease-operations-by-late-2021/"&gt;with Eagle Creek sliding into oblivion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: My wonderful wife bought me one of these for Christmas… I've already had a chance to use it on the return trip, and am giving away a couple packers it replaces. It's great!&lt;/p&gt;
&lt;p&gt;Credit to /u/mug3n on &lt;a href="https://www.reddit.com/r/onebag/comments/p5uhhr/comment/h98mi5i/"&gt;/r/onebag&lt;/a&gt;&lt;/p&gt;</description><author>trv.rs</author><pubDate>Tue, 17 Aug 2021 10:32:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/08/17/dirty-laundry/</guid></item><item><title>A Wild Sheep Chase</title><link>https://apurva-shukla.me/bookshelf/a-wild-sheep-chase/</link><description>⭐ ⭐ ⭐ ⭐ ⭐ A  is a beautiful novel. Murakami’s writing as always is expertly crafted and full of imagery that your…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Tue, 17 Aug 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/bookshelf/a-wild-sheep-chase/</guid></item><item><title>Book review - Writing and Designing Manuals and Warnings</title><link>https://river.me/blog/book-review-writing-designing-manuals-warnings/</link><description>A review of &amp;ldquo;Writing and Designing Manuals and Warnings, Fifth Edition&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 17 Aug 2021 08:44:26 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-writing-designing-manuals-warnings/</guid></item><item><title>NPM: What are project dependencies?</title><link>https://dylanpaulus.com/posts/2021/node-dependency-types/</link><description>&lt;p&gt;Code dependencies are like &lt;a href="https://www.lego.com/en-us"&gt;Lego's&lt;/a&gt;. We are able to pull in other people's code, combining and stacking different packages together to achieve our goals. Using dependencies greatly reduces the complexity of developing software. We can take advantage of the hard work someone has already done to solve a problem so that we can continue building the projects we want. A development pipeline can have multiple kinds of code dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dependencies&lt;/li&gt;
&lt;li&gt;developer dependencies &lt;em&gt;(devDependencies)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;peer dependencies &lt;em&gt;(peerDependencies)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In JavaScript, we have a &lt;code&gt;package.json&lt;/code&gt; file that holds metadata about our project. &lt;code&gt;package.json&lt;/code&gt; can store things like our project name, the version of our project, and any dependencies our project has. Dependencies, devDependencies, and peerDependencies are properties that can be included in a &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    "dependencies": {
        ...
    },
    "devDependencies": {
        ...
    },
    "peerDependencies": {
        ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Production vs. Development&lt;/h2&gt;
&lt;p&gt;The type of dependency a package is changes depending on the instance where code will be used. Some packages are necessary for users to run our code. By "users", we refer to people who are not directly working in our code-base, such as those who interact with an application we wrote or a developer writing a separate library. In other words, these are packages that are required in a production environment. On the other hand, there are packages that are only needed by developers or systems while working in our code, such as linters, testing frameworks, and build tools. These are packages that users won't need, but developers or build systems will need.&lt;/p&gt;

&lt;h2&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;Dependencies refer to packages that our project uses in &lt;em&gt;production&lt;/em&gt;. They are essential for making our application run, as they are included with our code. Whenever we install a dependency, the package and any of its dependencies are downloaded onto our local hard drive. The more dependencies we add, the larger our production code becomes, since each new dependency is included in the production build of our code. It's important to evaluate whether new dependencies are truly needed before adding them, in order to minimize the size of our production code.&lt;/p&gt;
&lt;p&gt;Dependencies are installed using &lt;code&gt;npm install X&lt;/code&gt; or &lt;code&gt;yarn add X&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Examples of dependencies: React, stylized-components, jQuery&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Dev Dependencies&lt;/h2&gt;
&lt;p&gt;Packages needed in &lt;em&gt;development&lt;/em&gt;, or while developing our code, are considered dev dependencies. These are programs, libraries, and tools that assist in our development workflow. Dev dependencies also get downloaded to your local hard drive when installed, but the user will never see these dependencies. So adding a lot of dev dependencies only affects the initial &lt;code&gt;yarn&lt;/code&gt; or &lt;code&gt;npm install&lt;/code&gt; completion time.&lt;/p&gt;
&lt;p&gt;Dev Dependencies are installed using &lt;code&gt;npm install --save-dev X&lt;/code&gt; or &lt;code&gt;yarn add --dev X&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Examples of Dev Dependencies: Jest, ESLint, Webpack&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Peer Dependencies&lt;/h2&gt;
&lt;p&gt;Peer dependencies are similar to dependencies except for a few key features. First, when installing a peer dependency it doesn't get added to your &lt;code&gt;node_modules/&lt;/code&gt; directory on your local hard drive. Why is that? Well, peer dependencies are dependencies that are needed in &lt;em&gt;production&lt;/em&gt;, but we expect the user of our code to provide the package. The package doesn't get included in our code. This is to reduce including multiples of the same dependency in &lt;em&gt;production&lt;/em&gt;. If every React library included a version of React as a dependency, then in &lt;em&gt;production&lt;/em&gt; our users would download React multiple times. Peer dependencies are a tool for library owners to optimize their project size.&lt;/p&gt;
&lt;p&gt;Peer Dependencies are installed using &lt;code&gt;yarn add --peer X&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Examples of Peer Dependencies: React, Bootstrap&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;End&lt;/h2&gt;

&lt;p&gt;I recently released a course, Creating React Libraries from Scratch, where we walk through deploying a React library from &lt;code&gt;yarn init&lt;/code&gt; to &lt;code&gt;yarn publish&lt;/code&gt;. Creating React Libraries from Scratch includes content just like this and more!&lt;/p&gt;
&lt;p&gt;To learn more click the image below!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.newline.co/courses/creating-react-libraries-from-scratch"&gt;&lt;img alt="Click here to learn more!" src="/images/create-react-libraries-card.png" /&gt;&lt;/a&gt;&lt;/p&gt;</description><author>Dylan Paulus' Blog</author><pubDate>Tue, 17 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dylanpaulus.com/posts/2021/node-dependency-types/</guid></item><item><title>Your private communications can (and will) be spied on</title><link>https://stop.zona-m.net/2021/08/your-private-communications-can-and-will-be-spied-on/</link><description>&lt;p&gt;It&amp;rsquo;s official. And it&amp;rsquo;s not good, even if it is &amp;ldquo;for the children&amp;rdquo;.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 16 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/your-private-communications-can-and-will-be-spied-on/</guid></item><item><title>New maintainers needed for the Thunderbird Lookout Fix-version add-on</title><link>https://peekread.info/tech/20210817-new-maintainers-needed-for-the-thunderbird-lookout-fix-version-add-on/</link><description>&lt;p&gt;&lt;/p&gt;&lt;div class="d-flex position-relative pt-3 pb-3"&gt;
    &lt;div class="flex-shrink-0 me-3"&gt;
        &lt;img alt="Lookout!" src="https://peekread.info/images/2019/0627-lookout-fix-version/image01.png" style="height: 89px;" /&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;p class="mt-3"&gt;&lt;/p&gt;&lt;p&gt;In august 2018 I took over maintenance of the Thunderbird addon &lt;a href="https://addons.thunderbird.net/en-US/thunderbird/addon/lookout-fix-version/"&gt;Lookout-fix-version&lt;/a&gt;. I soon set up a the Github Organization &lt;a href="https://github.com/TB-throwback"&gt;TB-throwback&lt;/a&gt; so that future development can be expanded and transferred easier if I stop work on it.&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;LookOut Fix-Version is a plugin which allows Thunderbird to interface with Microsoft’s mail tools by decoding metadata and attachments encapsulated/embedded in a TNEF (Transport Neutral Encapsulation Format) encoded attachment (aka winmail.dat).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Transport Neutral Encapsulation Format or TNEF is a proprietary email attachment format used by Microsoft Outlook and Microsoft Exchange Server. An attached file with TNEF encoding is most often named winmail.dat or win.dat, and has a MIME type of Application/MS-TNEF. The official (IANA) media type, however, is application/vnd.ms-tnef.
Source: https://en.wikipedia.org/wiki/Transport_Neutral_Encapsulation_Format&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately I had to stop working on Lookout a while ago because my employer switched us back to Outlook leaving me with no company time to maintain the project any more. The last major release of Thunderbird I received a lot of help from the TB team's John Bieling to bootstrap a workaround to get things rolling again, now Thunderbird 91 is coming and it's another case of the old xul add-ons not working in the webextension world.&lt;/p&gt;
&lt;p&gt;After receiving no takers to my maintainer request over &lt;a href="https://thunderbird.topicbox.com/groups/addons/T263d93db60cf14be-Mb7ce4a3a6ba61fac7cb8f2fb/lookout-fix-version-no-longer-receiving-company-backed-support"&gt;on the mailing list&lt;/a&gt; I have decided if I am unable to locate new maintainers I will archive the project on Github. I hope the Thunderbird devs finally assign &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=77811"&gt;Bug 77811&lt;/a&gt; to someone as I know that proper integration for decoding TNEF emails was &lt;a href="https://developer.thunderbird.net/planning/roadmap-1#investigate-microsoft-tnef-support-tb-2022"&gt;added to the roadmap&lt;/a&gt;, however in the meantime you are interested in updating the project please let me know as I'll actively be monitoring &lt;a href="https://github.com/TB-throwback/LookOut-fix-version"&gt;the Github project&lt;/a&gt; and &lt;a href="https://github.com/TB-throwback/LookOut-fix-version/issues/65"&gt;the issue calling for new maintainers&lt;/a&gt;&lt;/p&gt;</description><author>Peek Read Info</author><pubDate>Mon, 16 Aug 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://peekread.info/tech/20210817-new-maintainers-needed-for-the-thunderbird-lookout-fix-version-add-on/</guid></item><item><title>Why Buy When You Can Spend More Time and Money Making Your Own?</title><link>https://shielddigitaldesign.com/posts/2021/susb/</link><description>&lt;p&gt;
We've all been there: you've got a problem to solve and are faced with
the make versus buy decision. In my case, I wanted to switch a USB
peripheral between two PCs; that's the only hard requirement. The
easiest solution would have been to simply add a USB hub to my &lt;span class="glossary-term" tabindex="0" title="Keyboard, Video, and Mouse switch. A hardware device that allows a user to control multiple computers from a single set of keyboards, video monitors, and mice."&gt;
    KVM switch
&lt;/span&gt;


and call it a day, but that wouldn't carry USB-3 multi-gigabit
data rates (not that I needed to, but I &lt;em&gt;wanted&lt;/em&gt; to).&lt;/p&gt;</description><author>Shield Digital Design</author><pubDate>Mon, 16 Aug 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://shielddigitaldesign.com/posts/2021/susb/</guid></item><item><title>Measuring Developer Relations</title><link>https://www.swyx.io/measuring-devrel</link><description>DevRel is hot but nobody knows how to measure it. That's because we don't agree on what effective DevRel is, and we don't agree on the tradeoffs of lagging vs leading metrics for a creative, unattributable, intimately human endeavor.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 16 Aug 2021 04:06:41 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/measuring-devrel</guid></item><item><title>Riding the bull; the npm package, that is</title><link>https://www.alexanderlolis.com/riding-the-bull</link><description>This is a post about a specific Node.js library, named bull, which is used to execute background jobs.</description><author>Alexander's Blog Blog</author><pubDate>Mon, 16 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.alexanderlolis.com/riding-the-bull</guid></item><item><title>Weaknesses of agile and Scrum</title><link>https://b.agilob.net/work/agile/weaknesses_of_agile/</link><description>Jira is agile: do you use Jira and Confluence and consider this one of the main reasons contributing to your agility? Congratulations, you are part of the problem.
Boring Backlog: fixed really hard bug? Implemented complex algorithm or cut processing time from 10 hours to 22 seconds? It doesn&amp;rsquo;t matter, just pick another thing from backlog. Scrum/backlog promotes code monkeys where good effort isn&amp;rsquo;t rewarded. Personal efforts don&amp;rsquo;t matter.
Story points implementation: as long as story is implemented, quality or tech-debt doesn&amp;rsquo;t matter.</description><author>Random Notes by agilob</author><pubDate>Mon, 16 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/work/agile/weaknesses_of_agile/</guid></item><item><title>Weekly Review: Education Unboxed, Google Jupyter, and unrelated</title><link>https://yasha.solutions/posts/wr-education-unboxed-google-jupyter-sap-fow-et-al/</link><description>Ok this is an experimental post.
I realised I collect a lot of information during the week.
I don&amp;rsquo;t have the time to make sense of it immediately, in a way that would be deep, and reasonate with the global zeigest&amp;hellip;
So, instead of pilling drafts on my drive, I decided to publish it as a weekly review of the stuff I collect. and will later, if time allow, get back to it.</description><author>Yasha Solutions</author><pubDate>Mon, 16 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/wr-education-unboxed-google-jupyter-sap-fow-et-al/</guid></item><item><title>Bracket zoom bug</title><link>https://river.me/blog/bracket-zoom-bug/</link><description>A quick CSS bugfix that was particularly satisfying - and interesting!</description><author>River Writes - A MediaWiki Blog</author><pubDate>Mon, 16 Aug 2021 00:16:24 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/bracket-zoom-bug/</guid></item><item><title>CTF Writeup - InCTF 2021 - Miz</title><link>https://nevesnunes.github.io/blog/2021/08/15/CTF-Writeup-InCTF2021-Miz.html</link><description/><author>onKeyPress</author><pubDate>Sun, 15 Aug 2021 18:51:02 GMT</pubDate><guid isPermaLink="true">https://nevesnunes.github.io/blog/2021/08/15/CTF-Writeup-InCTF2021-Miz.html</guid></item><item><title>GMTK Game Jam 2021</title><link>https://www.swilliams.io/w/gmtk-game-jam-2021</link><description>&lt;p&gt;The GMTK game jam was held between June 11th and June 13th this year. It was one
    of the largest game jams of all time, with 5'761 games built during the weekend.
    I endeavoured to create a game from scratch, writing all the code myself and
    making all the assets with what small amount of artistic talent I have. For the
    SFX and music, a fantastic composer offered their services - something I greatly
    appreciated!
&lt;/p&gt;
&lt;p&gt;
    I used a simple JavaScript game engine called KaboomJS. I have nothing but praise
    for this engine. It's super simple to get into, with a fantastic website detailing
    its API with dozens of examples to guide you. It being JS is a massive boon to
    me personally since it means its easy to develop using VSCode, as opposed to
    something bulky and slow like Unity, and there's a huge wealth of tutorials and
    libraries for JS that other game engines lack. The engine is, admittedly, a bit
    limited, with effectively no physics to speak of, but for the simplistic arcade
    games I'd choose to develop in a game jam, it is as perfect as can be.
&lt;/p&gt;
&lt;p&gt; My result of the jam was a hectic bullet hell-like space shooter called Triple Threat.
    It got a surprising amount of praise; a lot of people said it was an interesting
    concept and quite fun once they got the hang of the controls, and of course many
    people praised the fantastic music and sound in the game. The greatest compliment
    I got was knowing a few people were able to smash my very own highscore - one
    got a monumental score of 560, well over double what I ever achieved at my own
    game!
&lt;/p&gt;
&lt;figure&gt;
    &lt;img src="/w/gmtk-game-jam-2021/TripleThreat.png" href="/w/gmtk-game-jam-2021/TripleThreat.png" alt="Screenshot from Triple Threat."
        style="width:500px;"&gt;
    &lt;figcaption&gt;The player ship, on the left, is composed of three separate components, responding
        to the Game Jam's theme of "Joined Together".&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After the jam, I cleaned it up a little, responding to some player feedback regarding
    the controls and implementing music and SFX mute button for accessibility reasons.
    Overall, it was a fantastic learning experience and I loved working with everyone
    and seeing other people's creations.&lt;/p&gt;
&lt;p&gt;I also uploaded the game to Newgrounds, which is a great feeling. I've used Newgrounds
    to play online games since I was a kid, and I know young me would be super happy
    to hear he eventually managed to get something he made up there.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a name="fn-1" href="https://swilliamsio.itch.io/triple-threat"&gt;Play it on Itch!&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a name="fn-2" href="https://www.newgrounds.com/portal/view/804804"&gt;Play it on Newgrounds!&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a name="fn-3" href="https://github.com/s-williams/Triple-Threat"&gt;Link to the Github repository.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><author>SWilliams.io</author><pubDate>Sun, 15 Aug 2021 04:01:01 GMT</pubDate><guid isPermaLink="true">https://www.swilliams.io/w/gmtk-game-jam-2021</guid></item><item><title>Enter the Circle of Blame</title><link>https://www.marginalia.nu/log/14-enter-the-circle-of-blame/</link><description>&lt;p&gt;So that IPCC report, huh. It&amp;rsquo;s provoked interesting behavior in people. I&amp;rsquo;ll skip over the minority that deny the findings, many people seem to agree that the report isn&amp;rsquo;t great news. Then they stop to look around and start pointing fingers.&lt;/p&gt;
&lt;p&gt;In summary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The producers blame the consumers for making the wrong purchases.&lt;/li&gt;
&lt;li&gt;The consumers blame the producers for producing the wrong things.&lt;/li&gt;
&lt;li&gt;The voters blame the politicians for incorrect policies.&lt;/li&gt;
&lt;li&gt;The politicians blame the voters for expressing the wrong wants.&lt;/li&gt;
&lt;li&gt;The right blames the market for not adapting fast enough.&lt;/li&gt;
&lt;li&gt;The left blames capitalism for being shortsighted.&lt;/li&gt;
&lt;li&gt;The dogs blame the cats and the cats blame the mice.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can run around this endless circle of blame until it&amp;rsquo;s 2121, and we still won&amp;rsquo;t have gotten any closer to finding a solution. What we&amp;rsquo;re looking for is some witch to burn, someone to be really mad at for causing us grief; but what we need is to change our behavior. Don&amp;rsquo;t forget: The producers are people. The consumers are people. The voters are people. The politicians are people. The rich, the poor, the right, the left, the market, capitalism; it&amp;rsquo;s all people.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sun, 15 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/14-enter-the-circle-of-blame/</guid></item><item><title>Minutes - Building the History Page</title><link>https://trigonaminima.github.io/2021/08/flutter_app_3/</link><description>In the last two posts [1, 2], I had described the app I want for personal tracking and my progress on the app. I have to create five tabs: History, Dashboard, Tracker, People, and Settings. I have finished designing the Settings page. The Tracker page is in the making. Today, I am going to talk about the design of the History page.</description><author>Playground</author><pubDate>Sun, 15 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://trigonaminima.github.io/2021/08/flutter_app_3/</guid></item><item><title>A/A testing and false positives with CUPED</title><link>https://bytepawn.com/aa-testing-and-false-positives-with-cuped.html</link><description>&lt;p&gt;I use Monte Carlo simulations of A/A tests to demonstrate how Data Scientists can incorrectly skew lift and p-values if they pick-and-choose between reporting traditional and CUPED results after the experiment has concluded.&lt;br /&gt;&lt;br /&gt; &lt;img alt="Historic lift" src="/images/aa_cuped_2.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 15 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/aa-testing-and-false-positives-with-cuped.html</guid></item><item><title>Why e-scooters, not Segways, are innovative</title><link>https://stop.zona-m.net/2021/08/why-e-scooters-not-segways-are-innovative/</link><description>&lt;p&gt;It&amp;rsquo;s so obvious, it hurts. Really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 14 Aug 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/why-e-scooters-not-segways-are-innovative/</guid></item><item><title>How to Contribute a Change to Nginx</title><link>https://duncanlock.net/blog/2021/08/14/how-to-contribute-a-change-to-nginx/</link><description>&lt;section id="preamble"&gt;&lt;figure class="image-block"&gt;&lt;img alt="Two screenshots of the nginx Welcome page, side-by-side, showing the page before the change and afterwards. The one on the left is dark-on-light and the one on the right is light-on-dark." height="211px" src="https://duncanlock.net/images/posts/how-to-contribute-a-change-to-nginx/nginx-welcome-page-before-after.webp" width="547px" /&gt;
&lt;figcaption&gt;Figure 1. The nginx &amp;#8220;Welcome&amp;#8221; page, before &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; after my intended change.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I wanted to add dark mode support to the default nginx &amp;#8220;Welcome to nginx&amp;#8221; page. This is about the simplest change I could choose to make - it&amp;#8217;s a simple, backwards compatible, small additive change to one single &lt;code&gt;index.html&lt;/code&gt; file. My &lt;em&gt;initial&lt;/em&gt; version of this change looks like this, and is added to the files &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element, in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#363839&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d1cec9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#c4c4ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, this is the process of getting that change from my brain, into the upstream nginx codebase.&amp;nbsp;&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_checking_out_the_code_making_the_change"&gt;Checking out the Code &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Making the&amp;nbsp;Change&lt;/h2&gt;&lt;p&gt;The nginx project uses &lt;a href="https://www.mercurial-scm.org/wiki/QuickStart"&gt;Mercurial&lt;/a&gt;, rather than git for source code control. So you will need Mercurial installed for this. This page has &lt;a href="https://www.mercurial-scm.org/wiki/Download"&gt;download links &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; installation info&lt;/a&gt;. If you are using Debian/Ubuntu, you can just run this to install&amp;nbsp;it:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;mercurial
&lt;span class="go"&gt;// Once that's done, check to make sure
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hg &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;Mercurial Distributed SCM (version 5.3.1)
&lt;/span&gt;&lt;span class="c"&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once Mercurial is installed, you can check out the source and make the change like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="go"&gt;// Go to the folder where you want to check out the nginx source
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dev
&lt;span class="go"&gt;// Use Mercurial to clone the nginx repo
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hg clone http://hg.nginx.org/nginx
&lt;span class="go"&gt;// Go into the folder
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;nginx
&lt;span class="go"&gt;// Find the file you need to edit
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'Welcome'&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="gp"&gt;./docs/html/index.html:&amp;lt;title&amp;gt;&lt;/span&gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&lt;span class="gp"&gt;./docs/html/index.html:&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to nginx!&amp;lt;/h1&amp;gt;
&lt;span class="go"&gt;// Make the change
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$EDITOR&lt;/span&gt; ./docs/html/index.html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To test this change, you can just open the &lt;code&gt;~/dev/nginx/docs/html/index.html&lt;/code&gt; in a browser using &amp;#8220;File &amp;#8594; Open&amp;#8221;, as it&amp;#8217;s just static &lt;span class="caps"&gt;HTML&lt;/span&gt;.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_contributing_the_change_upstream"&gt;Contributing the Change&amp;nbsp;Upstream&lt;/h2&gt;&lt;p&gt;Nginx development is mailing list based, rather than Github/lab/forge/etc&amp;#8230;&amp;#8203; based. The basic plan is&amp;nbsp;to:&lt;/p&gt;
&lt;div class="olist arabic"&gt;&lt;ol class="arabic"&gt;&lt;li&gt;Join the mailing&amp;nbsp;list&lt;/li&gt;&lt;li&gt;Send an email to the list, containing a patch with the&amp;nbsp;change.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;
&lt;p&gt;Follow &lt;a href="https://mailman.nginx.org/mailman/listinfo/nginx-devel"&gt;the instructions here to join the mailing list&lt;/a&gt;. You will receive a confirmation email. Once you&amp;#8217;ve done that, you can send an email to the list with your change&amp;nbsp;in.&lt;/p&gt;
&lt;p&gt;To get your change in a format that&amp;#8217;s ready to email, I did&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="go"&gt;// Commit the change and write a changelog
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hg commit
&lt;span class="go"&gt;// Export the change as a patch, ready to email
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hg &lt;span class="nb"&gt;export &lt;/span&gt;tip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the output of that, which I copied &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; pasted into an email&amp;#8201;&amp;#8211;&amp;#8201;and sent it to &lt;a href="mailto:nginx-devel@nginx.org"&gt;nginx-devel@nginx.org&lt;/a&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;# HG changeset patch
# User Duncan Lock &amp;lt;duncan.lock@gmail.com&amp;gt;
# Date 1628952253 25200
#      Sat Aug 14 07:44:13 2021 -0700
# Node ID 81294b370e774c792210904f710abc0a494c5c05
# Parent  dda421871bc213dd2eb3da0015d6228839323583
&lt;span class="p"&gt;Add support for dark color scheme in default index.html page
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;Add a little CSS to index.html to support dark color schemes.
This will display the index page in dark colors if the user has
requested a dark color scheme in their system UI or browser, and
display the same as the previous version if not.
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;See: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gh"&gt;diff -r dda421871bc2 -r 81294b370e77 docs/html/index.html
&lt;/span&gt;&lt;span class="gd"&gt;--- a/docs/html/index.html      Tue Aug 10 23:43:17 2021 +0300
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/docs/html/index.html      Sat Aug 14 07:44:13 2021 -0700
&lt;/span&gt;&lt;span class="p"&gt;@@ -8,6 +8,15 @@&lt;/span&gt;
         margin: 0 auto;
         font-family: Tahoma, Verdana, Arial, sans-serif;
     }
&lt;span class="gi"&gt;+    @media (prefers-color-scheme: dark) {
+               body {
+                       background-color: #363839;
+                       color: #d1cec9;
+               }
+               a {
+                       color: #c4c4ff;
+               }
+    }
&lt;/span&gt; &amp;lt;/style&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_back_forth_on_the_mailing_list"&gt;Back &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Forth on the Mailing&amp;nbsp;List&lt;/h2&gt;&lt;p&gt;This is &lt;a href="https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014217.html"&gt;my thread on the mailing lists web archive&lt;/a&gt;, showing the back &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; forth discussion which ensued after my initial&amp;nbsp;email.&lt;/p&gt;
&lt;p&gt;First Maxim Dounin, one of the nginx core developers, politely said no&amp;nbsp;thanks:&lt;/p&gt;
&lt;div class="quote-block"&gt;&lt;blockquote&gt;&lt;p&gt;Thank you for the patch.  I don&amp;#8217;t think this is something we want
to customize in the example pages such as index.html, especially
given that we don&amp;#8217;t set other&amp;nbsp;colors.&lt;/p&gt;&lt;footer&gt;&amp;#8212; &lt;cite&gt;Maxim Dounin, https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014218.html&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;p&gt;My heart sank&amp;#8230;&amp;#8203; but after a while I came back and read it again&amp;#8230;&amp;#8203; &amp;#8220;especially given that we don&amp;#8217;t set other colors.&amp;#8221;. I guess I shouldn&amp;#8217;t have hardcoded those dark colors in my change&amp;#8201;&amp;#8211;&amp;#8201;that&amp;#8217;s fair enough. I then started looking to see if there was a way to tell the browser to just use its own built-in stylesheet for the dark-mode colors, just like it does for the regular, light-mode&amp;nbsp;version.&lt;/p&gt;
&lt;p&gt;A little while later, Steffan Weber replied with the&amp;nbsp;answer:&lt;/p&gt;
&lt;div class="quote-block"&gt;&lt;blockquote&gt;&lt;p&gt;You could add the following line which makes modern browsers use colors
from their built-in dark color&amp;nbsp;scheme:&lt;/p&gt;
&lt;p&gt;&amp;lt;meta name=&amp;#8221;color-scheme&amp;#8221; content=&amp;#8221;light&amp;nbsp;dark&amp;#8221;&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="bare" href="https://web.dev/color-scheme/"&gt;https://web.dev/color-scheme/&lt;/a&gt;&lt;/p&gt;&lt;footer&gt;&amp;#8212; &lt;cite&gt;Steffan Weber, https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014220.html&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;p&gt;So, &lt;a href="https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014221.html"&gt;I said I&amp;#8217;d submit a new updated patch in the morning&lt;/a&gt;.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_second_try"&gt;Second&amp;nbsp;Try&lt;/h2&gt;&lt;p&gt;So, next morning I did some research &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; testing, then reset my repository to have another&amp;nbsp;go:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hg strip tip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I then &lt;a href="https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014222.html"&gt;sent in this patch&lt;/a&gt;, with Steffan&amp;#8217;s suggested change, which did exactly what we&amp;nbsp;wanted:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;# HG changeset patch
# User Duncan Lock &amp;lt;duncan.lock@gmail.com&amp;gt;
# Date 1629049097 25200
#      Sun Aug 15 10:38:17 2021 -0700
# Node ID 945d9836012ed84dea05577027a30a38e38a59f3
# Parent  dda421871bc213dd2eb3da0015d6228839323583
&lt;span class="p"&gt;Add support for dark color scheme in default index &amp;amp; 50x pages
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;Add a meta tag to index.html &amp;amp; 50x.html to support dark color schemes.
This will display the index page in dark colors if the user has
requested a dark color scheme in their system UI or browser, and
display the same as the previous version if not.
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;This uses the browsers built-in styles and doesn't hard code any colors or styles.
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gh"&gt;diff -r dda421871bc2 -r 945d9836012e docs/html/50x.html
&lt;/span&gt;&lt;span class="gd"&gt;--- a/docs/html/50x.html        Tue Aug 10 23:43:17 2021 +0300
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/docs/html/50x.html        Sun Aug 15 10:38:17 2021 -0700
&lt;/span&gt;&lt;span class="p"&gt;@@ -2,6 +2,7 @@&lt;/span&gt;
 &amp;lt;html&amp;gt;
 &amp;lt;head&amp;gt;
 &amp;lt;title&amp;gt;Error&amp;lt;/title&amp;gt;
&lt;span class="gi"&gt;+&amp;lt;meta name="color-scheme" content="light dark"&amp;gt;
&lt;/span&gt; &amp;lt;style&amp;gt;
     body {
         width: 35em;
&lt;span class="gh"&gt;diff -r dda421871bc2 -r 945d9836012e docs/html/index.html
&lt;/span&gt;&lt;span class="gd"&gt;--- a/docs/html/index.html      Tue Aug 10 23:43:17 2021 +0300
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/docs/html/index.html      Sun Aug 15 10:38:17 2021 -0700
&lt;/span&gt;&lt;span class="p"&gt;@@ -2,6 +2,7 @@&lt;/span&gt;
 &amp;lt;html&amp;gt;
 &amp;lt;head&amp;gt;
 &amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&lt;span class="gi"&gt;+&amp;lt;meta name="color-scheme" content="light dark"&amp;gt;
&lt;/span&gt; &amp;lt;style&amp;gt;
     body {
         width: 35em;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then &lt;a href="https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014223.html"&gt;Maxim replied&lt;/a&gt; saying, essentially, &amp;#8220;better, but I&amp;#8217;d rather use &lt;span class="caps"&gt;CSS&lt;/span&gt; instead of adding a meta tag&amp;#8221; - i.e. remove the meta tag and add this &lt;span class="caps"&gt;CSS&lt;/span&gt;: &lt;code&gt;html { color-scheme: light dark; }&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Maxim also included &lt;em&gt;his&lt;/em&gt; patch which implemented this and asked me to test&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;I &lt;a href="https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014224.html"&gt;tested his patch, which worked great and then made a suggestion about removing the hardcoded fonts too&lt;/a&gt;, which was declined,&amp;nbsp;sadly.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_done_released"&gt;Done &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;Released&lt;/h2&gt;&lt;p&gt;Maxim &lt;a href="https://mailman.nginx.org/pipermail/nginx-devel/2021-August/014226.html"&gt;then committed his final version of the patch&lt;/a&gt;&amp;#8230;&amp;#8203; and that was&amp;nbsp;it!&lt;/p&gt;
&lt;p&gt;So, in the end, I didn&amp;#8217;t &lt;em&gt;actually&lt;/em&gt; get any of &lt;em&gt;my&lt;/em&gt; code into nginx, but the process was fairly painless, everyone was polite and helpful&amp;#8201;&amp;#8211;&amp;#8201;and the nginx welcome &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; error pages now respect your dark mode preferences. I did at least get my name in the&amp;nbsp;changelog:&lt;/p&gt;
&lt;div class="quote-block"&gt;&lt;blockquote&gt;&lt;p&gt;Dark mode support in welcome and 50x error&amp;nbsp;pages.&lt;/p&gt;
&lt;p&gt;Prodded by Duncan&amp;nbsp;Lock.&lt;/p&gt;&lt;footer&gt;&amp;#8212; &lt;cite&gt;&lt;a href="https://trac.nginx.org/nginx/timeline?from=08%2F16%2F21&amp;amp;daysback=5&amp;amp;authors=&amp;amp;changeset=on&amp;amp;repo-nginx=on&amp;amp;repo-nginx-tests=on&amp;amp;repo-nginx_org=on&amp;amp;milestone=on&amp;amp;ticket=on&amp;amp;ticket_details=on&amp;amp;wiki=on&amp;amp;update=Update#"&gt;Nginx Changelog&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;p&gt;This is what the welcome page code looks like&amp;nbsp;now:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;table class="linenotable"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="linenos gl"&gt;&lt;pre class="lineno"&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Welcome to nginx!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="hll"&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Tahoma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Verdana&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to nginx!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;For online documentation and support please refer to
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://nginx.org/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;nginx.org&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
Commercial support is available at
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://nginx.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;nginx.com&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&lt;/span&gt;Thank you for using nginx.&lt;span class="nt"&gt;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This got released on Aug 31&lt;sup&gt;st&lt;/sup&gt; 2021, as part of nginx 1.21.2&amp;nbsp;🎉&lt;/p&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Sat, 14 Aug 2021 18:23:54 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/08/14/how-to-contribute-a-change-to-nginx/</guid></item><item><title/><link>https://hth.is/konnun/</link><description>Markaðskönnun Link to heading Takk fyrir að vilja taka þátt í þessari markaðskönnun!
Spurningarnar snúa að húsfélögum í fjölbýli og hverskonar vandamálum þau finna fyrir í daglegum rekstri. Einnig er spurt um hverskonar þjónustu þau nýta sér. Öll innsend svör eru nafnlaus.
Könnunin er viljandi stutt og ætti ekki að taka meira en 5 mínútur að svara henni.
Könnunina á íslensku má finna hér.
Með fyrirfram þökk,
Hrafn Þorvaldsson</description><author>Hrafn Thorvaldsson</author><pubDate>Sat, 14 Aug 2021 14:31:00 GMT</pubDate><guid isPermaLink="true">https://hth.is/konnun/</guid></item><item><title/><link>https://hth.is/survey/</link><description>Marketing survey Link to heading Thank you for wanting to take part in this marketing survey!
The questions revolve around the kinds of day to day problems housing unions in Iceland may have, and what kinds of services they use. All submissions are anonymous.
The survey is short and shouldn&amp;rsquo;t take much more than 5 minutes to answer.
The survey in English may be found here
Thank you for the consideration,</description><author>Hrafn Thorvaldsson</author><pubDate>Sat, 14 Aug 2021 14:31:00 GMT</pubDate><guid isPermaLink="true">https://hth.is/survey/</guid></item><item><title>ArchUnit check if abstract classes are abstract</title><link>https://b.agilob.net/programming/java/archunit-test-abstract-classes-are-abstract/</link><description>@Test public void abstractClassesAreAbstract() { final JavaClasses importedClasses = new ClassFileImporter() .importPackages("net.agilob.project"); LoggingRulesTest.ABSTRACT_CLASS_MUST_BE_ABSTRACT.check(importedClasses); } public static final ArchRule ABSTRACT_CLASS_MUST_BE_ABSTRACT = classes() .that() .haveSimpleNameContaining("Abstract").or().haveSimpleNameContaining("abstract") .should() .haveModifier(JavaModifier.ABSTRACT);</description><author>Random Notes by agilob</author><pubDate>Sat, 14 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/programming/java/archunit-test-abstract-classes-are-abstract/</guid></item><item><title>Core Haskell tools</title><link>https://gilmi.me/post/2021/08/14/hs-core-tools</link><description>An overview of the core tools in the Haskell toolchain. Specifically, ghc, runghc and ghci</description><author>λm.me</author><pubDate>Sat, 14 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://gilmi.me/post/2021/08/14/hs-core-tools</guid></item><item><title>Demystifying Rails autoloading</title><link>https://kyrofa.com/posts/demystifying-rails-autoloading/</link><description>When I first started learning Rails back in the day, it was my first introduction to Ruby: I was learning them both at the same time. As a result, the line between them was rather blurry; I didn&amp;rsquo;t know what was coming from Ruby, and what was coming from Rails. The Rails approach of monkey-patching Ruby didn&amp;rsquo;t help. If I&amp;rsquo;m being honest, I didn&amp;rsquo;t realize that Object#blank? wasn&amp;rsquo;t a Ruby method until only a few years ago.</description><author>kyrofa's blog</author><pubDate>Sat, 14 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://kyrofa.com/posts/demystifying-rails-autoloading/</guid></item><item><title>Trust the Maintainers</title><link>https://www.macchaffee.com/blog/2021/maintenance/</link><description>&lt;p&gt;Since &lt;a href="https://en.wikipedia.org/wiki/Colonial_Pipeline_ransomware_attack"&gt;cyber attacks&lt;/a&gt;, a &lt;a href="https://en.wikipedia.org/wiki/Surfside_condominium_collapse"&gt;building collapse&lt;/a&gt;, and an &lt;a href="https://en.wikipedia.org/wiki/Infrastructure_Investment_and_Jobs_Act"&gt;infrastructure bill&lt;/a&gt; have been in the headlines recently, I think the time has come for us to re-assess our relationship with &lt;em&gt;maintenance&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;While I work in the "tech" field, I don't align myself with inventors, entrepreneurs, or green-field software engineers. I align myself with the whole spectrum of "maintainers", like civil engineers, power plant operators, janitors, building inspectors, security compliance officers, mechanics, etc. These jobs are mostly all considered respectable, but a building inspector will never get the same prestige as someone who invents a new gadget, founds a new company, or develops a new popular app. Why is that?&lt;/p&gt;
&lt;p&gt;There's an organization called &lt;a href="https://themaintainers.org/why-do-people-neglect-maintenance/"&gt;The Maintainers&lt;/a&gt; which has a comprehensive list of reasons why we don't value maintenance, ranging from cognitive biases to historical reasons to financial incentives. But I'd like to focus on trust, and how mistrusting maintainers can lead to putting off maintenance.&lt;/p&gt;
&lt;h3 id="trust"&gt;Trust&lt;/h3&gt;
&lt;p&gt;Trust has been dropping across the board according to the &lt;a href="https://www.edelman.com/sites/g/files/aatuss191/files/2021-03/2021%20Edelman%20Trust%20Barometer.pdf"&gt;Edelman Trust Barometer&lt;/a&gt;. I think the ripple effects of this are also being felt by the maintainers of the world. Maintainers are always vying for the trust of decision-makers to get anything done. That leaky pipe won't get fixed unless the suits approve the purchase order. But this imbalance of power is so frustratingly misplaced. The plumber who noticed the leaky pipes &lt;em&gt;is the subject-matter expert&lt;/em&gt;. They are well-aware that new pipes cost money, but they've seen (or learned about) the risk of leaving it unfixed. The person holding the checkbook hasn't. But somehow they get the final say? Why is the subject-matter expert not trusted to make the cost-benefit analysis?&lt;/p&gt;
&lt;p&gt;The world of software security is another example where trust is lacking. In my experience, half the work is just convincing leadership to care the tiniest bit about security, which requires trust. Since it's difficult to explain e.g. a &lt;a href="https://en.wikipedia.org/wiki/Side-channel_attack"&gt;side-channel attack&lt;/a&gt; to someone non-technical, your chances of getting resources devoted to fixing the issue are slim, unless they trust you already.&lt;/p&gt;
&lt;p&gt;I'd say usually this mistrust is unfounded (based on bias, misunderstanding, or otherwise). But it's true there are some untrustworthy maintainers in our ranks. When kids get their first car, they're often warned of crooked mechanics. Or in the software world, "&lt;a href="https://doi.org/10.1109/ICSE-SEIS52602.2021.00011"&gt;resume-driven development&lt;/a&gt;" is similarly a problem. Perhaps some kind of formal licensure could help build trust, like requiring adherence to the &lt;a href="https://ethics.acm.org/code-of-ethics/software-engineering-code/"&gt;ACM Software Engineering Code of Ethics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There's no doubt that maintenance is necessary and that we can never seem to do enough of it. But setting aside the cognitive biases or financial incentives against it, consider how trusting subject-matter experts plays into it. Think of the last time an outsider completely butchered an explanation of your hobby or field of study; that's how maintainers feel every time they get overruled by laymen.&lt;/p&gt;</description><author>Mac's Tech Blog</author><pubDate>Sat, 14 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.macchaffee.com/blog/2021/maintenance/</guid></item><item><title>Health and Taking Care of Yourself</title><link>https://lambdaland.org/posts/2021-08-14_health/</link><description>&lt;p&gt;It’s going to be another long night. My baby has reached the point where she’s too tired to sleep. She alternates between screaming at 90+ decibels and sleeping fitfully. She only transfers from the crying state to the sleep state after prolonged, labored rocking and soothing. She transfers back to the base crying state on her own after a few seconds.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sat, 14 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-08-14_health/</guid></item><item><title>Do you know where your Phone Maid is?</title><link>https://stop.zona-m.net/2021/08/do-you-know-where-your-phone-maid-is/</link><description>&lt;p&gt;And what they are DOING with her?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 14 Aug 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/do-you-know-where-your-phone-maid-is/</guid></item><item><title>The missing guide for Arch Linux PKGBUILD's pkgver() version numbers</title><link>https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/</link><description>&lt;p&gt;Pacman's version comparison algorithm was designed over a decade ago to properly sort many categories of real-world version numbers, and is now set in stone, quirks and all. Later on, the AUR developed &lt;code&gt;pkgver()&lt;/code&gt; conventions and templates which turn Git commits into version numbers that would sort properly in Pacman. But what are Pacman's requirements for sorting real-world version numbers, how does Pacman's version comparison algorithm work, and how are AUR &lt;code&gt;pkgver()&lt;/code&gt; built around the algorithm?&lt;/p&gt;
&lt;h1 id="how-pacman-compares-versions"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#how-pacman-compares-versions"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
How Pacman compares versions&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;vercmp&lt;/code&gt; is a command-line utility which takes two string arguments and compares them using Pacman's version comparison algorithm.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;vercmp&lt;/code&gt; executable exposes the algorithm used by Pacman to determine whether a different package version is newer than what you have currently installed. Sadly, https://man.archlinux.org/man/vercmp.8 (as well as the pacman manpage) is inadequate and fails to explain the algorithm, only providing a few examples.&lt;/p&gt;
&lt;h2 id="requirements-for-comparing-versions"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#requirements-for-comparing-versions"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Requirements for comparing versions&lt;/h2&gt;
&lt;p&gt;Pacman needs to compare the versions of real-world software programs and its own conventions correctly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0-beta &amp;lt; 1.0 (from semver)
&lt;ul&gt;
&lt;li&gt;pacman and vercmp fail to fulfill this requirement, because it interprets &lt;code&gt;-beta&lt;/code&gt; as build metadata (see &lt;code&gt;parseEVR()&lt;/code&gt; &lt;code&gt;-release&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1.0beta &amp;lt; 1.0 (Arch labels pre-release packages as 1.0beta rather than 1.0-beta)&lt;/li&gt;
&lt;li&gt;1.0 &amp;lt; 1.0.1&lt;/li&gt;
&lt;li&gt;1.0.1 &amp;lt; 1.0.2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pacman's version comparison algorithm also has incidental properties that I don't consider to be first principles. However, AUR &lt;code&gt;pkgver()&lt;/code&gt; depend on certain ones to generate unusual-looking unintuitive version numbers that nonetheless sort properly in Pacman.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0 &amp;lt; 1.0.0 (I think they should be equal)&lt;/li&gt;
&lt;li&gt;alpha &amp;lt; beta &amp;lt; 1.0&lt;/li&gt;
&lt;li&gt;1.0 &amp;lt; 1.0.alpha (it's strange that 1.0 &amp;lt; 1.0.alpha &amp;lt; 1.0.0)&lt;/li&gt;
&lt;li&gt;1.0.alpha &amp;lt; 1.0.0&lt;/li&gt;
&lt;li&gt;1.0.alpha &amp;lt; 1.0.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="algorithm-implementation"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#algorithm-implementation"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Algorithm implementation&lt;/h2&gt;
&lt;p&gt;The algorithm is implemented in &lt;code&gt;alpm_pkg_vercmp()&lt;/code&gt; in the Pacman source code (&lt;a href="https://gitlab.archlinux.org/pacman/pacman/-/blob/master/lib/libalpm/version.c" rel="external"&gt;&lt;code&gt;:lib/libalpm/version.c&lt;/code&gt;&lt;/a&gt;). The file is 260 lines of code, with multiple functions dedicated to different aspects of version comparison. The algorithm is written in raw C, with &lt;em&gt;glorious&lt;/em&gt; null-terminated strings, and string slicing implemented via &lt;code&gt;const&lt;/code&gt;-incompatible null byte insertion. 😿&lt;/p&gt;
&lt;h3 id="epoch-version-and-release"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#epoch-version-and-release"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Epoch, version, and release&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;parseEVR()&lt;/code&gt; parses Arch package versions using the format &lt;code&gt;[epoch:]version[-release]&lt;/code&gt;. More specifically, all characters after the last hyphen form the release (even if there are colons afterwards), and the epoch is "0" unless the first non-digit is a colon. If no epoch is present, the epoch is labeled as 0.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;parseEVR()&lt;/code&gt; allows only numbers in the epoch field. It is usually absent, but can be used as a "major version" to ensure that newer program versions compare higher, even if the newer program's version number (stored in the version field) is &lt;em&gt;lower&lt;/em&gt; than in older versions.&lt;/p&gt;
&lt;p&gt;The release field is an optional location for "build metadata". A version with no release field is considered equal to otherwise-identical versions with any release field, but two otherwise-identical versions with different release fields use the release field to break ties.&lt;/p&gt;
&lt;h3 id="comparing-versions"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#comparing-versions"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Comparing versions&lt;/h3&gt;
&lt;p&gt;Each field is then compared using &lt;code&gt;rpmvercmp()&lt;/code&gt;. Missing epochs are assumed to be 0, and missing releases are assumed to be equal to any numbered release.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rpmvercmp()&lt;/code&gt; decomposes its input into "segments", where each segment starts with 0 or more "separator" characters (any non-alphanumeric character), which are followed by 1 or more "body" characters (each body contains either alphabetic characters or numeric characters, so "1a" is 2 segments). The input may be terminated by a "dangling" segment with only separator characters and no body (but realistic version numbers will not have a dangling segment).&lt;/p&gt;
&lt;p&gt;This can be modeled as the regex &lt;code&gt;([^a-zA-Z0-9]* ([a-zA-Z]+ | [0-9]+) )* [^a-zA-Z0-9]*&lt;/code&gt; more or less.&lt;/p&gt;
&lt;p&gt;Both inputs are split into segments (including dangling segments), starting at the beginning. The algorithm loops over segments from both inputs, starting with the first segment from each, until either input runs out of segments entirely (one or both segments are absent).&lt;/p&gt;
&lt;p&gt;Each loop iteration receives one segment from each version, for as long as both versions have segments remaining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All leading separators are trimmed off both segments. Results:
&lt;ul&gt;
&lt;li&gt;1.1 = 1_1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If either segment is empty after trimming separators (because it's a dangling segment), the loop breaks.&lt;/li&gt;
&lt;li&gt;If one segment started with more separator characters, it's a larger version. Note that the Pacman developers believe that realistic version numbers do not have multiple separator characters in a row, and Pacman isn't designed to handle this situation perfectly. Results:
&lt;ul&gt;
&lt;li&gt;1 &amp;lt; .1 = _1 &amp;lt; ..1&lt;/li&gt;
&lt;li&gt;1.1 &amp;lt; 1..1&lt;/li&gt;
&lt;li&gt;1.a &amp;lt; 1..a&lt;/li&gt;
&lt;li&gt;1rev &amp;lt; 1.rev &amp;lt; 1..rev&lt;/li&gt;
&lt;li&gt;a10 &amp;lt; a.10&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Alphabetic segments are sorted lexicographically, and sort before numeric segments (sorted numerically). Results:
&lt;ul&gt;
&lt;li&gt;a &amp;lt; aa &amp;lt; z &amp;lt; zz &amp;lt; 1 = 01 &amp;lt; 9 &amp;lt; 10&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The function returns immediately if the loop finds a pair of segments that compare unequal. Otherwise the loop stops (without stripping separators) when one or both inputs reach the end of line, or breaks (after stripping separators) when one or both inputs reach a final dangling segment.&lt;/p&gt;
&lt;p&gt;At this point, one of these is true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;at least one version has no segment.&lt;/li&gt;
&lt;li&gt;no versions have missing segments, but at least one version has a dangling segment (causing both segments to be stripped, so at least one version &lt;em&gt;now&lt;/em&gt; has no segment).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The segments are compared as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;none = none&lt;/li&gt;
&lt;li&gt;none &amp;gt; alpha&lt;/li&gt;
&lt;li&gt;none &amp;lt; separator or number&lt;/li&gt;
&lt;li&gt;alpha &amp;lt; none&lt;/li&gt;
&lt;li&gt;separator or number &amp;gt; none&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The algorithm is complete.&lt;/p&gt;
&lt;p&gt;All dangling segments compare equal to one another, but come after "segment with text" and "no segment" and before "segment with number".&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1a &amp;lt; 1 &amp;lt; 1.a &amp;lt; 1. = 1.. &amp;lt; 1.0&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;'' &amp;lt; '.' = '..'&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 &amp;lt; 1. = 1_ = 1..&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately this algorithm has a cycle, caused by how more leading separators wins a version comparison (even if followed by a losing body) if both segments have bodies, but gets ignored if one or both segments are empty after trimming.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0 &amp;lt; 1..a (more leading separators wins since both segments have bodies)&lt;/li&gt;
&lt;li&gt;1..a &amp;lt; 1. (leading separators ignored since 1. is empty after trimming, 'a' &amp;lt; '')&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;&amp;lt; 1.0 (leading separators ignored since 1. is empty after trimming, '' &amp;lt; '1')&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that 1. and 1... are interchangeable, because the dangling separators get stripped out either way.&lt;/p&gt;
&lt;p&gt;The Pacman developers commented, "Fun example :) Like I said, having multiple delimiters in a row doesn't make a lot of sense, so that is pretty much undefined behaviour"&lt;/p&gt;
&lt;h2 id="testing-the-requirements"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#testing-the-requirements"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Testing the requirements&lt;/h2&gt;
&lt;p&gt;Dangling segments and multiple separators don't occur in real-world version numbers and can be ignored. Does this algorithm properly order real-world versions?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0beta &amp;lt; 1.0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, "beta" &amp;lt; "".&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0 &amp;lt; 1.0.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, "" &amp;lt; ".1".&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0.1 &amp;lt; 1.0.2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, ".1" &amp;lt; ".2".&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.0 &amp;lt; 1.0.alpha&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, "" &amp;lt; ".alpha"&lt;/p&gt;
&lt;h1 id="what-is-pkgbuild-and-pkgver"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#what-is-pkgbuild-and-pkgver"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
What is PKGBUILD and &lt;code&gt;pkgver&lt;/code&gt;?&lt;/h1&gt;
&lt;p&gt;PKGBUILD files are shell scripts defining variables and functions used by &lt;code&gt;makepkg&lt;/code&gt; to build a binary package. The &lt;code&gt;pkgver&lt;/code&gt; variable serves as the version number of the PKGBUILD and the package produced. All PKGBUILD files contain a &lt;code&gt;pkgver&lt;/code&gt; variable, storing the package's version at the time the file was written. However, this is insufficient for VCS/&lt;code&gt;-git&lt;/code&gt; packages tracking the latest commit in a Git repository, where the version of software built by a PKGBUILD can change even when the PKGBUILD does not. To accommodate this, &lt;code&gt;makepkg&lt;/code&gt; also supports a &lt;code&gt;pkgver()&lt;/code&gt; function, which when run produces the &lt;em&gt;current&lt;/em&gt; version of the package.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;pkgver&lt;/code&gt; is a variable only, then an unmodified PKGBUILD and &lt;code&gt;pkgver&lt;/code&gt; means the package has not been updated. But if a &lt;code&gt;pkgver()&lt;/code&gt; function is present, then an AUR helper trying to determine if an installed package is outdated must re-clone/pull the VCS repo listed in &lt;code&gt;source=(...)&lt;/code&gt; and call &lt;code&gt;pkgver()&lt;/code&gt; again, even if the PKGBUILD and &lt;code&gt;pkgver&lt;/code&gt; are unmodified.&lt;/p&gt;
&lt;p&gt;If a &lt;code&gt;pkgver()&lt;/code&gt; function is present, then running &lt;code&gt;makepkg&lt;/code&gt; to build the PKGBUILD into a binary package also rewrites the PKGBUILD file with a &lt;em&gt;new&lt;/em&gt; value for the &lt;code&gt;pkgver&lt;/code&gt; variable. A few fixed-version packages like &lt;a href="https://github.com/archlinux/svntogit-packages/blob/master/qt5-base/trunk/PKGBUILD" rel="external"&gt;qt5-base&lt;/a&gt; and &lt;a href="https://github.com/archlinux/svntogit-packages/blob/master/qt5-wayland/trunk/PKGBUILD" rel="external"&gt;qt5-wayland&lt;/a&gt; use this property by defining a &lt;code&gt;pkgver()&lt;/code&gt; function to automatically recompute complex version numbers. Unlike &lt;code&gt;-git&lt;/code&gt; packages in the AUR, these PKGBUILDs build a fixed version of the source code, and their &lt;code&gt;pkgver()&lt;/code&gt; functions return a fixed value.&lt;/p&gt;
&lt;h1 id="building-a-pkgver-so-pacman-sorts-git-repositories-correctly"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#building-a-pkgver-so-pacman-sorts-git-repositories-correctly"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Building a &lt;code&gt;pkgver()&lt;/code&gt; so Pacman sorts Git repositories correctly&lt;/h1&gt;
&lt;p&gt;Git repositories in the wild have a lot of variance; some don't have tags, some have tags that sort properly, and some have tags in the wrong order. And some repositories start with no tags, but create tags later on when they make their first release.&lt;/p&gt;
&lt;h2 id="requirements-for-comparing-versions-1"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#requirements-for-comparing-versions-1"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Requirements for comparing versions&lt;/h2&gt;
&lt;p&gt;What are the requirements for generating version numbers from a Git repository?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As a repository without tags creates more commits, the version number should increase.&lt;/li&gt;
&lt;li&gt;When a repository creates its first release/tag, the version number should increase.&lt;/li&gt;
&lt;li&gt;As a repository with tags creates more commits, the version number should increase.&lt;/li&gt;
&lt;li&gt;If the most recent tag changes from 1.0 to 1.1, the version number should increase.&lt;/li&gt;
&lt;li&gt;If the most recent tag changes from 1.0 to 1.0.1, the version number should increase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How can we achieve these criteria, given how Pacman works?&lt;/p&gt;
&lt;h2 id="arch-wiki-templates"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#arch-wiki-templates"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Arch Wiki templates&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://wiki.archlinux.org/index.php/VCS_package_guidelines#The_pkgver()_function" rel="external"&gt;The Arch wiki&lt;/a&gt; provides copy-paste snippets of example pkgver() functions, but fails to explain the underlying concepts (what &lt;code&gt;git describe&lt;/code&gt; outputs, what the sed expression does, how the resulting expression is evaluated by &lt;code&gt;vercmp&lt;/code&gt; and &lt;code&gt;pacman&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="untagged-git-repositories"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#untagged-git-repositories"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Untagged Git repositories&lt;/h3&gt;
&lt;p&gt;In a Git repo where the history of &lt;code&gt;master&lt;/code&gt; has no tags, the recommended &lt;code&gt;pkgver()&lt;/code&gt; counts commits:&lt;/p&gt;
&lt;pre class="giallo" style="color: #1F2328; background-color: #FFFFFF;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span style="color: #8250DF;"&gt;pkgver&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #0550AE;"&gt;  cd&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$pkgname&lt;/span&gt;&lt;span style="color: #0A3069;"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #0550AE;"&gt;  printf&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; &amp;quot;r%s.%s&amp;quot; &amp;quot;$(&lt;/span&gt;&lt;span style="color: #953800;"&gt;git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; rev-list&lt;/span&gt;&lt;span style="color: #0550AE;"&gt; --count&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; HEAD)&amp;quot; &amp;quot;$(&lt;/span&gt;&lt;span style="color: #953800;"&gt;git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; rev-parse&lt;/span&gt;&lt;span style="color: #0550AE;"&gt; --short&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; HEAD)&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces a string &lt;code&gt;r{number of commits}.{commit hash}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Any letter would work equally well for the version comparison algorithm, &lt;code&gt;r&lt;/code&gt; was chosen because it sounds like "revision". But what is the purpose of a letter?&lt;/p&gt;
&lt;h3 id="tagged-git-repositories"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#tagged-git-repositories"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Tagged Git repositories&lt;/h3&gt;
&lt;p&gt;If the repo has tags like 0.2.5 which begin with a number (no leading "v" prefix like v0.2.5), &lt;code&gt;git describe --long --tags&lt;/code&gt; can be used as the root source for the version:&lt;/p&gt;
&lt;pre class="giallo" style="color: #1F2328; background-color: #FFFFFF;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span style="color: #8250DF;"&gt;pkgver&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #0550AE;"&gt;  cd&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$pkgname&lt;/span&gt;&lt;span style="color: #0A3069;"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #953800;"&gt;  git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; describe&lt;/span&gt;&lt;span style="color: #0550AE;"&gt; --long --tags&lt;/span&gt;&lt;span style="color: #CF222E;"&gt; |&lt;/span&gt;&lt;span style="color: #953800;"&gt; sed&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; 's/\([^-]*-g\)/r\1/;s/-/./g'&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git describe --long&lt;/code&gt; produces a string with format &lt;code&gt;{most recent tag}-{commits since tag}-g{commit hash}&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="giallo" style="color: #1F2328; background-color: #FFFFFF;"&gt;&lt;code&gt;&lt;span class="giallo-l"&gt;&lt;span style="color: #953800;"&gt;git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; checkout master&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #953800;"&gt;git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; describe&lt;/span&gt;&lt;span style="color: #0550AE;"&gt; --long --tags&lt;/span&gt;&lt;span style="color: #6E7781;"&gt;  # v2.4-25-ga240b43&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #953800;"&gt;git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; checkout v2.4&lt;/span&gt;&lt;span style="color: #6E7781;"&gt;  # or git checkout HEAD~25&lt;/span&gt;&lt;/span&gt;
&lt;span class="giallo-l"&gt;&lt;span style="color: #953800;"&gt;git&lt;/span&gt;&lt;span style="color: #0A3069;"&gt; describe&lt;/span&gt;&lt;span style="color: #0550AE;"&gt; --long --tags&lt;/span&gt;&lt;span style="color: #6E7781;"&gt;  # v2.4-0-g51e51f4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sed expression turns it into &lt;code&gt;{most recent tag}.r{commits since tag}.g{commit hash}&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="testing-the-requirements-1"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#testing-the-requirements-1"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Testing the requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;As a repository without tags creates more commits, the version number should increase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;"r###" &amp;lt; "r###+1"? Trivially so, as the "r" segment is the same, but the "number of commits" segment increases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a repository creates its first release/tag, the version number should increase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;"r###" &amp;lt; "1.0.r###"? Yes. The version of the untagged repository starts with a "r" segment. The version of the tagged repository starts with a numeric segment (taken from the tag), which comes after.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As a repository with tags creates more commits, the version number should increase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;"1.0.r###" &amp;lt; "1.0.r###+1"? Yes. "most recent tag" is unchanged, ".r" is unchanged, and "commits since tag" increases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the most recent tag changes from 1.0 to 1.1, the version number should increase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;"1.0.r###" &amp;lt; "1.1.r###"? Yes. "most recent tag" increases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the most recent tag changes from 1.0 to 1.0.1, the version number should increase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;"1.0.r###" &amp;lt; "1.0.1.r###"? Yes. "1"="1", ".0"=".0", and ".r" &amp;lt; ".1".&lt;/p&gt;
&lt;h3 id="why-not"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#why-not"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Why not "."?&lt;/h3&gt;
&lt;p&gt;If tag-based versions were &lt;code&gt;{most recent tag}.{commits since tag}...&lt;/code&gt;, then if the most recent tag changes from 1.0 to 1.0.1, the version would change from "1.0.###" to "1.0.1.###", where ".1" sorts &lt;em&gt;before&lt;/em&gt; ".###" despite 1.0.1 being a newer program version.&lt;/p&gt;
&lt;p&gt;This was first brought up by @diabonas:archlinux.org:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You need it because otherwise 1.0.500 (where 500 is the revision count) would be newer than 1.0.1.30 (again, 30 is the revision count) - this doesn't happen with 1.0.r500, which is older than 1.0.1.r30 because a letter is always older than a digit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="why-not-r"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#why-not-r"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
Why not "r"?&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;It's 1.0.1.r30 - the dot is important as 1.0.1r30 would be older than 1.0.1, but 1.0.1.r30 is newer - it's a revision after 1.0.1 after all. And yeah, 1.0r31 is a revision after 1.0, but before the next upstream release 1.0.1, whole 1.0.1.r31 is a revision after 1.0.1, so newer than 1.0.r30&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="the-arch-wiki-is-wrong"&gt;&lt;a class="zola-anchor" href="https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/#the-arch-wiki-is-wrong"&gt;&lt;span&gt;🔗&lt;/span&gt;&lt;/a&gt;
The Arch Wiki is wrong&lt;/h2&gt;
&lt;p&gt;The Arch wiki's stated requirements for generating version numbers are:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is recommended to have following version format: &lt;em&gt;RELEASE.rREVISION&lt;/em&gt; where &lt;em&gt;REVISION&lt;/em&gt; is a monotonically increasing number that uniquely identifies the source tree (VCS revisions do this).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Arch wiki is wrong; given the "RELEASE.RELEASE.rREVISION" convention recommended by the wiki, for Pacman to properly identify older and newer packages, REVISION does not need to be globally monotonic, only within a given RELEASE. And the Arch wiki even breaks its own rules: the example "Git with tags" &lt;code&gt;pkgver()&lt;/code&gt;'s REVISION is not monotonic except within a given RELEASE (Git tag).&lt;/p&gt;
&lt;p&gt;Even if the Arch wiki was changed to say that REVISION needs to be monotonic within a given RELEASE, it states that &lt;code&gt;0.1.r456 &amp;gt; r454&lt;/code&gt; but &lt;code&gt;0.1.456 &amp;lt; 454&lt;/code&gt;, without explaining the algorithm used to compare revisions. This only serves to confuse the reader.&lt;/p&gt;</description><author>nyanpasu64's blog</author><pubDate>Fri, 13 Aug 2021 17:21:00 GMT</pubDate><guid isPermaLink="true">https://nyanpasu64.gitlab.io/blog/the-missing-guide-for-arch-linux-pkgbuild-s-pkgver-version-numbers/</guid></item><item><title>Go Web应用中常见的反模式</title><link>https://www.4async.com/2021/08/common-anti-patterns-in-go-web-applications/</link><description>&lt;img alt="Featured image of post Go Web应用中常见的反模式" src="https://www.4async.com/2020/02/2020-02-16-moving-towards-domain-driven-design-in-go/cover_huea63e6370dcf0c375755d886a5d0b9c6_97708_1600x0_resize_q75_box.jpg" /&gt;&lt;blockquote&gt;
&lt;p&gt;作者：Miłosz Smółka &lt;br /&gt;
译者：Kevin &lt;br /&gt;
原文地址：https://threedots.tech/post/common-anti-patterns-in-go-web-applications/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;在我职业生涯的某个阶段，我对我所构建的软件不再感到兴奋。&lt;/p&gt;
&lt;p&gt;我最喜欢的工作内容是底层的细节和复杂的算法。在转到面向用户的应用开发之后，这些内容基本消失了。编程似乎是利用现有的库和工具把数据从一处移至另一处。到目前为止，我所学到的关于软件的知识不再那么有用了。&lt;/p&gt;
&lt;p&gt;让我们面对现实吧：大多数Web应用无法解决棘手的技术挑战。他们需要做到的是正确的对产品进行建模，并且比竞争对手更快的改进产品。&lt;/p&gt;
&lt;p&gt;这起初看起来似乎是那么的无聊，但是你很快会意识到实现这个目标比听起来要难。这是一项完全不同的挑战。即使它们技术上实现并没有那么复杂，但时解决它们会对产品产生巨大影响并且让人获得满足。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web应用面临的最大挑战不是变成了一个无法维护的屎山，而是会减慢你的速度，让你的业务最终失败。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是他们如何在Go中发生和我是如何避免他们的。&lt;/p&gt;
&lt;h2 id="松耦合是关键"&gt;松耦合是关键
&lt;/h2&gt;&lt;p&gt;应用难以维护的一个重要原因是强耦合。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在强耦合应用中，任何你尝试触动的东西都有可能产生意想不到的副作用&lt;/strong&gt;。每次重构的尝试都会发现新的问题。最终，你决定字号从头重写整个项目。在一个快速增长的产品中，你是不可能冻结所有的开发任务去完成重写已经构建的应用的。而且你不能保证这次你把所有事都完成好。&lt;/p&gt;
&lt;p&gt;相比之下，&lt;strong&gt;松耦合应用保持了清晰的边界&lt;/strong&gt;。他们允许更换一些损坏的部分不影响项目的其他部分。它们更容易构建和维护。但是，为什么他们如此罕见呢？&lt;/p&gt;
&lt;p&gt;微服务许诺了松耦合时实践，但是我们现在已经过了他们的炒作年代，而难以维护的应用仍旧存在。有些时候这反而变得更糟糕了：我们落入了&lt;a class="link" href="https://threedots.tech/post/microservices-or-monolith-its-detail/" rel="noopener" target="_blank"&gt;分布式单体&lt;/a&gt;的陷阱，处理和之前相同的问题，而且还增加了网络开销。&lt;/p&gt;
&lt;p&gt;&lt;img alt="从强耦合单体应用到分布式单体" src="https://threedots.tech/post/common-anti-patterns-in-go-web-applications/microservices.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;❌ 反模式：分布式单体 &lt;br /&gt;
在你了解边界之前，不要将你的应用切分成为微服务。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;微服务并不会降低耦合，&lt;strong&gt;因为拆分服务的次数并不重要。重要的是如何连接各个服务&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="从模块化单体应用到松耦合微服务" src="https://threedots.tech/post/common-anti-patterns-in-go-web-applications/monolith.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✅ 策略：松耦合 &lt;br /&gt;
以实现松耦合的模块为目标。如何部署它们（作为模块化单体应用或微服务）是一个实现细节。&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id="dry引入了耦合"&gt;DRY引入了耦合
&lt;/h2&gt;&lt;p&gt;强耦合十分常见，因为我们很早就学到了不要重复自己(Don&amp;rsquo;t Repeat Yourself, DRY)原则。&lt;/p&gt;
&lt;p&gt;简短的规则很容易被大家记住，但是简短的三个单词很难概括所有的细节。《程序员修炼之道: 从小工到专家》这本书提供了一个更长的版本：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每条知识在系统中都必须有一个单一的、明确的、权威的表述。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;ldquo;每一条知识&amp;quot;这个说法相当极端。大多数编程困境的答案是看情况而定，DRY也不例外。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;当你让两个事物使用相同抽象的时候，你就引入了耦合。如果你严格遵循DRY原则，你就需要在这个抽象之前增加抽象。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://threedots.tech/post/common-anti-patterns-in-go-web-applications/coupling.png" /&gt;&lt;/p&gt;
&lt;h2 id="在go中保持dry"&gt;在Go中保持DRY
&lt;/h2&gt;&lt;p&gt;相比于其他现代语言，Go是清晰的，缺少很多特性，没有太多的语法糖来隐藏复杂性。&lt;/p&gt;
&lt;p&gt;我们习惯了捷径，所以一开始很难接受Go的冗长。就像我们已经开发出一种去寻找一种更加聪明的编写代码的方式的本能。&lt;/p&gt;
&lt;p&gt;最典型的例子就是错误处理。如果你有编写Go的经验，你会觉得下面的代码片段很自然&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-go"&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #66d9ef;"&gt;if&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;err&lt;/span&gt; &lt;span style="color: #f92672;"&gt;!=&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #66d9ef;"&gt;return&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是对新手而言，一遍又一遍的重复这三行就是似乎在破坏DRY原则。他们经常想办法来规避这种样板方法，但是却没有什么好的结果。&lt;/p&gt;
&lt;p&gt;最终，大家都接受了Go的工作方式。&lt;strong&gt;它让你重复你自己，不过这并不是DRY告诉你的你要避免重复。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="单一数据模型带来的应用耦合"&gt;单一数据模型带来的应用耦合
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Go中有一个特性引入了强耦合，但会让你认为你自己在遵循DRY原则。这就是在一个结构体中使用多个标签。&lt;/strong&gt; 这似乎是一个好主意，因为我们经常对不同的事物使用相似的模型。&lt;/p&gt;
&lt;p&gt;这里有一个流行的方式保存单个&lt;code&gt;User&lt;/code&gt;模型的方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-go"&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #66d9ef;"&gt;type&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;User&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;ID&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;int&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"id" gorm:"autoIncrement primaryKey"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;FirstName&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"first_name" validate:"required_without=LastName"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;LastName&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"last_name" validate:"required_without=FirstName"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;DisplayName&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"display_name"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;Email&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"email,omitempty" gorm:"-"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;Emails&lt;/span&gt; []&lt;span style="color: #a6e22e;"&gt;Email&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"emails" validate:"required,dive" gorm:"constraint:OnDelete:CASCADE"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;PasswordHash&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"-"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;LastIP&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"-"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;CreatedAt&lt;/span&gt; &lt;span style="color: #f92672;"&gt;*&lt;/span&gt;&lt;span style="color: #a6e22e;"&gt;time&lt;/span&gt;.&lt;span style="color: #a6e22e;"&gt;Time&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"-"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;UpdatedAt&lt;/span&gt; &lt;span style="color: #f92672;"&gt;*&lt;/span&gt;&lt;span style="color: #a6e22e;"&gt;time&lt;/span&gt;.&lt;span style="color: #a6e22e;"&gt;Time&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"-"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #66d9ef;"&gt;type&lt;/span&gt; &lt;span style="color: #a6e22e;"&gt;Email&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;ID&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;int&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"-" gorm:"primaryKey"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;Address&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;string&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"address" validate:"required,email" gorm:"size:256;uniqueIndex"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;Primary&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;bool&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"primary"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt; &lt;span style="color: #a6e22e;"&gt;UserID&lt;/span&gt; &lt;span style="color: #66d9ef;"&gt;int&lt;/span&gt; &lt;span style="color: #e6db74;"&gt;`json:"-"`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;完整代码：&lt;a class="link" href="https://github.com/ThreeDotsLabs/go-web-app-antipatterns/blob/ab94d47350756716a7fed42b7ae546c51c11406a/01-coupling/01-tightly-coupled/internal/user.go#L9" rel="noopener" target="_blank"&gt;github.com/ThreeDotsLabs/go-web-app-antipatterns/01-coupling/01-tightly-coupled/internal/user.go&lt;/a&gt;&lt;/p&gt;</description><author>ipfans's Blog</author><pubDate>Fri, 13 Aug 2021 17:09:00 GMT</pubDate><guid isPermaLink="true">https://www.4async.com/2021/08/common-anti-patterns-in-go-web-applications/</guid></item><item><title>Dell Latitudes are Great Laptops (and they run Ubuntu well)</title><link>https://www.mikekasberg.com/blog/2021/08/12/dell-latitudes-are-great-laptops.html</link><description>&lt;p&gt;I’ve written before about my own search for &lt;a href="https://www.mikekasberg.com/blog/2019/05/03/computer-shopping-the-ultimate-developer-laptop.html"&gt;The Ultimate Developer
Laptop&lt;/a&gt; back in 2019. I chose to buy a Dell Precision 5510, but I also mentioned a
few other laptop models that work really well with Ubuntu. Laptop series like Dell
XPS, Dell Latitude, and Lenovo ThinkPad. Well, I actually own a &lt;a href="https://www.dell.com/support/kbdoc/en-us/000125555/dell-latitude-e7450-visual-guide"&gt;Dell Latitude
E7450&lt;/a&gt;,
and I’ve recently started using it a lot more often. It’s a small, lightweight
laptop made with high-quality parts, and it runs Ubuntu flawlessly. Mine doesn’t
have a discrete graphics card either, so I don’t have to bother with graphics
drivers in Ubuntu. The overall experience is super-smooth, and it puts a smile
on my face to use this computer!&lt;/p&gt;

&lt;div class="warning"&gt;
&lt;p&gt;Related posts about finding the perfect computer:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;2019-05-03: &lt;a href="https://www.mikekasberg.com/blog/2019/05/03/computer-shopping-the-ultimate-developer-laptop.html"&gt;Computer Shopping: The Ultimate Developer Laptop&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2020-05-24: &lt;a href="https://www.mikekasberg.com/blog/2020/05/24/why-i-love-ubuntu-as-a-desktop-os.html"&gt;Why I Love Ubuntu As a Desktop OS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2022-05-07: &lt;a href="https://www.mikekasberg.com/blog/2022/05/07/the-best-computer-you-can-buy-for-100.html"&gt;The Best Computer You Can Buy For $100&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;2023-09-09: &lt;a href="https://www.mikekasberg.com/blog/2023/09/09/my-500-developer-laptop.html"&gt;My $500 Developer Laptop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;If you’ve ever asked yourself, “What laptop should I buy to run Ubuntu?”, any
Dell Latitude is probably a great choice. Whether you want to spend thousands of
dollars on the newest hardware or get a cheap laptop to experiment with Ubuntu,
there’s probably some variant of a Dell Latitude that’s exactly what you’re
looking for. I specifically recommend Dell Latitude 7xxx series computers (more
on that below), but any Latitude made in the last 8 years or so will probably
run Ubuntu very well.&lt;/p&gt;

&lt;h2 id="why-are-dell-latitude-7xxx-laptops-great-for-ubuntu"&gt;Why are Dell Latitude 7xxx laptops great for Ubuntu?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Official Support&lt;/strong&gt; – Dell supports using Linux on most Latitude laptops. New Latitude laptops are listed
on &lt;a href="https://www.dell.com/en-us/work/shop/overview/cp/linuxsystems"&gt;Dell’s Linux systems
page&lt;/a&gt;. Moreover,
many Dell Latitude laptops are listed as being &lt;a href="https://ubuntu.com/certified?q=Latitude&amp;amp;limit=20&amp;amp;category=Laptop&amp;amp;vendor=Dell&amp;amp;limit=20"&gt;certified with
Ubuntu&lt;/a&gt;.
And even if a model isn’t officially certified, most Latitude models work flawlessly
anyway because they share most hardware with other models that &lt;em&gt;are&lt;/em&gt; certified.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Intel Graphics&lt;/strong&gt; – You can get a Latitude with Intel graphics, and I’d
recommend doing so. Avoid Nvidia and AMD. Nvidia and AMD graphics are both a headache with Linux,
and Intel graphics (i.e. embedded graphics with no discrete graphics card) work
flawlessly, with no special configuration required. The usual Linux graphics
card headaches disappear, and it’s a much better experience as long as you
don’t really need the graphics card for gaming or something.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Everything &lt;em&gt;just works&lt;/em&gt;&lt;/strong&gt; – As long as you don’t have a graphics card, you don’t
need to install any special drivers. Wifi, Bluetooth, audio, the SD card
reader – everything should work on Ubuntu without any additional setup.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Battery Life&lt;/strong&gt; – Using Intel graphics makes the battery life significantly
better. (Are you noticing a trend about Intel graphics?) Most of my
experience with Ubuntu is on laptops with Nvidia graphics, and the battery is
usually dead in an hour or two because the laptop always runs the graphics
card. (Yes, I’m aware of Nvidia prime, but it’s also a PITA.) In contrast, I 
get 6+ hours of battery life on my E7450 (with a 6-year-old battery), which
is by far the longest I’ve ever experienced with Ubuntu.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Size&lt;/strong&gt; – The 7xxx series of Dell Latitude represents Dell’s
premium/ultrabook line. These laptops are only a little bigger than a MacBook
Air, so they’re light to carry and they fit into any bag.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Quality&lt;/strong&gt; – Latitude laptops are business-grade machines, made to last through years of
use. They feel well-built and they’re nice to work with. It’s also easy to
perform basic maintenance and replace certain parts like RAM.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Ubiquitous&lt;/strong&gt; – Dell Latitude laptops are common (widely used in business), so it’s easy to
find support. When you Google a problem (even on Ubuntu), it’s likely someone
else has experienced the same problem and found a solution. Also, it’s easy to
find replacement parts if you ever need them.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Value&lt;/strong&gt; – Latitude laptops can be relatively cheap (especially if you buy used or
refurbished), and you get great value for your money. I’m writing this
on an E7450 from 6 years ago. And even though the computer’s 6 years old, it
feels snappy and runs Ubuntu well.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="about-dell-latitudes"&gt;About Dell Latitudes&lt;/h2&gt;

&lt;p&gt;“Latitude” is a line of business-grade laptops from Dell. They’ve been around
forever – Dell was making Latitudes in the ’90s. But as I said, I’d recommend
specifically looking at the 7xxx series. Dell started making this line of
laptops in 2013 with the E7440. The number scheme of Dell’s Latitudes tells you
about the computer. The first number (7xxx) tells you that this is a
premium/ultrabook laptop. The second number (x4xx) tells you that this is a 14”
laptop. You’ll also see 12” (x2xx) and 13” (x3xx) laptops, and they’re all great
(and otherwise mostly identical) – pick the size you like best. The last two
digits (xx40) tell you the model year (though it only roughly corresponds with
the year). Dell went all the way up to the E7490 in 2018, then went back to 00
with the 7400 in 2019 (though they dropped the “E”). There’s a really great
&lt;a href="https://en.wikipedia.org/wiki/Dell_Latitude"&gt;Dell Latitude Wikipedia page&lt;/a&gt;
where you can find more information about the specific laptop you might be
interested in, and what changed from year to year. One of the most important
changes to note is the addition of a USB-C port on the E7x80 models.&lt;/p&gt;

&lt;h2 id="where-to-buy"&gt;Where to Buy&lt;/h2&gt;

&lt;p&gt;If you want a brand-new Dell Latitude, you can order a new one directly from
Dell or from your favorite computer store. However, there’s also a great used
market for these laptops. Because they’re business-grade laptops, many
businesses upgrade their whole fleet at once (or on a rolling rotation) so
there’s a lot of 2-4 year old laptops on the used/refurbished market at great
prices!&lt;/p&gt;

&lt;p&gt;You can find used Latitudes at most of the places you’d guess, including
&lt;a href="https://www.ebay.com/"&gt;eBay&lt;/a&gt;. However, I particularly like
&lt;a href="https://www.newegg.com/"&gt;Newegg&lt;/a&gt; for used/refurbished laptops because of their
great search tools. For example, &lt;a href="https://www.newegg.com/p/pl?N=100006740%20601283908%20601286756%20601283909%20601283905%20601303704%20601333386%20601343209%20601283904%20601283903%20601283906%20601283907%20601303722%20601323887%20601343206%20600004343%2050010772%20601296065%20601296066%20601296059%20601183897&amp;amp;SrchInDesc=latitude&amp;amp;Order=1"&gt;this
search&lt;/a&gt;
for a Dell Latitude with a 4th-gen or newer i5 or i7 processor returns tons of
good results. (I’m focused on the 7xxx series here, but the 5xxx series in those
results are also good laptops.) For under $300, you can buy a laptop with 8GB
RAM and an SSD. And while you probably won’t be playing modern video games on
it, that laptop should run Ubuntu very well and would be fine for web browsing,
email, programming, and office or school work. And in the $550 price range, you
can get a more modern laptop with a USB-C port and a faster i7 processor. Of
course, I can’t personally vouch for all these Newegg vendors, but I’ve bought
refurbished machines off Newegg a couple times in the past and I’ve always had a
good experience.&lt;/p&gt;

&lt;h2 id="how-to-install-ubuntu"&gt;How to Install Ubuntu&lt;/h2&gt;

&lt;p&gt;Installing Ubuntu on a Latitude laptop is easy. You’ll want to use a USB stick
since your laptop probably doesn’t have a DVD drive. First, create a USB stick
from &lt;a href="https://ubuntu.com/tutorials/create-a-usb-stick-on-windows"&gt;Windows&lt;/a&gt;,
&lt;a href="https://ubuntu.com/tutorials/create-a-usb-stick-on-macos"&gt;macOS&lt;/a&gt;, or
&lt;a href="https://ubuntu.com/tutorials/create-a-usb-stick-on-ubuntu"&gt;Linux&lt;/a&gt;. Then, boot
from the USB stick and &lt;a href="https://ubuntu.com/tutorials/install-ubuntu-desktop"&gt;install
Ubuntu&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;I think I’ve become a little bit of a Dell fanboy in the past several years…
But if I have, it’s only because I always seem to have consistently great
experiences with Dell laptops and Ubuntu. Using Ubuntu on these machines makes
me happy. 😊  This blog post wasn’t sponsored by anyone, and I didn’t receive
any form of compensation for writing it – I just like writing about Ubuntu and
technology.&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Fri, 13 Aug 2021 05:30:00 GMT</pubDate><guid isPermaLink="true">https://www.mikekasberg.com/blog/2021/08/12/dell-latitudes-are-great-laptops.html</guid></item><item><title>Rendered static HTML</title><link>https://www.marginalia.nu/log/13-static-html/</link><description>&lt;p&gt;The technological choices we make determine the rules we have to abide by.&lt;/p&gt;
&lt;p&gt;If every page load incurs hundreds of database calls on the server, and 30 seconds of javascripting on the front-end, then obviously you need to reduce the number of page loads to a minimum. They are frustrating for the user and expensive for the server. This makes the front-end even more slow and stateful, and so the urgency for reducing page loads increases even further.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 13 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/13-static-html/</guid></item><item><title>2 Second Lean</title><link>https://june.kim/two-second-lean/</link><author>june.kim</author><pubDate>Fri, 13 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/two-second-lean/</guid></item><item><title>Who owns what chips know about THEMSELVES?</title><link>https://stop.zona-m.net/2021/08/who-owns-what-chips-know-about-themselves/</link><description>&lt;p&gt;Our chips have complex lives, but nobody knows who could know them.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 12 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/who-owns-what-chips-know-about-themselves/</guid></item><item><title>Conditionally setting your gitconfig</title><link>https://utf9k.net/blog/conditional-gitconfig/</link><description>Did you know you can change Git attributes based on what folder you're in?</description><author>utf9k</author><pubDate>Thu, 12 Aug 2021 10:17:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/conditional-gitconfig/</guid></item><item><title>Deployment Hook Error Handling in Envoyer</title><link>https://james.brooks.page/blog/deployment-hook-error-handling-in-envoyer</link><description>Laravel’s Envoyer service allows you to break up your deployment process into multiple steps, which makes it really easy to manage deployments. Envoyer runs each step individually, checking the exit code of the last command within the step. Because each deployment step is Bash, if it’s a non-zero exit code then it gets reported back as a failure.</description><author>James Brooks</author><pubDate>Thu, 12 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://james.brooks.page/blog/deployment-hook-error-handling-in-envoyer</guid></item><item><title>Culture - Measure what you Treasure</title><link>https://www.danstroot.com/posts/2021-08-12-culture-measure-what-you-treasure</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/culture.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;When measuring culture note that culture is an outcome. If you could measure culture directly, you would be measuring a result - e.g. the current state (kind of like reading the speedometer in your car). Does simply knowing the current state help you improve it, or do you have to go deeper?&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-08-12-culture-measure-what-you-treasure"&gt;Culture - Measure what you Treasure&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Thu, 12 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-08-12-culture-measure-what-you-treasure</guid></item><item><title>We are dooming our own files, part two</title><link>https://stop.zona-m.net/2021/08/we-are-dooming-our-own-files-part-two/</link><description>&lt;p&gt;Nothing new in this new story. Sadly.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 12 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/we-are-dooming-our-own-files-part-two/</guid></item><item><title>Dark Emu</title><link>https://apurva-shukla.me/bookshelf/dark-emu/</link><description>Bruce Pascoe’s Dark Emu is a fantastic achievement, not entirely for it’s content but for it’s power in reviving the national attention on…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Wed, 11 Aug 2021 15:45:59 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/bookshelf/dark-emu/</guid></item><item><title>Continuous Delivery to ECS with Terraform</title><link>/terraform-ecs-cicd/</link><description>&lt;p&gt;Continuous delivery is something that we're all striving for. I was doing the same, but there was a snag:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My terraform code and API code were in separate projects&lt;/li&gt;
&lt;li&gt;I wanted to make updates to the API code and have it build and update the ECS service&lt;/li&gt;
&lt;li&gt;I didn't want to manage the container definition separately as it had too many dependant resources (Datadog sidecar etc.)&lt;/li&gt;
&lt;li&gt;You have multiple environments and don't want to have to use a separate git branch for the API code.&lt;/li&gt;
&lt;li&gt;You have a branch for each environment for your infrastructure that is independently deployed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Maybe you have this problem too. Because Terraform uses a specific ECR image path to build the container definition, how do we ever update it automatically?&lt;/p&gt;
&lt;p&gt;There are several ways to solve this problem, many of which are discussed in &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/632"&gt;this thread&lt;/a&gt;. But, today, I'm going to show you how I resolved this issue.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup your Docker build/deploy pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First things first, we need the API Docker image inside ECR. This will vary according to what CI system you use - in our case we use Azure DevOps.&lt;/p&gt;
&lt;p&gt;When the image is being built, we want to tag it uniquely. Ideally, you want something numerable. In our case, we chose to use Azure's built in &amp;quot;BuildId&amp;quot; parameter to tag the images.&lt;/p&gt;
&lt;p&gt;Below you can see the build steps we take in the CI pipeline. After the image is built, it creates a text file with the BuildId in it and ships that as an &amp;quot;Artifact&amp;quot;. This will become important later. But the main thing is you need to trigger a further pipeline for your environments based on that parameter changing.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-yml"&gt;- task: Docker@2
  inputs:
    command: build
    DockerFile: &amp;quot;$(Build.SourcesDirectory)/Dockerfile&amp;quot;
    repository: $
    tags: |
      $(Build.BuildId)

- task: ECRPushImage@1
  inputs:
    imageSource: &amp;quot;imagename&amp;quot;
    sourceImageName: $
    sourceImageTag: &amp;quot;$(Build.BuildId)&amp;quot;
    repositoryName: $
    pushTag: &amp;quot;$(Build.BuildId)&amp;quot;

- task: Bash@3
  displayName: &amp;quot;Upload Build Artifact of the Docker image Id&amp;quot;
  inputs:
    targetType: &amp;quot;inline&amp;quot;
    script: |
      # Add the build Id to a new file that will then be published as an artifact
      echo $(Build.BuildId) &amp;gt; .buildId
      cat .buildId

- task: CopyFiles@2
  displayName: &amp;quot;Copy BuildId file&amp;quot;
  inputs:
    Contents: &amp;quot;.buildId&amp;quot;
    TargetFolder: &amp;quot;$(Build.ArtifactStagingDirectory)&amp;quot;

- task: PublishBuildArtifacts@1
  displayName: &amp;quot;Publish Artifact&amp;quot;
  inputs:
    pathToPublish: $(Build.ArtifactStagingDirectory)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to run this pipeline now you've created it.&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Setup an SSM (Systems Manager) parameter&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;SSM is an AWS service I had previously never really used. Its parameter store feature will allow us to store a variable that we can update later - in this case, the docker image tag.&lt;/p&gt;
&lt;p&gt;Create a new parameter by going to AWS Systems Manager &amp;gt; Application Management &amp;gt; Parameter Store. Name the parameter something like &lt;code&gt;/my-api/${env}/docker-image-tag&lt;/code&gt; (where &lt;code&gt;env&lt;/code&gt; is the environment, you'll need to duplicate this parameter for all the environments you have). It should be a &amp;quot;String&amp;quot; variable and be the unique tag that is generated by your CI build pipeline - in my case, the BuildId.&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Create your deployment pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, we need to define a way to update the image just in a certain environment (e.g., just &lt;code&gt;development&lt;/code&gt;). How can we do that?
Because of our setup, the duplication effort is fairly minimal. We already have our image build (which is consistent across all environments). We just need to update that SSM parameter to use the unique tag (BuildId) that the build pipeline generated.&lt;/p&gt;
&lt;p&gt;In Azure, you can trigger a pipeline based on an artifact as we generated in step 1. I then configured 3 tasks based on this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get the BuildId from the file and add it to the runners environment&lt;/li&gt;
&lt;li&gt;Update the SSM parameter for that environment to the new BuildID&lt;/li&gt;
&lt;li&gt;Trigger the Infrastructure/Terraform pipeline for that environment - this is where the new SSM parameter value will get picked up and used in a container definition.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="image"&gt;
	&lt;img alt="Sample of the update SSM job" src="../../assets/images/azure-update-ssm-pipeline.png" /&gt;
&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Update Terraform to use the SSM parameter&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you've got the SSM parameter being updated each time there is a new build, we need to set up Terraform to use the SSM parameter as part of the image name.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-hcl"&gt;
// Import the SSM parameter
// This can be done on a module level because it depends on the environment
data &amp;quot;aws_ssm_parameter&amp;quot; &amp;quot;docker_image_id&amp;quot; {
  name = &amp;quot;/my-api/${var.environment}/docker-image-tag&amp;quot;
}

// Use it later on...
container_image = &amp;quot;&amp;lt;AWS_ACCOUNT_ID&amp;gt;.dkr.ecr.&amp;lt;REGION&amp;gt;.amazonaws.com/my-api:${data.aws_ssm_parameter.docker_image_id.value}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="5"&gt;
&lt;li&gt;All done!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you've set up a complete continuous deployment pipeline with Terraform and ECS. To review, here's how the system works&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creates and builds a new ECR image tagged in a unique way&lt;/li&gt;
&lt;li&gt;The build pipeline notifies the release pipeline of this new image tag in some way (in Azure's case, a build artifact)&lt;/li&gt;
&lt;li&gt;The release pipeline updates the SSM parameter based on the image tag and triggers the Terraform deployment pipeline for that environment.&lt;/li&gt;
&lt;li&gt;Terraform picks up the new SSM value and implements it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are two obvious downsides to this approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Multiple updates to multiple API's could cause locking issues where you have to manually run the Terraform pipeline once.&lt;/li&gt;
&lt;li&gt;It's a bit slower than other approaches, but the best one I could find. Plus if your Terraform repo deploys in less than 2 minutes like ours, then it's not a big problem.&lt;/li&gt;
&lt;/ol&gt;</description><author/><pubDate>Wed, 11 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/terraform-ecs-cicd/</guid></item><item><title>The Future is Inspiring</title><link>https://macleodsawyer.com/blog/the-future-is-inspiring/</link><description>&lt;p&gt;If you don’t find the future inspiring, then you’re thinking about it wrong.&lt;/p&gt;
&lt;p&gt;If you don’t have hope for the future, then you’re thinking about it wrong.&lt;/p&gt;
&lt;p&gt;Someone reblogged this with a caption (summarized as “if all you’ve had to do in life is to survive, then you don’t know what you want”) , and promptly deleted it. I will write what I was writing here:&lt;/p&gt;
&lt;p&gt;That’s exactly what I am talking about! That’s what makes the future so exciting and hopeful, against all odds that is the only time you have to find out what you want, whether that be to change, to discover, or to experience. The future is a world of endless possibilities that isn’t beholden to the present, and less so than the past.&lt;/p&gt;
&lt;p&gt;There are mindsets that feast on disappointment and sticking to the status quo, they fear the future not because of the current world, but because their current world could change to something that they never imagined.&lt;/p&gt;
&lt;p&gt;There is a strict dichotomy between two different mindsets: The idea that change is good, and that change is bad. It’s up to each person to determine if they want a change or if they want to be stuck to the same rhythm humming the same chorus all their life.&lt;/p&gt;
&lt;p&gt;Being optimistic about the future, no matter the terms, is always a positive. The future is change, the past isn’t, and the present is bound to the past. The future is the only spot you can place happiness because it is the only thing that hasn’t happened and that you can do something about it, even if incredibly minor.&lt;/p&gt;
&lt;p&gt;I have depression and extreme anxiety, came from less than nothing and have had to fight tooth and nail for everything I have and then some (mostly against myself), I know how easy the programming in my brain is to prefer being nihilistic and fatalistic, but there are better ways to be. I know this, we all know this, being negative doesn’t help make the world better it just makes the world a more tortured place to exist in.&lt;/p&gt;
&lt;p&gt;A child doesn’t think about yesterday as much as they think about tomorrow. The possibilities of growing up in a castle, or parties, seeing their friends, having a car, or having cake every single day. They think about what could be in a positive light, not in a negative way because that’s more fun, that brings joy and happiness, to at the very least imagine it. Doom and gloom have their place, but to be the ultimate force in your mind to dictate every action taints every action you can possibly take.&lt;/p&gt;
&lt;p&gt;If you don’t find the future inspiring, then you’re thinking about it wrong.&lt;/p&gt;</description><author>Macleod Sawyer | Web Portal Feed</author><pubDate>Wed, 11 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://macleodsawyer.com/blog/the-future-is-inspiring/</guid></item><item><title>Targeted ads ALWAYS divide</title><link>https://stop.zona-m.net/2021/08/targeted-ads-always-divide/</link><description>&lt;p&gt;Why wasn&amp;rsquo;t this more obvious..&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 11 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/targeted-ads-always-divide/</guid></item><item><title>First steps into Collapse OS</title><link>http://localhost:4000/2021/08/10/collapse-os.html</link><description>So I guess people actually read my zany hot takes on Hacker News, because on a whim I visited the Collapse OS website and noticed right there on the homepage that apparently I’m so good at answering questions about things I learned about 5 minutes prior that Collapse OS’ creator namedropped me for, well, doing a “good job” answering questions about a thing I learned about 5 minutes prior.</description><author>RyNo’s Site</author><pubDate>Tue, 10 Aug 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">http://localhost:4000/2021/08/10/collapse-os.html</guid></item><item><title>Docks and Ubuntu</title><link>https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html</link><description>&lt;p&gt;Laptop docks are great! As I wrote about in &lt;a href="https://www.mikekasberg.com/blog/2019/05/03/computer-shopping-the-ultimate-developer-laptop.html"&gt;The Ultimate Developer Laptop&lt;/a&gt; and &lt;a href="https://www.mikekasberg.com/blog/2020/04/10/my-home-office-setup.html"&gt;My
Home Office Setup&lt;/a&gt;, I’ve been
using a &lt;a href="https://www.dell.com/support/home/en-us/product-support/product/dell-thunderbolt-dock-tb16/overview"&gt;Dell
TB16&lt;/a&gt;
thunderbolt dock for about two years now, and I really love it. The dock makes
it so much quicker and easier to connect my laptop to all the peripherals on my
desk. And that’s important to me because it makes it feel easy to take my work to
another part of the house or go work on the deck – I don’t feel tied down to my
desk.&lt;/p&gt;

&lt;p&gt;But recently, I’ve found myself wishing my dock could do more. In particular,
I’ve found myself wanting to be able to connect my dock to a wider variety of
computers. For example, I have a Dell Latitude E7450 that doesn’t have a
thunderbolt port, and sometimes I want to use that computer at my desk. Doing so
requires moving the cables for my keyboard, mouse, monitor, speakers, etc. from
my dock to the laptop, completely negating the purpose of the dock! My TB16 dock
works great with the laptop I bought it for (my &lt;a href="https://www.mikekasberg.com/blog/2019/05/03/computer-shopping-the-ultimate-developer-laptop.html"&gt;Dell Precision
5510&lt;/a&gt;),
but leaves something to be desired with other laptops where it’s less compatible
(or not compatible at all). I was curious to find out if there were any docks
compatible with Ubuntu that would allow me to connect a wider variety of
computers. And while researching different docks and testing some out, I think I
developed a better understanding of which kinds of docks work best with Ubuntu
in different circumstances.&lt;/p&gt;

&lt;h2 id="types-of-docks"&gt;Types of Docks&lt;/h2&gt;

&lt;p&gt;Fundamentally, there are at least four major categories of docks available today.&lt;/p&gt;

&lt;div class="message"&gt;
&lt;p&gt;As of May 2024, I've updated this list to include Thunderbolt Monitors as a
subsection of Thunderbolt Docks!&lt;/p&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html#oem-docks"&gt;OEM Docks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html#displaylink-docks"&gt;DisplayLink Docks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html#thunderbolt-docks"&gt;Thunderbolt Docks&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href="https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html#thunderbolt-monitors"&gt;Thunderbolt Monitors&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html#usb-hubs"&gt;USB Hubs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="oem-docks"&gt;OEM Docks&lt;/h3&gt;

&lt;p&gt;&lt;img alt="Dell OEM Dock" src="https://www.mikekasberg.com/images/docks-and-ubuntu/dell-dock.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;OEM docks are the old-fashioned dock you probably remember from the early 2010s.
They are usually specific to a single laptop model or series, and they usually
connect to a special port on the bottom of the laptop.  These were common on
older Dell Latitudes or IBM/Lenovo ThinkPads. You’d set your laptop down on top
of the dock and by doing so connect it to the mouse, keyboard, monitor, and
other peripherals plugged into the back.  The primary problem with these docks,
of course, is that they use a custom port that’s only compatible with one model
or series of laptop, and they aren’t even really used anymore on modern
computers (they’ve basically been superseded by Thunderbolt docks). If you’re
curious about an OEM dock’s compatibility with Ubuntu, I think they’re mostly
compatible but you’ll need to look up the specific information for the laptop
and dock you care about.&lt;/p&gt;

&lt;h3 id="displaylink-docks"&gt;DisplayLink Docks&lt;/h3&gt;

&lt;p&gt;&lt;img alt="A DisplayLink dock" src="https://www.mikekasberg.com/images/docks-and-ubuntu/displaylink.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;DisplayLink Docks Are USB-2.0 or USB-3.0 docks that use the
&lt;a href="https://www.displaylink.com/"&gt;DisplayLink&lt;/a&gt; protocol to provide video over USB.
In addition to providing video over USB, they also provide additional USB ports
and sometimes other ports like ethernet. Thus, you can connect all your monitors
and peripherals at once over a single USB cable! Living the dream! Of course,
there are always downsides, and for DisplayLink docks there are two major
compromises. First, the dock doesn’t typically provide power, so you’ll still
have to connect your laptop’s charger. Second, DisplayLink compresses the video
to send it over USB, so it doesn’t do well with high framerates like videos or
games. The compression algorithm optimizes for changes, so you don’t actually
notice any stutter with mouse movements, typing, or other changes where a small
screen area is updating, but you do start to notice some visual artifacts when
you try to play a fullscreen video at 1080p, for example. Overall, these are
minimal compromises in many use cases – the dock is particularly well suited
for an office or developer setting.&lt;/p&gt;

&lt;p&gt;If you’re interested in learning more about how DisplayLink docks work with
Ubuntu, I wrote a separate blog post about &lt;a href="https://www.mikekasberg.com/blog/2021/08/07/displaylink-docks-and-ubuntu.html"&gt;DisplayLink Docks and Ubuntu&lt;/a&gt; with more details.&lt;/p&gt;

&lt;h3 id="thunderbolt-docks"&gt;Thunderbolt Docks&lt;/h3&gt;

&lt;p&gt;&lt;img alt="A Thunderbolt dock" src="https://www.mikekasberg.com/images/docks-and-ubuntu/thunderbolt-dock.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Thunderbolt Docks use a Thunderbolt port to send video to the dock, and can
also connect other peripherals over the same connection – much like a USB-3.0
DisplayLink dock. The difference is that Thunderbolt can support native video,
so there’s no need for a DisplayLink driver (nor its compression algorithm). In
some cases (i.e. if your dock and computer both support it), you can also charge
the computer over the same Thunderbolt cable, so there’s truly only a single
cable to plug in! In many cases, this is probably the best kind of dock, but it’s
also the most expensive and requires a modern computer and dock. In my
experience, they are a little finnicky – my Dell dock won’t charge my Lenovo
laptop, though USB and video still work fine.&lt;/p&gt;

&lt;p&gt;It’s worth noting that there are some weird sub-categories of USB-C docks that
might be confused with Thunderbolt docks. (Although Thunderbolt 3 and
USB-C use the same connector, they support different things. Technically,
Thunderbolt 3 is a superset of USB-C, a faster connection over the same kind of
port.) There are some docks that support DisplayPort over USB-C but aren’t
Thunderbolt docks.  I’m sure you can also find a USB-C dock that’s neither
DisplayPort nor Thunderbolt. For example, there are some docks that use the
DisplayLink protocol over a USB-C connection. A post on the Dell community
forums about &lt;a href="https://www.dell.com/community/Laptops-General-Read-Only/Demystifying-USB-C-and-Thunderbolt-i-e-adapter-and-dock/td-p/5153006"&gt;Demystifying USB-C and
Thunderbolt&lt;/a&gt;
recently made it to the front page of &lt;a href="https://news.ycombinator.com/item?id=29447216"&gt;Hacker
News&lt;/a&gt; and is worth the read if
you want more detailed technical information. The post goes into detail about
how USB-C, DisplayPort, and Thunderbolt allocate lanes of data over a USB-C
connector and how docks can use those lanes differently, resulting in better or
worse video quality. It also looks at how video quality over a USB-C connection
might vary across different hardware due to other factors, like whether you
laptop has a GPU wired to the USB-C port. In general, your display quality will
downgrade to the &lt;em&gt;worst&lt;/em&gt; component in the system between your computer, monitor,
dock, and cables. Suffice it to say, looking at the connector alone won’t
confirm what kind of connection you actually have with the dock and monitor,
which can make the Thunderbolt docking experience a little confusing.&lt;/p&gt;

&lt;p&gt;But the good news is that in general, I think Thunderbolt docks tend to work
well with Ubuntu. In my personal experience, the Dell TB16 Thunderbolt dock
works flawlessly with Ubuntu on my Dell Precision 5510 laptop. Of course, since
these are both Dell products, they were designed to work well together, so your
experience might vary and you should research the compatibility of the dock and
the laptop with Ubuntu. With that being said, I think most Thunderbolt docks use
the same underlying technology and don’t typically require any additional
drivers (at least on the latest version of Ubuntu), so it would be reasonable to
expect that they work well with Linux. My general recommendation would be to get
a laptop with documented Linux support and then look for docks that are
specifically compatible with that Laptop. Even better if the dock has documented
support for Linux.&lt;/p&gt;

&lt;p&gt;I do think a Thunderbolt dock provides the smoothest experience as long as you
plan to only use one laptop with the dock (or if all the laptops you’ll use
support that Thunderbolt dock). Thunderbolt docks will probably continue to grow
in popularity well into the future.&lt;/p&gt;

&lt;h4 id="thunderbolt-monitors"&gt;Thunderbolt Monitors&lt;/h4&gt;

&lt;p&gt;I originally omitted Thunderbolt monitors from this blog post, but updated it in
May, 2024 to include them. I think they’re popular enough to warrant a mention
here, and are worth considering as an option if you’re searching for a
single-cable solution that’s compatible with Linux.&lt;/p&gt;

&lt;p&gt;A Thunderbolt monitor connects directly to your computer using a DisplayPort
signal over Thunderbolt. A Thunderbolt monitor is functionally similar to a
Thunderbolt dock in that both transmit a DisplayPort signal over Thunderbolt,
and allow you to connect a monitor in addition to other (USB) accessories. The
biggest difference, of course, is that a Thunderbolt monitor eliminates the dock
entirely (and creates more free space on your desk). Some monitors can even
supply 65W power over the Thunderbolt connection to the computer, allowing you
to achieve the same single-cable experience as a Thunderbolt dock without the
dock itself! In my experience, Thunderbolt monitors are broadly compatible with
Linux (since Linux is compatible with the underlying DisplayPort, USB, and
Thunderbolt technologies), but the caveats of Thunderbolt docks I discussed
above still apply. In particular, you might run into problems when pushing
devices to their limits (i.e. max supported resolution or number of monitors),
like &lt;a href="https://x.com/dhh/status/1788220471810662615"&gt;DHH did in this thread on
X&lt;/a&gt;. But as these monitors become
more popular and the hardware continues to improve, I’d expect the linux
experience to get better soon.&lt;/p&gt;

&lt;p&gt;Overall, I think a Thunderbolt monitor may be &lt;strong&gt;the best choice&lt;/strong&gt; for a
single-cable solution that connects your monitor, keyboard, mouse, speakers,
power, and other accessories to your computer if you have a modern computer and
are willing to pay for an expensive monitor. &lt;strong&gt;But&lt;/strong&gt; you need to do research to
make sure the products you use (laptop graphics card, display, and cables) are
compatible and support the required technologies (like the DisplayPort version).
(Or buy a monitor with a great return policy in case it doesn’t work.) As with
Thunderbolt docks, reading &lt;a href="https://www.dell.com/community/Laptops-General-Read-Only/Demystifying-USB-C-and-Thunderbolt-i-e-adapter-and-dock/td-p/5153006"&gt;Demystifying USB-C and
Thunderbolt&lt;/a&gt;,
and then understanding the data lanes your setup will use and what resolutions
and speed are supported by that setup will help you ensure everything is
compatible.&lt;/p&gt;

&lt;h3 id="usb-hubs"&gt;USB Hubs&lt;/h3&gt;

&lt;p&gt;&lt;img alt="A USB hub" src="https://www.mikekasberg.com/images/docks-and-ubuntu/usb-hub.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;USB Hubs hardly count as docks because they don’t typically handle display
output. (Though some can pass a DisplayPort connection through them.)
Still, they’re worth a brief mention because you can use a USB hub as a cheap and
effective dock. You can connect your keyboard, mouse, other peripherals, and
sometimes even ethernet to a USB hub. Then, you’d probably only have 3 cables to
plug in to your computer – the USB hub, power, and video. Another benefit of
USB hubs is they don’t typically require any drivers. They should &lt;em&gt;just work&lt;/em&gt;
with Ubuntu, or with anything else for that matter, because it’s really just a
standard USB connection.&lt;/p&gt;

&lt;h2 id="which-dock-should-you-use"&gt;Which dock should you use?&lt;/h2&gt;

&lt;p&gt;If your laptop has a Thunderbolt 3 port and you can afford to buy a thunderbolt
3 dock or monitor, that’s probably your best option. And if you’re shopping for
a new laptop and want a dock to go with it, Thunderbolt 3 is what you should
look for.  Thunderbolt docks are the most expensive docking option (and also the
newest), and they obviously won’t work with laptops that don’t have a
Thunderbolt 3 port, but they provide the best experience as long as everything’s
compatible.&lt;/p&gt;

&lt;p&gt;If a Thunderbolt dock doesn’t work for you, or if you want to connect a display
over a regular USB connection, a DisplayLink dock might be the next best option.
I’d go for a USB-A 3.0 dock since most computers that don’t have a Thunderbolt
port also don’t have a USB-C port. Games and fullscreen videos won’t work well
with DisplayLink, but you won’t notice any issues with most other types of
day-to-day computing.&lt;/p&gt;

&lt;p&gt;Finally, if you don’t want to use DisplayLink (perhaps because you need higher
framerates for videos or gaming), consider using a USB hub (for your keyboard,
mouse, ethernet, speakers, etc) and connecting your monitor directly to the
laptop. Although it’s one extra cable to plug into the laptop compared to a
DisplayLink dock, a USB hub can still provide a good experience and help with cable
organization. And a USB hub is great if you’re on a budget – you should be able
to find a cheap one for about $20.&lt;/p&gt;

&lt;h2 id="what-am-i-doing"&gt;What am I doing?&lt;/h2&gt;

&lt;p&gt;I’m currently using a DisplayLink dock as a USB hub. (The DisplayLink is
irrelevant in this setup – it might as well be a cheap hub.) I’m connecting an
HDMI cable from my monitor directly to my laptop to avoid the DisplayLink lag
with videos, but I’m connecting my keyboard, mouse, and audio through the USB
dock. I use 3 different laptops at my desk right now, and one of them doesn’t
have a Thunderbolt port, so this solution is a bit of a compromise but it’s
working very well for me. I did try using video through the DisplayLink dock for
several days but ultimately found that I &lt;em&gt;do&lt;/em&gt; want a fast display connection for
videos and games, and the HDMI port is the best way for me to get that across 3
different laptops with different ports. So I have 3 cables to plug in when I
change laptops. This isn’t completely ideal, but it’s still better than a
tangled mess of 6+ cables on my desk. Next time I upgrade the whole setup (and
stop using my oldest computers), I’ll probably opt for a Thunderbolt dock or
monitor.&lt;/p&gt;

&lt;p&gt;&lt;img alt="A desktop-mounted power outlet" src="https://www.mikekasberg.com/images/docks-and-ubuntu/outlet.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;I’ve also made one other big improvement for using multiple computers at my desk –
I added a &lt;strong&gt;desktop-mounted outlet&lt;/strong&gt;, and I’m finding it even more useful than I
thought I would. I can use this outlet on top of my desk to plug in my laptop
charger, which means it’s very easy to change the charger I’m using when I swap
the laptop at my desk. This is also great for my work laptop – I need to take
both the laptop and charger to work with me when I go to the office, and I don’t
want to climb behind my desk each time to unplug the charger. There isn’t
anything special about this particular outlet – you can find something similar
on Amazon for about $25.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I hope you learned something useful from reading this or that I at least gave
you some ideas or inspiration for making your own setup nicer! I’ve found that
using a dock makes my life smoother and more convenient, so it’s great that many
docking solutions work well with Ubuntu, and I hope we’ll see even better Linux
docking support in the future!&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Tue, 10 Aug 2021 03:30:00 GMT</pubDate><guid isPermaLink="true">https://www.mikekasberg.com/blog/2021/08/09/docks-and-ubuntu.html</guid></item><item><title>Gadget - Copy category members</title><link>https://river.me/blog/copy-category-members/</link><description>A gadget to copy all members of a category</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 10 Aug 2021 03:13:09 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/copy-category-members/</guid></item><item><title>Setup a command line app in python</title><link>https://yasha.solutions/posts/command-line-app-python/</link><description>Goal Setup a basic command line package in python.
Package  Git: https://github.com/pallets/click/ Doc: https://click.palletsprojects.com/  Let&amp;rsquo;s get to it Create a virtual environnement.
1 2  python -m venv my_env . my_env/bin/activate   Then the setup.py file: (require setuptools)
1 2 3 4 5 6 7 8 9 10 11 12 13 14  from setuptools import setup setup( name='myapp', version='1.0', py_modules=['myapp'], install_requires=[ 'Click', ], entry_points=""" [console_scripts] myapp=myapp:cli """, )   and the myapp.</description><author>Yasha Solutions</author><pubDate>Tue, 10 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/command-line-app-python/</guid></item><item><title>Using python to work with orgmode and gitlab - Part 1 - Onboarding Users</title><link>https://yasha.solutions/posts/using-python-with-orgmode-gitlab-part1/</link><description>Since I moved to orgmode I have been trying to make this work with most of my environnment. Since I work with gitlab on a daily basis I thought I would start there first.
Libraries Org Parse  Git Repo: https://github.com/karlicoss/orgparse Docs : https://orgparse.readthedocs.io/  Org Format  Git: https://github.com/novoid/orgformat  Convert stuff : pypandoc  Git : https://github.com/NicklasTegner/pypandoc  Python for Gitlab  Git Repo : https://github.com/python-gitlab/python-gitlab Doc : https://python-gitlab.</description><author>Yasha Solutions</author><pubDate>Tue, 10 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/using-python-with-orgmode-gitlab-part1/</guid></item><item><title>2021–08–10: Wrapping up Pinephone keyboard firmware development</title><link>https://xnux.eu/log/#044</link><author>megi's PinePhone Development Log</author><pubDate>Tue, 10 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#044</guid></item><item><title>Two records Italy cannot be proud of</title><link>https://stop.zona-m.net/2021/08/two-records-italy-cannot-be-proud-of/</link><description>&lt;p&gt;Or, how COVID made  digital ignorance visible AGAIN.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 09 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/two-records-italy-cannot-be-proud-of/</guid></item><item><title>My dotfiles</title><link>https://ivymike.dev/my-dotfiles.html</link><description>&lt;p&gt;In late 1991, I walked into the Computing Services Office at UIUC and timidly asked "Is there where we get an email account?" Not only did I get an email account, as mentioned in &lt;a href="https://ivymike.dev/my-first-hack-the-sparcserver-600mp.html"&gt;My First Hack&lt;/a&gt;, but they also gave me a sheet of paper with a bunch of …&lt;/p&gt;</description><author>IvyMike.dev</author><pubDate>Mon, 09 Aug 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ivymike.dev/my-dotfiles.html</guid></item><item><title>SIGGRAPH 2021 Talk- Unbiased Emission and Scattering Importance Sampling for Heterogeneous Volumes</title><link>https://blog.yiningkarlli.com/2021/08/unbiased-emission-and-scattering-volumes.html</link><description>&lt;p&gt;This year at SIGGRAPH 2021, Wei-Feng Wayne Huang, Peter Kutz, Matt Jen-Yuan Chiang, and I have a talk that presents a pair of new distance-sampling techniques for improving emission and scattering importance sampling for volume path tracing cases where low-order heterogeneous scattering dominates.
These techniques were developed as part of our ongoing development on &lt;a href="https://www.disneyanimation.com/technology/hyperion/"&gt;Disney’s Hyperion Renderer&lt;/a&gt; and first saw full-fledged production use on &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;, although limited testing of in-progress versions also happened on &lt;em&gt;Frozen 2&lt;/em&gt;.
This work was led by Wayne, building upon important groundwork that was put in place by Peter before Peter left Disney Animation.
Matt and I played more of an advisory or consulting role on this project, mostly helping with brainstorming, puzzling through ideas, and figuring out how to formally describe and present these new techniques.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Aug/unbiased-emission-and-scattering-volumes/teaser.png"&gt;&lt;img alt="A higher-res version of Figure 1 from the paper: a torch embedded in thin anisotropic heterogeneous mist. Equal-time comparison of a conventional null-collision approach (left), incorporating our emission sampling strategy (middle), and additionally combining with our scattering sampling strategy via MIS (right)." src="https://blog.yiningkarlli.com/content/images/2021/Aug/unbiased-emission-and-scattering-volumes/preview/teaser.webp" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the paper abstract:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We present two new distance-sampling methods for production volume path tracing. We extend the null-collision integral formulation to efficiently gather heterogeneous volumetric emission, achieving higher-quality results. Additionally, we propose a tabulation-based approach to importance sample volumetric in-scattering through a spatial guiding data structure. Our methods improve the sampling efficiency for scenarios where low-order heterogeneous scattering dominates, which tends to cause high variance renderings with existing null-collision methods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paper and related materials can be found at:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.yiningkarlli.com/projects/emissionscattervolumes.html"&gt;Project Page (Author’s Version and Supplementary Material)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3450623.3464644"&gt;Official Print Version (ACM Library)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As covered in several previous publications, several years ago we replaced Hyperion’s old residual ratio tracking &lt;a href="https://dl.acm.org/citation.cfm?id=2661292"&gt;[Novák et al. 2014&lt;/a&gt; , &lt;a href="http://graphics.pixar.com/library/ProductionVolumeRendering"&gt;Fong et al. 2017]&lt;/a&gt; based volume rendering system with a new, state of the art, null-collision (also called delta tracking or Woodcock tracking) tracking theory based volume rendering system.
Null-collision volume rendering systems are extremely good at dense volumes where light transport is dominated by high-order scattering, such as clouds and snow and sea foam.
However, null-collision volume rendering systems historically have struggled with efficiently rendering optically thin volumes dominated by low-order scattering, such as mist and fog.
The reason null-collision systems struggle with optically thin volumes is because in a thin volume, the average sampled distance is usually very large, meaning that ray often goes right through the volume with very few scattering events &lt;a href="http://jcgt.org/published/0007/03/03/"&gt;[Villemin et al. 2018]&lt;/a&gt;.
Since we can only evaluate illumination at each scattering event, not having a lot of scattering events means that the illumination estimate is necessarily often very low-quality, leading to tons of noise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Frozen 2&lt;/em&gt;’s forest scenes tended to include large amounts of atmospheric fog to lend the movie a moody look; these atmospherics proved to be a major challenge for Hyperion’s modern volume rendering system.
Going in to &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;, we knew that the challenge was only going to get harder: from fairly early on in &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;’s production, we already knew that the cinematography direction for the film was going to rely heavily on atmospherics and fog &lt;a href="https://doi.org/10.1145/3450623.3464676"&gt;[Bryant et al. 2021]&lt;/a&gt; even more than &lt;em&gt;Frozen 2&lt;/em&gt;’s cinematography did.
To make things even harder, we also knew that a lot of these atmospherics were going to be lit using emissive volume light sources like fire or torches; not only did we need a good way to improve how we sampled scattering events, but we also needed a better way to sample emission.&lt;/p&gt;

&lt;p&gt;The solution to the second problem (emission sampling) actually came long before the solution to the first problem (scattering sampling).
When we first implemented our new volume rendering system, we evaluated the emission term only when an absorption even happened, which is an intuitive interpretation of a random walk since each interaction is associated with one particular event.
However, shortly after we wrote our Spectral and Decomposition Tracking paper &lt;a href="https://doi.org/10.1145/3072959.3073665"&gt;[Kutz et al. 2017]&lt;/a&gt;, Peter realized that absorption and emission can actually also be evaluated at scattering and null-collision events too, and provided that some care was taken, doing so could be kept unbiased and mathematically correct as well.
Peter implemented this technique in Hyperion before he move on from Disney Animation; later, through experiences from using an early version of this technique on &lt;em&gt;Frozen 2&lt;/em&gt;, Wayne realized that the relationship between voxel size and majorant value needed to be factored in to this technique.
When Wayne made the necessary modifications from his realization, the end result sped up this technique dramatically and in some scenes sped up overall volume rendering by up to a factor of 2x.
A complete description of how all of the above is done and how it can be kept unbiased and mathematically correct makes up the first part of our talk.&lt;/p&gt;

&lt;p&gt;The solution to the first problem (scattering sampling) came out of many brainstorming and discussion sessions between Wayne, Matt, and myself.
At each volume scattering point, there are three terms that need to be sampled: transmittance, radiance, and the phase function.
The latter two are directly analogous to incoming radiance and the BRDF lobe at a surface scattering event; transmittance is an additional thing that volumes have to worry about over what surfaces care about.
The problem we were facing in optically thin volumes fundamentally boiled down to cases where these three terms have extremely different distributions for the same point in space.
In surface path tracing, the solution to this type of problem is well understood: sample these different distributions using separate techniques and combine using MIS &lt;a href="http://jcgt.org/published/0002/02/10/"&gt;[Villemin &amp;amp; Hery 2013]&lt;/a&gt;.
However, we had two obstacles preventing us from using MIS here: first, MIS requires knowing a sampling pdf, and at the time, computing the sampling pdf for distance sampling in a null-collision system was an unsolved problem.
Second, we needed a way to do distance sampling based off of not transmittance, but instead the product of incoming radiance and the phase function; this term needed to be learned on-the-fly and stored in an easy-to-sample spatial data structure.
Fortunately, almost exactly around the time we were discussing these problems, Miller et al. &lt;a href="https://doi.org/10.1145/3306346.3323025"&gt;[2019]&lt;/a&gt; was published, which solved the longstanding open research problem around computing a usable pdf for distance samples, allowing for MIS.
Our idea for on-the-fly learning of the product of incoming radiance and the phase function was to simply piggyback off of Hyperion’s existing cache points light-selection-guiding data structure &lt;a href="https://doi.org/10.1145/3182159"&gt;[Burley et al. 2018]&lt;/a&gt;.
Wayne worked through the details of all of the above and implemented both in Hyperion, and also figured out how to combine this technique with the previously existing transmittance-based distance sampling and with Peter’s emission sampling technique; the detailed description of this technique makes up the second part of our talk.
The end product is a system that combines different techniques for handling thin and thick volumes to produce good, efficient results in a single unified volume integrator!&lt;/p&gt;

&lt;p&gt;Because of the limited length of the SIGGRAPH Talks short paper format, we had to compress our text significantly to fit into the required short paper length.
We put much more detail into the slides that Wayne presented at SIGGRAPH 2021; for anyone that is interested and is attending SIGGRAPH 2021, I’d highly recommend giving the talk a watch (and then going to see all of the other cool Disney Animation talks this year)!
For anyone interested in the technique post-SIGGRAPH 2021, hopefully we’ll be able to get a version of the slides cleared for release by the studio at some point.&lt;/p&gt;

&lt;p&gt;Wayne’s excellent implementations of the above techniques proved to be an enormous win for both rendering efficiency and artist workflows on &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;; I personally think we would have had enormous difficulties in hitting the lighting art direction on &lt;em&gt;Raya and the Last Dragon&lt;/em&gt; if it weren’t for Wayne’s work.
I owe Wayne a huge debt of gratitude for letting me be a small part of this project; the discussions were very fun, seeing it all come together was very exciting, and helping put the techniques down on paper for the SIGGRAPH talk was an excellent exercise in figuring out how to communicate cutting edge research clearly.&lt;/p&gt;

&lt;div class="embed-container-cinema" id="beforeaftercomparison_crop"&gt;
&lt;/div&gt;
&lt;div class="figcaption"&gt;&lt;span&gt;A frame from &lt;em&gt;Raya and the Last Dragon&lt;/em&gt; without our techniques (left), and with both our scattering and emission sampling applied (right). Both images are rendered using 32 spp per volume pass; surface passes are denoised and composited with non-denoised volume passes to isolate noise from volumes. A video version of this comparison is included in our talk's supplementary materials. For a larger still comparison, &lt;a href="/content/images/2021/Aug/unbiased-emission-and-scattering-volumes/comparisons/beforeaftercomparison_crop.html"&gt;click here.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;h2 id="references"&gt;References&lt;/h2&gt;

&lt;p&gt;Marc Bryant, Ryan DeYoung, Wei-Feng Wayne Huang, Joe Longson, and Noel Villegas. 2021. &lt;a href="https://doi.org/10.1145/3450623.3464676"&gt;The Atmosphere of Raya and the Last Dragon&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2021 Talks&lt;/em&gt;. Article 51.&lt;/p&gt;

&lt;p&gt;Brent Burley, David Adler, Matt Jen-Yuan Chiang, Hank Driskill, Ralf Habel, Patrick Kelly, Peter Kutz, Yining Karl Li, and Daniel Teece. 2018. &lt;a href="https://doi.org/10.1145/3182159"&gt;The Design and Evolution of Disney’s Hyperion Renderer&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics&lt;/em&gt; 37, 3 (Jul. 2018), Article 33.&lt;/p&gt;

&lt;p&gt;Julian Fong, Magnus Wrenninge, Christopher Kulla, and Ralf Habel. 2017. &lt;a href="http://graphics.pixar.com/library/ProductionVolumeRendering"&gt;Production Volume Rendering&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2017 Courses&lt;/em&gt;. Article 2.&lt;/p&gt;

&lt;p&gt;Peter Kutz, Ralf Habel, Yining Karl Li, and Jan Novák. 2017. &lt;a href="https://doi.org/10.1145/3072959.3073665"&gt;Spectral and Decomposition Tracking for Rendering Heterogeneous Volumes&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics (Proc. of SIGGRAPH)&lt;/em&gt; 36, 4 (Aug. 2017), Article 111.&lt;/p&gt;

&lt;p&gt;Bailey Miller, Iliyan Georgiev, and Wojciech Jarosz. 2019. &lt;a href="https://doi.org/10.1145/3306346.3323025"&gt;A Null-Scattering Path Integral Formulation of Light Transport&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics (Proc. of SIGGRAPH)&lt;/em&gt; 38, 4 (Jul. 2019), Article 44.&lt;/p&gt;

&lt;p&gt;Jan Novák, Andrew Selle and Wojciech Jarosz. 2014. &lt;a href="https://dl.acm.org/citation.cfm?id=2661292"&gt;Residual Ratio Tracking for Estimating Attenuation in Participating Media&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics (Proc. of SIGGRAPH Asia)&lt;/em&gt; 33, 6 (Nov. 2014), Article 179.&lt;/p&gt;

&lt;p&gt;Ryusuke Villemin and Christophe Hery. 2013. &lt;a href="http://jcgt.org/published/0002/02/10/"&gt;Practical Illumination from Flames&lt;/a&gt;. &lt;em&gt;Journal of Computer Graphics Techniques&lt;/em&gt; 2, 2 (Dec. 2013), 142-155.&lt;/p&gt;

&lt;p&gt;Ryusuke Villemin, Magnus Wrenninge, and Julian Fong. 2018. &lt;a href="http://jcgt.org/published/0007/03/03/"&gt;Efficient Unbiased Rendering of Thin Participating Media&lt;/a&gt;. &lt;em&gt;Journal of Computer Graphics Techniques&lt;/em&gt; 7, 3 (Sep. 2018), 50-65.&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Mon, 09 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/08/unbiased-emission-and-scattering-volumes.html</guid></item><item><title>Upgrading my old desktop</title><link>https://boyter.org/posts/upgrading-my-old-desktop/</link><description>&lt;p&gt;I used to be really into gaming and even more so building computers, reading CPU reviews, video card reviews, learning about water cooling, what is the best hard disk, psu and such. That was a rather long time ago now. In fact the last time I put any effort into this was the Athlon XP days where I think I had a 2700+ with a Radeon 9700, which was a pretty sick build at the time.&lt;/p&gt;</description><author>Ben E. C. Boyter</author><pubDate>Mon, 09 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/upgrading-my-old-desktop/</guid></item><item><title>Snapshotting memory to scrape encrypted network requests</title><link>https://jonlu.ca/posts/heap-snapshot-scraping</link><description>Using browser heap snapshots to recover structured data after network responses have been encrypted.</description><author>JonLuca's Blog</author><pubDate>Mon, 09 Aug 2021 00:06:09 GMT</pubDate><guid isPermaLink="true">https://jonlu.ca/posts/heap-snapshot-scraping</guid></item><item><title>The Kominsky Method: Season 3</title><link>https://olshansky.info/tv/the_kominsky_method_season_3/</link><description>Olshansky's review of The Kominsky Method: Season 3</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 08 Aug 2021 17:22:51 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/tv/the_kominsky_method_season_3/</guid></item><item><title>The Kominsky Method: Season 2</title><link>https://olshansky.info/tv/the_kominsky_method_season_2/</link><description>Olshansky's review of The Kominsky Method: Season 2</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 08 Aug 2021 17:13:36 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/tv/the_kominsky_method_season_2/</guid></item><item><title>A Brief Analysis of My Review Scores</title><link>https://bastian.rieck.me/blog/2021/review_scores/</link><description>&lt;p&gt;With the Twitterverse aflame and slinging mud concerning the latest
round of &lt;a href="https://neurips.cc"&gt;NeurIPS&lt;/a&gt; reviews, I wanted to follow the
maxim &amp;lsquo;know thyself&amp;rsquo; instead and analyse my &lt;em&gt;own&lt;/em&gt; behaviour as
a reviewer. To this end, I looked at all the review scores I had given
in my &lt;em&gt;initial&lt;/em&gt; review of a paper (the final scores might be
different, but more about that later). I only included reviews from
&lt;a href="https://icml.cc"&gt;ICML&lt;/a&gt;, &lt;a href="https://iclr.cc"&gt;ICLR&lt;/a&gt;, and
&lt;a href="https://neurips.cc"&gt;NeurIPS&lt;/a&gt;, putting them all on the same rating
system, which employs scores from 1&amp;ndash;10. While not all grades can be provided in all
conferences&amp;mdash;ICLR likes to remove some &amp;lsquo;in-between&amp;rsquo; grades in some
instances&amp;mdash;this should provide a rough overview of my tendencies as
a reviewer. Plus, this is not a scientific publication, so a slipshod
statistical analysis will not hurt anyone here.
Here is the resulting plot in all its gory details:&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img src="https://bastian.rieck.me/images/review_score_distributions.svg" /&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;The horizontal line indicates the (weak) acceptance threshold.
There are some interesting observations arising from this sort of
visualisation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;My opinion became more pronounced as time progressed. In 2018, I was
mostly initially opting for weak rejects and weak accepts. Now, being
more familiar with ML, I also feel more confident to express
a stronger opinion in both directions!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The overall tendency is not to accept too many papers in the initial
review. This is consistent with acceptance rates of the conferences.
I make no statements about whether this is also &lt;em&gt;good&lt;/em&gt; or &lt;em&gt;justified&lt;/em&gt;.
I should stress that I am always happy to raise my score if authors
are demonstrating that they thought about my suggestions, in
particular for borderline papers. In several cases, this proved to be
crucial for getting a paper accepted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The year 2020 appears to have slightly lower scores than this year so
far.
I hope that this is not indicative of my mood in this particular
year, but rather an expression of the scoring system that was used
during 2020. ICML, for instance, experimented with a new set of scores and tried to
condense the usual scale. Moreover, I feel that the overall quality
of papers was also slightly lower in 2020. With conferences
receiving a record number of submissions as everyone was quarantined,
I hypothesise that it was easier to submit work that was not quite finished yet. I wonder
whether the official statistics of review score distributions
would corroborate this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The review cycle for 2021 is not over, so the numbers have to be
taken with a certain grain of salt. I would hope that the mean score
goes up, though.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that the distributions are not all of the same size&amp;mdash;the review
load tends to increase over time.
Since there are no glaring outliers,
I am glad that my performance appears to be relatively consistent.&lt;/p&gt;
&lt;p&gt;To
continue this brief foray into my reviewing endeavours, I also
performed a quick sentiment analysis of review contents, using the
marvellous &lt;a href="https://github.com/sloria/textblob"&gt;&lt;code&gt;TextBlob&lt;/code&gt; package for Python&lt;/a&gt;.
This resulted in distributions of the &lt;em&gt;polarity&lt;/em&gt; of my
reviews. Polarity is scaled between $[-1, 1]$, with $1$ and $-1$
representing highly positive and highly negative sentiments,
respectively, whereas $0$ indicates mostly neutral language.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img src="https://bastian.rieck.me/images/review_polarity_distributions.svg" /&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;I think these plots uncover my growth as a reviewer&amp;mdash;I have also run
the same analysis for older reviews and find that my average polarity
slightly increased over time. Earlier reviews were slightly more
guarded and more negative in terms of their language (I sincerely hope that they were not
&lt;em&gt;overly critical&lt;/em&gt;, but I will never get an answer to this question
except by asking authors directly, which is of course not possible).&lt;/p&gt;
&lt;p&gt;As you can see from the score distributions above, more positive
language does not necessarily lead to more papers being accepted.
I think this is an important distinction to make: as I progress in
academia, I like to believe that my understanding and my compassion for
authors increases as well, with the ultimate outcome of me being able to
offer more precise critiques that nevertheless have an underlying
positive tone. At least that is the goal&amp;hellip;&lt;/p&gt;
&lt;p&gt;Closing this rather self-indulgent post, I think everyone would benefit
from running such an analysis on their reviews every once in a while. We
should ensure that we do not become vain, bitter, or belittling when it comes to
the work of others.
Instead, as machine learning venues&amp;mdash;and academia in
general&amp;mdash;become more and more competitive, we should do our best to
offer useful guidance to authors. It is all too easy to focus only on
the flaws and find a paper to be &amp;lsquo;rejected until proven worthy.&amp;rsquo; My nightmare would be to become
someone who writes reviews like the &lt;a href="https://wiki.teamfortress.com/wiki/Demoman"&gt;Demoman&lt;/a&gt; or the &lt;a href="https://wiki.teamfortress.com/wiki/Pyro"&gt;Pyro&lt;/a&gt;
would write them&amp;mdash;a perfect analogy for some of the reviewers encountered in modern
peer review, aptly described by &lt;a href="https://matt.might.net"&gt;Matt Might&lt;/a&gt; in his tongue-in-cheek article &lt;a href="https://matt.might.net/articles/peer-fortress"&gt;Peer Fortress: The Scientific Battlefield&lt;/a&gt;.
I therefore strive to approach every review with a fascination for the
topic and with the hope that the paper will (eventually) be
published. Let&amp;rsquo;s hope that we can all keep this spirit.&lt;/p&gt;
&lt;p&gt;Until next time! May you write the reviews that you like to receive, and may all your
papers be accepted with minor revisions.&lt;/p&gt;
&lt;p&gt;PS: if you are a reviewer yourself, you might consider reading &lt;a href="https://bastian.rieck.me/blog/2019/reviewing/"&gt;On Writing Reviews&lt;/a&gt;, which captures my thoughts about how to write useful reviews.&lt;/p&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Sun, 08 Aug 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/review_scores/</guid></item><item><title>The Violence of God and the Hermeneutics of Paul</title><link>https://josh.works/the-violence-of-god-and-the-hermeneutics-of-paul</link><description>&lt;p&gt;&lt;em&gt;Sometimes I (Josh) want to share around certain academic works. Sometimes its a PDF that I want someone to download and read, sometimes it’s text from a book I’ve read, and cannot otherwise get a sharable format of. So, I laboriously take photos of pages, use an optical character recognition tool to copy-paste the text from the image to my computer, clean it up, and repost it here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What follows is an excerpt from &lt;a href="https://www.amazon.com/Work-Jesus-Christ-Anabaptist-Perspective/dp/193103849X"&gt;The Work of Jesus Christ in Anabaptist Perspective: Essays in Honor of J. Denny Weaver&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Violence of God and the Hermeneutics of Paul&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By Christopher D. Marshall&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The jarring dissonance between the God of vengeance and violence in certain parts of the Old Testament and the God of indiscriminate love and mercy proclaimed by Jesus in the Gospels has long perplexed Christian interpreters. Echoing the verdict of many, Paul Anderson maintains that reconciling these contrasting portraits of God constitutes “the greatest theological and hermeneutical problem in the Bible.” &lt;sup id="fnref:1"&gt;&lt;a class="footnote" href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; It is a theological problem because it challenges the notion of a unitary and self-consistent divine will. It is a hermeneutical problem because it forces the question of how apparently contradictory views of God can exercise authority as equally part of sacred Scripture. It is also a moral problem, for the way believers resolve this ten sion will have implications for how they live their lives as servants of God, even if the common assertion today that violent religious texts lead directly to violent behavior must be dismissed as simplistic.&lt;sup id="fnref:2"&gt;&lt;a class="footnote" href="#fn:2" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;These difficulties are not unique to Christians, of course. Jewish interpreters, too, have long wrestled with how the compassionate God of, say, Ezekiel 33 is to be reconciled with the punitive God of, for example, Numbers 16.3 But these problems of interpretation confront Christian readers in a particularly acute way in light of the theological, moral, and hermeneutical privilege which Christianity accords, at least in theory, to the Jesus tradition.&lt;/p&gt;

&lt;p&gt;One early solution to the dilemma, proposed by Marcion and the Gnostics, was to unhook Jesus entirely from the God of the Jewish Scriptures, who was deemed to be a lesser deity. This option was roundly rejected by the early church. The heresy of ditheism not only leads to theological oblivion, it actually inscribes competitive violence in the nature of ultimate reality, something which biblical monotheism avoids. Christian orthodoxy has therefore always insisted that the God and Father of our Lord Jesus Christ must be none other than the one true God of Israel, the Creator of all that exists.&lt;/p&gt;

&lt;p&gt;But what does this identification tell us about God? Has God changed? Did God give up his violent ways with the advent of Christ in favor of nonviolent love? Is God’s fearsome career as a warrior over? Or does God remain fundamentally unchanged? Is God, by definition, unchangeable: the same yesterday, today, and forever? If so, what does that tell us about God’s involvement in violence? Is lethal coercion still an important part of God’s repertoire of methods for achieving saving and judging purposes in human affairs as it was in biblical times?&lt;/p&gt;

&lt;p&gt;If the testimony of Christian history and doctrine are indicative, the latter appears to be the most common conclusion Christians have reached, since God’s use of violence has continued to be as notable a feature of the Christian story of God as it was in the earlier Israelite story of God. Much atonement theology, for instance, as Denny Weaver spells out so clearly, envisages a patriarchal God who visits punitive violence on his only son to defend his personal dignity or uphold his superior justice. Similarly the post-Constantinian church’s majority endorsement of Christian participation in war has disclosed a willingness, if not an eagerness, to equate the violent shedding of human blood with the work and will of God.&lt;/p&gt;

&lt;p&gt;Then there is the projected violence of final judgment. Even those who feel squeamish about the idea of God using human agents to visit wrath on his enemies in present history still often espouse a concept of eschatological judgment where God, patience finally exhausted, violently destroys the wicked-or worse, callously consigns them to the everlasting violence of eternal torment. It is not uncommon even for Christian pacifists, who conscientiously renounce lethal violence themselves, keenly to anticipate God’s retribution on the un godly at the end of time. Some even argue that Christian renunciation of violence is materially dependent on the reality of God’s ultimate retributive justice on evildoers, for only an absolute confidence in God’s perfect judgment can free believers from the need to take matters into their own hands in the interim.5 Put crudely, from this perspective Christian nonviolence is sustainable only if there is a violent God giving ultimate backup.&lt;/p&gt;

&lt;p&gt;So Christian belief and practice have not, by and large, done away with the concept of divine violence. Even peace theology has not felt the need to posit a nonviolent God. Of course, retention of a militant deity eases the hermeneutical dilemma mentioned earlier, in that the violent God of early Israel does not cease to be violent in the Christian era but merely becomes less blatantly so. Instead of regularly deputizing human agents to slay the wicked, God now largely reserves that task for God’s self at the end of time (although exceptions may apply in the event of justifiable war).&lt;/p&gt;

&lt;p&gt;Yet arguably a theology that substitutes a conspicuously violent God with a cautiously violent God remains mired in what Walter Wink famously calls the myth of redemptive violence-the belief that violence saves, that war brings peace, that might makes right. As long as God is understood to rely on lethal force to achieve redemptive goals, whether in present history through appointed human instruments or at the end of time by God’s own clenched fist, disturbing implications follow, and especially for Anabaptist peace theology.&lt;/p&gt;

&lt;p&gt;One such implication is the absolutizing or deifying of violence. To reserve to God the right to use overwhelming violence is to pay violence the ultimate compliment. Violence is dignified by its association with God, and God is diminished by dependence on violence. In deed, the more violence is reserved as God’s prerogative alone, the more uniquely and terrifyingly violent God appears. “It is a fearful thing to fall into the hands of the living God.” But if frail humans are expected to feel angry without resorting to violence, why should not the same be expected of God? Is God unable to withstand the temptation to hit back? Is crushing the opposition God’s only solution?&lt;/p&gt;

&lt;p&gt;Again, a willingness to exempt God from the normative requirement of nonviolence sustains the kind of exceptionalism that always leaves the door open for human beings to perpetrate violence on God’s behalf or approve of it when done in God’s name. As long as killing is sanctioned by God-whether now in times of war or during some future apocalyptic maelstrom – no moral condemnation can apply to those who carry out the killing.&lt;/p&gt;

&lt;p&gt;Note, however, that whenever exceptions are permitted, there is a human propensity to expand the loophole endlessly. Each and every act of violence can be defended as a legitimate exception to the rule. Not surprisingly, it is always the perpetrators of violence, not its victims, who appeal to exceptional circumstances to legitimate their actions. Something similar happens with respect to those who cherish the expectation of God’s final destruction of sinners. They exempt themselves and their loved ones from the awaited firestorm while insisting that God’s justice requires others to be consumed by it. &lt;sup id="fnref:8"&gt;&lt;a class="footnote" href="#fn:8" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The grounding of Christian non-retaliation on trust in God’s eschatological vengeance also has troubling corollaries. The more one waits for God’s violent intervention to rectify the world, the less incentive there is to work for peaceful change now. Indeed, it could be argued (and often has been historically) that since God is clearly not yet riled enough by sin and injustice to intervene to overturn it, the unjust status quo ought to be accepted as something God permits to exist. Christians should not then actively oppose it, for that would be to usurp God’s work. Pacifism then slides into a passivism that leaves it over to God to sort out the mess while keeping one’s own hands clean. Although John Howard Yoder rejects such quietism, this could be one way of construing (or misconstruing) his contention that Christian nonviolence bears witness to Christians relinquishing control of history. Their call is to be faithful to the slain lamb, not to be “effective” in directing historical processes, a goal which is frequently taken to justify violence and is an illusory ambition at the best of times. &lt;sup id="fnref:9"&gt;&lt;a class="footnote" href="#fn:9" rel="footnote"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Does this then mean that assuming responsibility for the direction of human history necessarily requires violence, even for God? Is violence an inescapable corollary of exercising supreme authority? Perhaps a better grounding for Christian pacifism would be the recognition that, in a real sense, humans are in charge of this world, and that it is only by emulating the nonviolent rule of God that we have any real chance of undoing the yokes of oppression and recover ing the true purpose of our existence. After noting the vast literature written on theodicy and on whether God can ever be forgiven for the atrocities permitted on his watch, such as the incineration of a five year old girl called Esther before the eyes of her parents, or the random assassination carried out by a Palestinian gunman called Omar, J. Harold Ellens comments:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have figured out that the question is erroneous. It is not a question of justifying God or of bringing God to justice. It is not a question of forgiving God or holding God accountable. It is a matter of recognizing the limitations of God. God is not in charge of this world. We are. We should have gotten that clue at a more profound level of awareness than we seem to have done, from Genesis 1:28, where the ancient Israelite narrative informs us that God assigned us the task of dominion in this world, to bring it to its potential fruitfulness. If we do not take care of beautiful blond and blue-eyed Esther, God cannot. If we do not find reconciliation with Omar, God cannot stop the mass murders. If we do not reach beyond the alienations and transcend the terror of the terrorists, God cannot save us. &lt;sup id="fnref:10"&gt;&lt;a class="footnote" href="#fn:10" rel="footnote"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But perhaps the biggest problem with continuing to imagine a God who employs violence to advance his cause in the world is that it leads to an incoherent Christian monotheism. On the one hand, Jesus Christ is held to be the perfect revelation of the one true God. On the other hand, the God whom Jesus is said to reveal bears little resemblance to Jesus.&lt;/p&gt;

&lt;h2 id="jesus-as-the-nonviolent-icon-of-god"&gt;JESUS AS THE NONVIOLENT ICON OF GOD&lt;/h2&gt;

&lt;p&gt;The Christian story rests on two fundamental truth claims which set it apart from all other religious and ideological systems. First, it claims that the Creator God is made most fully known in the human person of Jesus Christ. If we want to know what God is really like, the New Testament authors submit, we must look at Jesus. He is the supreme benchmark for our understanding of Deity, “He is the image of the invisible God,” the apostle Paul writes, the one in whom “all the fullness of God was pleased to dwell.” “He is the reflection of God’s glory and the exact imprint of God’s very being.” Hebrews declares. He is also the one “through whom God created the worlds.” &lt;sup id="fnref:12"&gt;&lt;a class="footnote" href="#fn:12" rel="footnote"&gt;6&lt;/a&gt;&lt;/sup&gt; “ All things came into being through him,” John’s Gospel begins, “and without him not one thing came into being. What has come into being in him was life, and the life was the light of all people.” &lt;sup id="fnref:13"&gt;&lt;a class="footnote" href="#fn:13" rel="footnote"&gt;7&lt;/a&gt;&lt;/sup&gt; “For in him all things in heaven and on earth were created, things visible and in visible, whether thrones or dominions or rulers or powers-all things have been created through him and for him.” Jesus, then, is both the human embodiment of God’s very being and the one through whom and for whom God created the universe.&lt;/p&gt;

&lt;p&gt;This conviction invests the words and deeds of Jesus of Nazareth with unparalleled revelatory significance. When Jesus teaches and practices nonviolence, therefore, it is not enough to see it as a tactical expedient he employs because revolutionary violence against the Roman superpower was not a viable option at that time. It must be understood more profoundly as an articulation of what God is like and of how God exercises divine rule. That is why Jesus explicitly grounds his summons to enemy love and non-retaliation in the imitation of the heavenly Father, who “makes his sun to rise on the evil and on the good, and sends rain on the righteous and the unrighteous.” Jesus instructs his followers to conduct themselves as “children of the Most High, for he is kind to the ungrateful and the wicked.” They are to be merciful “just as your Father is merciful.”&lt;sup id="fnref:17"&gt;&lt;a class="footnote" href="#fn:17" rel="footnote"&gt;8&lt;/a&gt;&lt;/sup&gt; Jesus’ ethic of nonviolence, in other words, is predicated on the premise of a nonviolent God, a God who, as Raymund Schwager puts it, is “exactly the opposite of violence,” a God whose “limitless forgiveness and boundless love are distinct in every respect from the mechanism of violence and the vicious cycle of mutual destructiveness.”&lt;sup id="fnref:18"&gt;&lt;a class="footnote" href="#fn:18" rel="footnote"&gt;9&lt;/a&gt;&lt;/sup&gt; Those who claim to follow this God must therefore be nonviolent too.&lt;sup id="fnref:19"&gt;&lt;a class="footnote" href="#fn:19" rel="footnote"&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The second truth claim Christianity makes is that God has acted uniquely in the life, death, and resurrection of Jesus to restore the world to its originally intended state of freedom. In Jesus, God has entered fully into the human condition, shackled as it is to the power of sin and subject to the scourge of suffering and death, and has acted through him to defeat the power of evil and reconcile its victims to himself. “He has rescued us from the power of darkness,” Paul rejoices, “and transferred us into the kingdom of his beloved Son, in whom we have redemption, the forgiveness of sins…. For through him God was pleased to reconcile to himself all things, whether on earth or in heaven, by making peace through the blood of his cross. “&lt;sup id="fnref:20"&gt;&lt;a class="footnote" href="#fn:20" rel="footnote"&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Not only is “the blood of his cross,” by which Paul means his violent death on a Roman gallows, the decisive event that defeats evil and brings about peace, it is also the definitive revelation of what God is really like. Christian faith asserts that God is never more truly God than he is in the dying of Jesus. In the cross, as the gospel writers put it, the veil of the temple is torn in two and God stands revealed. God’s justice also stands revealed,&lt;sup id="fnref:21"&gt;&lt;a class="footnote" href="#fn:21" rel="footnote"&gt;12&lt;/a&gt;&lt;/sup&gt; The cross shows that God’s justice is a peacemaking justice,&lt;sup id="fnref:22"&gt;&lt;a class="footnote" href="#fn:22" rel="footnote"&gt;13&lt;/a&gt;&lt;/sup&gt; a reconciling, restoring, and healing justice. The God who is made climactically known in the cross of Christ is a God who secures justice not by violent imposition of his will on his enemies but by freely subjecting himself in suffering love to the violent impulses of humanity to liberate creation from its bondage to violence and to restore people to relationship with God and with each other.&lt;/p&gt;

&lt;p&gt;These, then, are the two mind-boggling assertions the New Testament authors make. They dare to propose that Jesus of Nazareth is the human face of God, “the flesh and blood embodiment of the perfections of God, and that the true character and the justice of this God are nowhere more evident than in his death and resurrection. But this is not all. From these two claims they arrive at a critically important deduction that what we learn of God in the story of Jesus is the key to understanding the meaning, inter-connectedness, and destiny of all created reality. As Ephesians states, “All things have been created through him and for him. He himself is before all things, and in him all things hold together. In him God has made known his “plan for the fullness of time, to gather up all things in him, things in heave and things on earth. “&lt;sup id="fnref:25"&gt;&lt;a class="footnote" href="#fn:25" rel="footnote"&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;What an astonishing assertion this is! All things have been created for, they are sustained by, and they find their ultimate meaning in Jesus Christ. All things are eternally imprinted with the moral character and career of the crucified and risen Lord. From this it follows that the central principle of creation is not naked power, or control, or order, or balance, but vulnerable, passionate, reconciling, self giving love, a love which subverts evil, not by an overwhelming dis play of coercive force, but by acting in amazing grace to redeem of fenders and to heal sin’s victims, and at great cost to itself. In short, the Jesus story reveals that God’s nonviolent love is the ground of the universe.&lt;/p&gt;

&lt;p&gt;This is not a wholly new revelation, however. It is already evident in the creation narratives of Genesis. It is hugely significant that, notwithstanding the violence ascribed to God in the pages of the Bible, the canonical record opens and closes with surprisingly peaceful scenes – the two accounts of creation (Gen. 1-2) and the presentation of the New Jerusalem (Rev. 21-22). Peace is both ontologically anterior to violence and eschatologically posterior to it. it. Violence has no role in God’s work of creation; it only enters into the picture later as a result of human sin and will eventually come to an end. This is quite different from other ancient Near Eastern creation myths, such as the Babylonian Emuna Elish, where creation is the result of a violent act of deicide and humans are created from the blood of the murdered god. There evil precedes good, chaos is conquered by violence, and the king serves as Marduk’s representative on earth, ruling by means of holy war. By contrast, as Walter Wink observe:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Bible portrays a good God who creates a good creation. Chaos does not resist order. Good is before evil. Neither evil nor violence is a part of the creation, but enter later as a result of the first couple’s sin and the connivance of the serpent (Gen. 3). A basically good reality is thus corrupted by free decisions reached by creatures. In this far more complex and subtle explanation of the origins of things, violence emerges for the first time as a problem requiring a solution. &lt;sup id="fnref:27"&gt;&lt;a class="footnote" href="#fn:27" rel="footnote"&gt;15&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The creation narratives, in other words, presuppose a divine ontology of peace. They portray a nonviolent God who speaks the world into existence and who makes human beings in the divine image and likeness to cultivate creation as devoted gardeners, not to pillage it as rapacious warriors. Things go badly wrong, however, and violence invades this peaceable reality. This is no minor problem that can be easily fixed. It not only escalates out of all control in the human community, it even provokes God to an act of massive counterviolence, something God apparently later regrets. Thus begins the long canonical story of divinely induced violence, a story which stands in stark contrast to what we have seen of God in the story of Jesus and cries out for some theological explanation&lt;/p&gt;

&lt;p&gt;It is important that this explanation is &lt;em&gt;theological&lt;/em&gt; in character, not just traditio-historical or psycho-cultural or phenomenological, be cause the canonical text is principally intended to exercise a theological role in the community that created and preserves it. The biblical traditions, for all their diversity, once gathered together as canonical Scripture, combine to tell a single overarching story about Israel, God, and the world. The texts constitute a narrative world into which readers enter, with its own plot, its own cast of characters (including God), and its own universe of meanings, a story which serves authoritatively to interpret God to Israel and Israel to itself.”In this connection it matters little whether the biblical accounts are historically reliable in all their details; of primary importance is the theological claim they make to narrate the story of God. This means that the problem of divine violence cannot be dealt with adequately simply by deeming this or that episode to be legendary or fictitious. Whether or not God actually killed the children of Egypt, the biblical story &lt;em&gt;says&lt;/em&gt; God did. This is how the biblical writers understand the involvement of God, and it is their interpretation of God that has normatively shaped and conditioned how the subsequent community of faith (including Jesus) has apprehended God.&lt;/p&gt;

&lt;p&gt;What is needed, then, is some explanation for God’s violence that both accords with the inner logic of the plotted narrative and that ac counts for &lt;em&gt;why&lt;/em&gt; it is that God is characterized in this way, especially when there are strong indications at the outset of the story that the Creator God works by peaceable means. Given the many incidents of divine violence that follow, it is also worth pondering how Jesus could possibly have conceived of God as being unfailingly kind to the ungrateful and wicked when he knew full well that within the biblical drama God discriminates in favor of friends, bears grudges against opponents for generations, and visits retribution on sinners.&lt;/p&gt;

&lt;h2 id="the-violence-of-god-in-scripture"&gt;THE VIOLENCE OF GOD IN SCRIPTURE&lt;/h2&gt;

&lt;p&gt;The Hebrew Bible has a reputation for being one of the bloodiest works in existence, Firmly fixed in the popular mind is the image of the “Old Testament God” as a God of war and destruction, a brutal, reactive, and ruthless deity who brooks no rivals and leaves no infraction unpunished. Whether this menacing reputation is fully deserved is debatable. Patricia M. McDonald argues that there is far less violence in the Hebrew Scriptures than is generally supposed and that the deepest concern of the biblical authors is to encourage ways of living that overcome violence and foster compassion. The problem with any textual portrayal of violence, she suggests, is that it tends to have a disproportionate impact on readers. Violent language and imagery take over our imagination and absorb our interest, so that we “find” more violence than is actually there.&lt;/p&gt;

&lt;p&gt;This rhetorical impact of the violence also diverts attention away from other themes in the text that are often of more fundamental importance to the author. The peripheral thus becomes central and the central peripheral. Nor, McDonald adds, does all the violent imagery used in the Bible function to endorse actual violence. The specifically military imagery for God, for example, is plainly metaphorical. God’s defeat of Egypt may be spoken of as a triumph of war,33 but in practice it was achieved by non-military terms. Although Yahweh uses “weapons” to overthrow his enemies, his weapons are the forces of the natural order. The language is analogical, not literal, although, as McDonald concedes, this choice of military analogies did encourage dangerous perceptions about God to emerge.&lt;/p&gt;

&lt;p&gt;As helpful as these considerations are, the fact remains that the biblical narrative attests to a deep and pervasive association between God and deadly violence. The connection between them is varied and complex. Sometimes God resolutely opposes the use of violence and identifies wholly with the victim. At other times God is portrayed as the perpetrator of violence, either by direct fiat or by organizing and sanctioning others to visit judgment on a disobedient people. Often it is precisely God’s capacity for superior violence that serves to establish Yahweh’s credentials as the only true God. Violence thus emerges as the most frequently mentioned activity in the Hebrew Bible. Schwager has counted some 600 passages in which violence is recorded and at least 1,000 verses in which God’s violence is de scribed. In some 100 passages God expressly commands people to kill others, and in some stories God tries to kill people for no apparent reason.4 God’s violence is particularly evident in the wilderness stories. It has been calculated that in the forty year period between Israel’s exodus from Egypt and their entry to Canaan, the Lord executes at least 30,000 of his own people. 35 Three times Yahweh threatens to annihilate them entirely, and on a number of occasions strikes them with plagues as he did the Egyptians,37 Even greater in number than such narrative acts of violence are the potential acts of violence commanded in the enforcement of the laws of the Sinai covenant.38&lt;/p&gt;

&lt;p&gt;The blood of Israel’s enemies runs even more freely. Yahweh smites innocent Egyptian children in Exodus 12, drowns the Egyptian army in the sea in Exodus 14-15, opens up the ground to swallow the Korahites in Numbers 16, orders the impaling of Baal worshippers in Numbers 25, calls for reprisal on the Midianites in Numbers 31, and orders the conquest of Canaan in Joshua 1. The most chilling of all biblical texts to do with war and violence are those that refer to the &lt;em&gt;herem&lt;/em&gt; or “ban,” under which all human beings among the defeated are “de voted to destruction,” sometimes at God’s explicit command.39 Emotions of pity are expressly forbidden in such cases.40 Susan Niditch identifies two main ideologies undergirding and justifying this practice of genocide. In one, the ban is understood as a sacrificial offering to God, which presupposes a God who appreciates human sacrifice. In the other, the ban is viewed as an act of divine justice upon idolatry, which was thought to threaten Israel’s own purity and survival. In both cases mass slaughter is a means of gaining God’s favor.&lt;/p&gt;

&lt;p&gt;It is possible to identify certain ameliorating features in the institution of &lt;em&gt;herem&lt;/em&gt;. McDonald mentions, for example, its role in discouraging the use of war purely for plunder, since all captured possessions were to be destroyed.42 Niditch detects a paradoxically high view of human dignity implicit in the ban. The “terrifying complete ness and fairness” of the ban’s indiscriminate massacre “may be viewed as admitting more respect for the value of human life than other war ideologies that allow for arbitrary killing of soldiers and God is still cast in the role as author or condoner of immense brutality, civilians. “43This sounds like special pleading. But even if it were true, the instigator of repeated episodes of ethnic cleansing, the perpetrator of what today would be termed crimes against humanity. Accord ing to one critic, the God of Exodus, Deuteronomy, and Joshua is little better than a violent, murderous, genocidal land thief. “Troubling images of God cascade from biblical texts like waterfalls after a violent storm. God’s repugnant words and pathological behavior are so widespread as to be considered normative behavior for God!”44 This judgment may be one-sided. But the fact that it can seriously be made at all is testimony to the extent to which God is deeply embroiled in gut-wrenching brutality in many parts of the biblical narrative.&lt;/p&gt;

&lt;p&gt;The most graphic accounts of divinely approved violence are set in the context of Israel’s early history, even if they were composed at a later stage. With the establishment of the monarchy, the pure ideal of holy war comes to an end and Israel learns to rely on conventional military might. War still carries a sacral element to some extent, but, as Mark McEntire explains “God is removed from the battlefield and eventually closed off in the temple along with the ark… the of God is no longer an essential element of a battle.” 45 In later presence prophetic and apocalyptic literature, emphasis shifts from the mediation of divine violence through historical agents to the expectation of God’s eschatological victory, which is portrayed in language no less violent than is found in Deuteronomy and Joshua. The Psalms also celebrate and anticipate the coming of the “God of salvation” who will “shatter the heads of his enemies” and will enable his own people to “bathe their feet in blood” and to feed their enemies to their dogs.46&lt;/p&gt;

&lt;p&gt;Not that God ever deserts the cut and thrust of present history. McEntire points out that the Hebrew canon (unlike the Greek canon and Christian Old Testament canon) closes with 2 Chronicles 36, which tells of the destruction of Jerusalem by the Babylonians. Although the term &lt;em&gt;herem&lt;/em&gt; is not used, the many parallels between 2 Chronicles 36 and Joshua 6 give the unmistakable impression that the city has fallen because God has instituted a ban on Israel. The classical prophets toyed with the possibility of Yahweh turning against Israel for breaking covenant,47 now it has happened. Yahweh disowns his people and brings devastation upon the holy city. No mercy is shown to its inhabitants; the chosen people are either killed or carried off into exile. “Yahweh begins the story as the compassionate, patient God of a disobedient people. As the story progresses, Yahweh becomes a wrathful avenger whose actions are confused with and indistinguishable from those of an earthly tyrant.” 48&lt;/p&gt;

&lt;p&gt;To be sure, bringing such episodes of divine violence into the limelight in this way is potentially misleading, for there is quite another side to God’s character in the biblical accounts as well. Hosea 11, for example, depicts Yahweh in emotional turmoil, torn apart by conflicting feelings of outrage at his people’s sin and tender compassion for their plight, contemplating terrible punishment but recoiling in horror from the prospect of executing fierce anger against them and promising never again to destroy Ephraim. Elsewhere too God is revealed as a God of mercy, forgiveness, and love, a God who displays a special concern for the most vulnerable members of the covenant community, a God who liberates the poor from oppression and who heals and restores the victims of violence. Indeed, this biblical notion of a God who sides with the weak and the downtrodden is radically different from that which prevailed in the ancient Near East, where the gods inaugurated and upheld the hierarchical structures of wealth and power.50&lt;/p&gt;

&lt;p&gt;Even so, God’s concrete actions at times belie God’s words. Yahweh appears not only as the God of victims but also as the God who devours victims. How the head-smashing God of the historical narratives, the psalms, and the prophets can be reconciled with the nonviolent God disclosed by Jesus still needs to be answered.&lt;/p&gt;

&lt;h2 id="hermeneutical-strategies"&gt;HERMENEUTICAL STRATEGIES&lt;/h2&gt;

&lt;p&gt;Many different interpretive strategies have been employed to try to resolve this problem. One common tactic is to refuse to question anything God does, as a matter of principle, for God’s ways are higher than our ways and God’s thoughts higher than ours (Isa. 55:9). God is sovereign and holy; it is simply wrong for human beings as mere creatures to subject God’s actions to moral scrutiny.&lt;/p&gt;

&lt;p&gt;But this pious concern to save God from criticism ducks the problem. Even Abraham was prepared to challenge God’s projected violence against the inhabitants of Sodom on the ground that the judge of all the earth has a moral obligation to abide by the principle of justice. If God’s deeds are beyond all moral valuation, so that nothing God ever does can be called bad, then it is equally true that nothing God does can ever be called good, and no way finally exists to differentiate between God and the Devil. There is also no ground for challenging religious zealots today who cast themselves in the role of human agents of divine wrath on the basis that whatever God is said to have done in sacred Scripture must be inherently good.&lt;/p&gt;

&lt;p&gt;Another common strategy is to offer pleas of mitigation on God’s behalf, to find in each episode of bloodletting a justifiable reason for divine judgment. The dispossession and extermination of the Canaanites, for example, is seen as fitting punishment for their idol try and wickedness. It is also often observed that given Israel’s precarious predicament in a hostile world, God simply had to employ lethal violence to safeguard the chosen instrument of salvation for the benefit of us all. God’s violence is an example of “good violence rather than “bad violence” because it is a redemptive or salvific violence 52&lt;/p&gt;

&lt;p&gt;Such appeals to mitigating circumstances, however, come perilously close to an ends-justifies-the-means style of moral reasoning It is true that the conquest of Canaan is sometimes defended in the biblical text as deserved retribution for the idolatry of the inhabitants. But the justice of the penalty is by no means self-evident. As McEntire points out, “The inhabitants of Jericho, and Canaan in general, are never accused of anything other than two dubious transgressions. They happen to live in the land Yahweh promised to Abraham and they do not exclusively worship a god, Yahweh, who has never been revealed to them.” McEntire notes that “As punishment for these transgressions, they become victims of destructive violence.” The net result is “people minding their own business becoming dead so that nomads can become farmers.”54&lt;/p&gt;

&lt;p&gt;It is also true that the continued existence of Israel as a holy people is of overriding importance to the story of salvation and that God favored their interests for the ultimate good of all humanity. But it is extremely doubtful that every episode of divinely sanctioned blood shed was critical to the maintenance of Israel’s national and religious integrity. Sometimes relatively minor infractions attract massive retribution while at other times mercy is extended to serious breaches of covenantal boundaries. It also needs saying that the ease with which some interpreters defend the necessity and morality of the “redemptive violence” employed in biblical history attests to an imaginative failure on their part to recognize the hideous suffering endured by its victims. Christian readers recoil in horror at the Christmas story of Herod’s slaughter of the infant boys in Bethlehem, yet often barely flinch when reading of God ordering the massacre of the Amalekite infants, and some two centuries after the offense for which the people were allegedly being punished.” Readers may delight in the sparing of Rahab and her family when Joshua “fit de battle of Jericho” but spare no thought for all the other inhabitants of the city who were devoted to destruction by the edge of the sword, both men and women, young and old, oxen, sheep, and donkeys” (Josh. 6:21).&lt;/p&gt;

&lt;p&gt;A third interpretive strategy is to sanitize the real-life violence of the biblical text by allegorizing or spiritualizing it. There is a long and venerable approach to biblical interpretation that is so convinced of the text’s divine origins that where the literal sense of the words pro duces impossible results, one must look for a deeper allegorical meaning, a meaning, as Origen put it, that is “worthy of God.” By this method stories of war and mayhem are transposed into uplifting moral and spiritual truths. But despite its impressive pedigree, allegorization is hardly a viable method in the modern world for dealing with offensive texts. Moreover, even if the allegorical meaning may be counted as worthy of God, the literal historical meaning remains deeply problematic.57 Allegorists and Sunday school teachers may find comforting spiritual messages disclosed by violent texts, but the texts themselves were initially intended to portray and validate actual bodily suffering and death.&lt;/p&gt;

&lt;p&gt;Yet another hermeneutical strategy is to dismiss the violent portrayals of God in the biblical text as historically and theologically false. They are the product of the biblical authors projecting onto God their own violent fantasies and vengeful impulses, seeking to justify human atrocities by claiming God decreed them. God thus gets the blame for what in reality were acts of human malice. This means that rather than blithely accepting the truthfulness of everything the text says about God as guaranteed by divine revelation, it is important to separate the wheat from the chaff, to distinguish the voice of divine truth from the voice of human self-deception.58 Nelson-Pallmeyer makes this point with uncompromising clarity:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Bible tells us more about human beings than it does about God, and it does so even when it claims to be talking about God. Revelation within the biblical story, in my view, is rare, and often overwhelmed by distorted human projections. The Bible is both sacred and dangerous. It is sacred because God is revealed partly within the experiences of those responsible for its pages. That is why many of us return to it day after day and year after year in search of meaning and guidance. It is also a dangerous book because we often ascribe divine will to the many human distortions it contains. We undermine the sacredness of the Bible and fuel its dangers whenever we fail to discern the difference between distortion and revelation…. Stated simply, the Bible can inform our religious experience, but it is often wrong about God.99&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Work of Jesus Christ in Anabaptist Perspective For Nelson-Pallmeyer it is not enough just to recognize the metaphorical character of the violent images used for God. Violent metaphors must be utterly repudiated as false and abusive. They distort the truth of God and should be expunged from the language of theology and worship. Other scholars, however, warn against discarding such morally offensive material, for, as John Collins suggests, violent texts still possesses revelatory power insofar as they give “an unvarnished picture of human nature and of the dynamics of history, and also of religion and the things that people do in its name.” 60&lt;/p&gt;

&lt;p&gt;This fourth hermeneutical strategy is helpful in alerting us to the complexity of the interface between divine revelation and human reception. Comprehending God’s self-disclosure in the biblical narrative is like following a fine silver thread woven into a dense and colourful tapestry, a tapestry embroidered from a wide range of human reflections and actions that are always culturally conditioned and ideologically slanted. Accordingly, just because God’s permission is frequently evoked by biblical figures does not necessarily mean that God has spoken in every one of those instances. Yet how do we make allowance for this possibility without remaking God in our own image? How do we avoid simply favoring those parts of Scripture that suit our own prejudices and biases and discarding the rest as false projections, thus reducing the text to an echo-chamber of modern liberal values and preconceptions? And how is the canonical status of the text sustained if we accept only the nice bits?&lt;/p&gt;

&lt;p&gt;The violence of God material is not easily separable from other more peaceful conceptions of divine activity. In one form or another, divine violence is woven into warp and woof of almost the entire biblical tradition. The self-same texts that extol God’s graciousness and mercy and forgiveness and slowness to anger, also warn of God visiting the iniquity of the parents on their children for three or four generations.61 No wonder Nelson-Pallmeyer is forced to conclude that revelation is rare in the Bible.&lt;/p&gt;

&lt;p&gt;This brings us to a final, and more promising, hermeneutical strategy, one that allows for a substantial change in God’s relation ship to violence in the course of the biblical story. In this approach the violent texts are not to be rejected as a simple distortion of divine reality. They are a reliable reflection of how God was experienced at this time. Amid the violence that pervaded human life and society, people encountered God as someone directly involved in the messiness of human life and conflict. They knew that God abhorred the squander ing of human life, for it was invested it with sacred significance. Yet they also wanted a God who would employ coercive power in great measure to punish, protect, and correct. Elijah, for instance, invited God to cast fire down from heaven to consume his opponents, for this was how any self-respecting deity should prove his superiority when challenged by competitors. 62&lt;/p&gt;

&lt;p&gt;With the coming of Jesus, a fresh experience of God is afforded. When Jesus’ disciples wanted to emulate Elijah by calling down fire on a Samaritan village, Jesus “turned and rebuked them. He did so not simply because such an extreme reprisal would be unfair in this particular circumstance, but because he considered violent vengeance to be wrong in principle and because he knew that God should no longer be understood to work in this way. God has disarmed! God’s perceived involvement in the infliction of violence is over. God no longer fights fire with fire. God has changed – or, perhaps more accurately, the human experience of God’s association with violence has changed. God will no longer permit his identity to be defined by violence; God actively repudiates the violent behavior which has hitherto clouded his character so that the duplicity of violence itself may be exposed and defeated.&lt;/p&gt;

&lt;h2 id="gods-changed-relationship-to-violence"&gt;GOD’S CHANGED RELATIONSHIP TO VIOLENCE&lt;/h2&gt;

&lt;p&gt;I noted earlier that the creation narratives presuppose a divine ontology of peace. Violence is neither part of God’s creative activity nor of God’s internal being. Violence appears only after the human community has fallen into sin. Interestingly the subsequent account depicts an initial reluctance on God’s part to employ violence, then a decision to do so, followed by a recoiling from its drastic consequences. This imaginative portrayal of divine ambivalence toward violence in the early scenes of the human story is, I suggest, an important key for understanding what follows. It dramatizes a profound insight into the perversity of violence, namely, that once violence is entrenched in human society, even the sacred is captured by its allure. Once “the earth is filled with violence” (Gen. 6:11,13), humanity’s apprehension of the divine is inescapably framed by its desire to have a deity whose power is greater than that of human violence and whose greatness is shown precisely in a heightened capacity for violence. The violence-of-God material that pervades the biblical narratives is thus symptomatic of this capture while also disclosive of it.&lt;/p&gt;

&lt;p&gt;God’s reply to Adam and Eve’s disobedience is not, at first, a violent one. Along with other consequences they are banished from the Garden to prevent their fallen condition from becoming everlasting (Gen. 3:22-24). When in the second generation the problem of sibling rivalry arises, God tries to warn Cain off from succumbing to his feelings of jealousy and resentment, but without success. Sin is personified as a hungry animal lying at the door ready to spring (4:6-7). It is so powerful that it overcomes the will of God and overtakes the passions of Cain, and he turns to violence. After Cain murders his brother, God still does not respond violently; instead the Lord acts to protect the life of the killer, though now threatening sevenfold vengeance against any who disregards the protective mark on Cain (4:15). God’s next response to human sinfulness is to impose a 120 year limit on the human life span (6:3) and to express regret over the decision to create human beings in the first place (6:6). But as violence spins out of control and fills the land, God plans, for the first time, an act of violent retribution in which “everything that is on the earth shall die” (6:17). In due course God carries out the plan, blotting out all but a tiny handful of creatures (7:21-24).&lt;/p&gt;

&lt;p&gt;Afterward, however, God is deeply disturbed by the indiscriminate nature of the punishment employed. Non-human creation has been made to bear the brunt of humanity’s sin. At the same time, God recognizes that wiping out sinful people still has not actually removed the problem of sin, “for the inclination of the human heart is evil from youth” (8:21, cf. 6:5). God therefore promises never again to curse the ground and destroy all living creatures because of human evil (8:21-22; 9:11, 15). As McDonald observes, “God tried violence once and now knows better.”God then makes two telling concessions to the descendants of Noah: humans may now kill animals for food (9:3-4), and those who take human life will face the death sentence (9-5-6), an attempt to use judicially circumscribed violence to break the spirit of vendetta displayed earlier by Lamech (4:23-24). In a sense, God makes a compromise with violence. Lethal violence in the human community is forbidden, but those who resort to it can expect God to respond in like manner.&lt;/p&gt;

&lt;p&gt;The patriarchal narratives that follow are remarkably peaceful. There is very little violence described at all, although God is often involved in what does occur, such as the destruction of Sodom and Gomorrah, and God’s long-term plan to dispossess the Canaanites of their land is frequently alluded to. Violence only becomes more pronounced during and after the deliverance from Egypt, and especially during the conquest of Canaan where, as we have seen, genocide is commanded. Thereafter in the waxing and waning of Israel’s fortunes, God is frequently depicted as the author and instrument of violence, ranging from incidents of individual retribution to episodes of large-scale war-making.&lt;/p&gt;

&lt;p&gt;God’s actual responsibility for authorizing this violence is, of course, doubtful, if not impossible. It is a standard feature of biblical idiom to ascribe a causative role to God for almost everything that happens, both good and bad, as well for narrators to assume a God’s eye perspective in interpreting events. It is crucial to recognize this interpretive technique. Raymund Schwager divides the texts that speak of God’s violence in the Old Testament into four categories: those where Yahweh strikes out irrationally for no apparent reason (which are extremely rare), those where God personally takes revenge on human wrongdoing, those where God uses other human beings to punish evildoers, and those where the wicked are punished by their deeds rebounding back on them under God’s supervision.”1&lt;/p&gt;

&lt;p&gt;But the distinction between these categories is more rhetorical than real, for the narrative texts show that even where God’s direct retribution is talked of, it is almost always mediated through human instruments.72 Both direct and indirect divine violence amount to the same thing; “it is always a question of human power interpreted as God’s action.”73 Direct heavenly intervention is rare, though it does happen (and presumably here natural calamities are being attributed to God).74 Much the same applies to those texts which speak of self punishment. The penalty may be conceptualized as the inherent con sequence of a wrongful deed boomeranging back on the doer, but in practice the penalty might still be inflicted by another person or be initiated by God.75 While violence, then, is explicitly ascribed to God in the biblical text, it is almost always committed by human beings. Texts on Yahweh’s violence normally refer to human deeds that are thought in some way to be related to God’s will, so that we may safely assume that “human violence is meant when there is talk of divine anger and retribution.”76&lt;/p&gt;

&lt;p&gt;But why is it that human violence is so naturally identified with the action of God? Why is God conceived as the author of so much carnage? Why are harmful human experiences viewed as the punishments of God? Part of the answer lies in the desire to affirm God’s transcendent immanence in all of historical experience, and part of it lies in the corrupting impact of violence itself. Such is the intrinsic nature of violence that once unleashed it changes everything, including humanity’s experience of God. In the Genesis story God is initially portrayed as one who resists and opposes violence, albeit without success. But as violence grows and spreads God is driven to counter-violence. Once this step is taken however, God is enmeshed in the very problem that needs addressing.77&lt;/p&gt;

&lt;p&gt;Redemptive violence takes on an irresistible logic of its own, that even the knowledge of God falls victim to it. It is almost as if it is imagined that God compromises with violence, God ends up being compromised. Consistently in the Bible the experience of violence changes people’s identity, the way individuals are perceived and known and relate to one another. The same happens to God’s identity, as the peaceable God of creation is inexorably defined in militant and aggressive ways. Counter-themes of love and mercy and forgiveness and restraint are always present, but the larger conceptual framework fundamentally remains one of redemptive violence.&lt;/p&gt;

&lt;p&gt;Once conceived as violent, God’s only option is to tarry with humanity in its misconceptions to win redemption by other means. God is forced to accommodate revelation to the limitations of human perception, not least so that the ultimate futility of redemptive violence – already intimated in Genesis 8:21 – might become apparent. 79 In the process horrendous acts of violence are attributed, or misattributed, to God. But God’s apparent capture by the categories of sacred violence becomes the precondition for exposing violence for what it is – an enslaving and self-perpetuating deception that contaminates all that it touches, including knowledge of God. With the coming of Jesus however, God finally casts off the illusions surrounding righteous violence to disclose “the plan of the mystery hidden for ages in God who created all things” (Eph. 3.9) – to reconcile all things by an act of self-giving, nonviolent, victorious love. Significantly this requires God-in-Christ personally to fall victim to divinely sanctioned lethal violence, making it plain once and for all that God-authored violence is a falsity, that “whenever sacred violence is mentioned, it is always human beings attacking one another.” 82&lt;/p&gt;

&lt;p&gt;It is God’s definitive renunciation of redemptive violence in the life, death, and resurrection of Christ that requires us to re-read the earlier biblical narratives of divine violence in such a way that God can no longer be seen as the ultimate author of the cruelty and killing they record. Certainly these bloody narratives attest to God’s providential presence amid human degradation. But insofar as they ascribe to God responsibility for acts of barbarism, they attest only to the veil of violence through which the experience of God has been filtered since the days of Cain. In Christ, however, the veil is taken away and God stands fully revealed.&lt;/p&gt;

&lt;p&gt;I am suggesting then that the biblical accounts of divine violence are both true to how God was experienced following the entry of sin, yet ultimately untrue to God’s real character. In this respect they are hermeneutically complex. It could be objected that allowing for such misrepresentation of God’s initiative in violence by the biblical authors undermines the authority and trustworthiness of the scriptural text. But this is not necessarily so. The text remains trustworthy in that it reliably discloses how God has been apprehended in history and how God perseveres with human fallibility. Those passages that ascribe violence to God should not be censored or sanitized or discarded; instead they should be read with a “critical charity” that embraces them, for all their gruesomeness, as a gift of God to aid in our instruction and formation. But neither should such passages be absolutized as an unassailable revelation of God’s true being; that role belongs to Christ alone.&lt;/p&gt;

&lt;p&gt;Objections might also be raised to the notion of God being “captured” by the mythology of violence, of God allowing the divine identity to be clouded by images of vengeance and viciousness. But this phenomenon can be seen as testimony to the seriousness of the cognitive and moral distortions created by the entry of sin and the irruption of violence (cf. Rom. 1:18-23). Also there are precedents within Scripture itself for questioning whether established understandings of God’s behavior are truly consonant with the character of God. Jeremiah, for example, despite repeated affirmations in sacred tradition that God employs the practice of collective punishment, extending down through many generations,85 looks forward to the day when this will no longer be so,86 and Ezekiel is rebuked by God for failing to see that the principle no longer applies.87&lt;/p&gt;

&lt;p&gt;An even more instructive analogy is the way in which the apostle Paul rethinks the role of the law in salvation. Paul faces a hermeneutical dilemma similar to the one explored above. He discovered that when God played his trump card in Jesus Christ, it looked disconcertingly different from what God’s hand had hitherto looked like. Until that point, God had insisted that obedience to the Torah was the in dispensable source of righteousness for the chosen people and the ground of hope for Israel’s redemption. The law was central to God’s saving purposes. But then something unexpected happened. When God finally intervened to secure salvation for Israel, it took place “apart from the law,”88 in some respects even contrary to the law.&lt;/p&gt;

&lt;p&gt;Paul was therefore forced to think his way through how and why God’s familiar ways in the past had altered so dramatically. The zealous young Pharisee, who himself had been prepared to use righteous violence to defend and uphold the law, 89 was compelled to read the Scriptures afresh, to interpret them from a new perspective, to discern in them a new understanding of God hitherto hidden from sight but now revealed through his son. In doing so Paul suggests a relation ship between God, sin, death, and the law that parallels, in broad outline, the relationship between God and violence I have sketched above.&lt;/p&gt;

&lt;h2 id="pauls-treatment-of-the-law-as-a-hermeneutical-precedent"&gt;PAUL’S TREATMENT OF THE LAW AS A HERMENEUTICAL PRECEDENT&lt;/h2&gt;

&lt;p&gt;The meaning and self-consistency of Paul’s extensive reflections on the place of the Mosaic law in the Christian era is a storm center in current Pauline scholarship. Over the last twenty-five years, the so called “New Perspective on Paul” has thrown up a series of objections to the traditional or “Lutheran” understanding that has dominated the interpretation of Paul since the time of the Reformation. Yet the New Perspective is itself coming under sustained criticism, with some scholars claiming that it lacks the theological depth and exegetical precision of the traditional model. No consensus has yet emerged, although there is a growing feeling that some mediating position is required that combines the insights of both older and the newer perspectives. There is no room here to explore this further. Suffice it to say that any satisfactory explanation of Paul’s critique of the law must, in my opinion, do justice to three main realities: the diversity of views on the Torah in first-century Judaism, Paul’s radical commitment to Gentile inclusion in the messianic community, and the apostle’s darkly pessimistic view of the human condition. All three inform and shape his reconsideration of the role of the law in God’s purposes, now that the Messiah has appeared.&lt;sup id="fnref:rephrase"&gt;&lt;a class="footnote" href="#fn:rephrase" rel="footnote"&gt;16&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;It is noteworthy that Paul approaches the issue within a narrative framework. That is to say, he reads Scripture primarily as a story, with an overarching plot, a set of characters, and a forward-reaching momentum that climaxes in the life, death, and resurrection of Christ. Paul recognizes that there are different ways of interpreting this story. As a Pharisee, he read the biblical story through the lens of the Torah, with Moses being the key actor in the drama and Israel’s holiness being its central concern. Now as a Christian, Paul has learned to read the story through the lens of Jesus Christ, with Abraham being the key figure and the eschatological uniting of Jew and Gentile in a new covenant community being its true message. Of course, for Paul, these two readings of the biblical narrative are not equally valid. The former is the product of viewing Scripture through a “veil” of ignorance, of lacking true enlightenment about the real import of God’s saving righteousness. Only in Christ is this veil removed; only in him do the lights come on. Only then does the true meaning of God’s activity in preceding history come clearly into view.&lt;/p&gt;

&lt;p&gt;From this hermeneutical vantage point, Paul is able to see the Torah in a completely new light. Once he viewed the law as God’s answer to Adam’s sin, the gracious means by which Israel could recover humanity’s true role in the world and secure admission to the world to come. Now, however, Paul considers the law to be part of the problem, not part of the solution. Paul faults the law on three main counts. First, the law has proven powerless to free God’s people from the grip of sin. Far from controlling sin, as God intended, the law actually makes the situation worse, because it simultaneously highlights Israel’s accountability to God’s requirements (Rom. 2:12; 3:19) and her impotence to achieve genuine righteousness no matter how sincerely she strives for it (9:31, cf. 2:17-29). Consequently the law cannot deliver the hoped for vindication; on the contrary it brings “the knowledge of sin” (3:20; 5:13; 7:7), the weight of God’s wrath (4:15), and the inevitability of God’s curse (Gal. 3:10). More than that, it actually exacerbates human sinfulness. The law functions to stir up the very passions it condemns (Rom. 7:5), so that the coming of the law resulted in trespasses being multiplied rather than being reduced (5:20).&lt;/p&gt;

&lt;p&gt;The reason for this sorry state of affairs is that the law, for all its divine qualities, is unable to overcome the indwelling and all-pervasive power of sin (3:9-18; 7:14-25; Gal. 3:22). The law is fatally “weakened by the flesh” (Rom. 8:3; 7:14, 25); it is stymied by that fallen human condition that “does not submit to God’s law, indeed it cannot” (8:7). The law has even become a weapon in the hands of sin (7:7-13), so much so that Paul can boldly declare that “the power of sin is the law” (1 Cor. 15:56, cf. Rom. 7:22-23). What God intended for life has become a vehicle of death. The source of the problem is not the law itself, which Paul considers to be “holy, just and good” (Rom. 7:12, 14, 16). The problem is the deep-rootedness of sin, which lies beyond the reach of any external legal code, even one given by God.&lt;/p&gt;

&lt;p&gt;The second problem Paul finds with the law is that it is limited to one ethnic community. Notwithstanding the ironical tone of his comments, Paul accepts the premise that the law was given uniquely to Israel to enable her to be “a guide to the blind, a light to those who are in darkness, a corrector of the foolish, a teacher of children, having in the law the embodiment of knowledge and truth” (Rom 2:19-20). But this mission has been subverted by Israel’s failure to practice what she preaches, so much so that Paul can even charge the covenant people with causing God’s name to be blasphemed among the Gentiles (2:24). What Israel’s track record proves, Paul argues, is that sin is no respecter of ethnic boundaries. Nor, conversely, is the capacity to perceive and obey God’s will. Paul believes that it is quite possible for uncircumcised Gentiles, who do not possess the Torah, “instinctively to do what the law requires” since “what the law requires is written on their hearts, to which their own conscience bears witness” (2:14-15, 27).&lt;/p&gt;

&lt;p&gt;So law and righteousness are not co-extensive realities; there is no necessary overlap. If there were, justification could legitimately come through the law and Gentiles would need to become observant Jews to appropriate it. But Israel’s own servitude to sin proves that this is not the case. In any event, even if it were the case, an additional problem would arise, for God would then be reduced to the status of a tribal deity rather than universal lord, and God’s promise to bless all the nations of the world through Abraham would be null and void (4:9-16; Gal. 3:15-22). If the unity, sovereignty, and justice of God – to which the law itself bears witness – are to be vouchsafed, justification must come by some means other than the law. It must potentially be open to every member of the human family.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For we hold that a person is justified by faith apart from works prescribed by the law. Or is God the God of Jews only? Is he not the God of Gentiles also? Yes, of Gentiles also, since God is one; and he will justify the circumcised on the ground of faith and the uncircumcised through that same faith. Do we then overthrow the law by this faith? By no means! On the contrary, we uphold the law. (Rom. 3:28-31, cf. 10:11-13)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The third problem Paul has with the law is that an overriding emphasis on law-keeping shifts the spotlight from God’s empowering grace to autonomous human achievement, Paul is adamant that justification cannot come from reliance on the works of the law. Now it is true that by this phrase Paul most likely has in mind ceremonial practices such as circumcision, food laws, purity regulations, and Sabbath observance which served visibly to demarcate the boundary between the law-keeping covenant community and the Gentile world. He is not thinking of legalism in a moral sense but of proud reliance on badges of ethnic distinction. He is not accusing Judaism of advocating an individualistic works-righteousness whereby people can earn their own salvation by merit. He is more likely targeting some conception of national-righteousness, whereby adoption of certain Jewish distinctives was thought essential to securing final vindication. Even so, Paul still recognizes the potential for a law-centered spirituality to over-emphasize human capability and to underestimate the insidiousness of sin and the necessity of grace.&lt;/p&gt;

&lt;p&gt;Viewed from the perspective of law-keeping, sin tends to get equated with a set of external behaviors that can be avoided with sufficient vigilance, rather than being recognized for what it really is – a deadly poison that permeates the entire life-system of humanity, a cosmic power that enslaves all humanity by habituating everyone to self-centeredness and idolatry.93 A certain kind of complacency can therefore emerge that is so assured of its own sincerity that it underrates the unmerited nature of grace (cf. Phil. 3:4-16). This is partly why Paul sets law and grace in such stark opposition.&lt;/p&gt;

&lt;p&gt;The only solution to this predicament, Paul argues, is the rupturing of sin’s lordship and the radical renewal of human nature from the inside out so that God’s law is written on the human heart by means of the indwelling of the Spirit. This is exactly what Christ achieves through his death and resurrection on behalf of all humanity, as a kind of second Adam.95 Those who participate through baptism in Christ’s victory (Rom. 6:1-14) are empowered by the eschatological Spirit to fulfil the true intention of God’s law (8:1-4). That intention is a life of freedom wholly devoted to the love of God and the love of neighbor, including even the love of enemies (12:9-21). Paul saw his own life experience as proof of this. Once supremely zealous for God’s law, he perpetrated violence on God’s behalf. Now graciously freed from the grip of sin through faith in Christ, he proclaims the peace of God, even taking up the cause of his former opponents in the interests of universal reconciliation.&lt;/p&gt;

&lt;p&gt;In light of this analysis, Paul concludes that the regime of law was only ever intended by God as a temporary holding measure, until Christ should come (Gal. 3:19-26). It served negatively to imprison all under sin (Gal. 3:22; Rom. 10:32) in preparation for the great liberation which Christ would win through his vicarious death and resurrection. Those united with Christ in this way are no longer “under law but under grace” (6:15). They have “died to the law” (7:4; Gal. 2:19), they are “discharged from the law” (7:6), they are “freed” and “redeemed” from the law (Gal. 3:13; 4:5;5:1), they are “no longer subject” to the law’s rule (Gal. 3:25) or its condemnation (Rom. 8:1). “For Christ is the end-goal [&lt;em&gt;telos&lt;/em&gt;] of the law” (10:5). Things have changed!&lt;/p&gt;

&lt;p&gt;In none of this, of course, is Paul wrestling with the problem of divine violence (he is actually struggling to make sense of the prodigiousness of God’s mercy!) But there are several features of Paul’s approach to the law that constitute a kind of hermeneutical template for the type of approach to divine violence I have advocated above. To begin with, in both cases, the presenting problem is a substantial shift in God’s &lt;em&gt;modus operandi&lt;/em&gt; from what was apparent in previous tradition. Paul openly acknowledges that God’s methods have changed. This is implicit in the ringing adversative declaration “But now, apart from law…” that commences his account of the revelation of saving justice in Christ in Romans 3:21-26. It is also evident in the way he divides Israel’s story into sequential phases (Adam to Abraham, Abraham to Moses, Moses to Christ), with the law entering 430 years after the promise to Abraham and serving as a disciplinarian “until Christ came,” after which time “we are no longer subject to a disciplinarian” (Gal. 3:15-26). Paul thus allows for real change in God’s ways.&lt;/p&gt;

&lt;p&gt;The sheer radicalism of this change ought not to be underestimated. It required for Paul a thoroughgoing revision of understanding of what had hitherto been the most fundamental ingredients of covenantal faithfulness-circumcision, Sabbath observance, food laws, and separation from all sources of impurity, including Gentiles. That the apostle can boldly declare that “I know and am persuaded in the Lord Jesus that nothing is unclean in itself” (Rom. 14:14) and that “neither circumcision nor uncircumcision is anything but a new creation is everything” (Gal. 6:15), is a measure of the extent to which he reckoned on a major revolution having occurred in God’s way of working.&lt;/p&gt;

&lt;p&gt;Second, in problematizing the law Paul seeks an explanation that both accords with the plot of the canonical story and explains why God appears to act differently than previously thought, which is exactly what I have said is needed to deal with the issue of divine violence. Paul accepts that radical changes have taken place in God’s priorities, but he is equally emphatic that there is a profound consistency between the realities of the present and the revelation of the past. The “new creation” afforded in Christ is not arbitrary or capricious; it accords with the deepest themes of the previous story. It is both “at tested by the law and the prophets” (Rom. 3:21, 31) and compatible with God’s own being (3:29). That authentic righteousness can come “apart from the law,” Paul argues, is already evident in the story of Abraham as a foreshadowing of what was to come (4:1-25), and it is only by transcending the limitations of Mosaic law that the promise of universal blessing to Abraham can possibly be realized (Gal. 3:6-17).&lt;/p&gt;

&lt;p&gt;The negative role of the law is thus balanced by its positive role in anticipating and illuminating eschatological events.” Even Moses can now be heard to speak of “the word of faith that we proclaim” (Rom 10:8): It is the fulfillment of eschatological hope in Christ and the Spirit that accounts for why God’s activity now seems so different. It is also what impels Paul to read the canonical story afresh to discern in it a meaning and dynamic once hidden from view. A similar strategy is needed, I have suggested, with respect to the narratives of divine violence.&lt;/p&gt;

&lt;p&gt;Finally, and most tellingly, Paul does not shrink from depicting God’s law as having been captured by the power of sin and becoming a source of enslavement and death. The law itself, Paul is clear, is imbued with the very attributes of God; it is “holy, just, and good,” it is even “spiritual” (Rom. 7:12, 14, 16). But regardless of its divine credentials the law has been bamboozled by sin. Seizing the opportunity created by the revelation of God’s will, sin commandeered the law to deceive and kill its adherents (7:11). Consequently, despite its intrinsic goodness, and despite its promise of life to those who observe its commandments, the law has in effect functioned to lock in, as it were, Israel’s servitude to sin and death.&lt;/p&gt;

&lt;p&gt;This is an incredibly daring claim for Paul to make! He asserts that not only has Gentile humanity’s knowledge of God been corrupted by the tyranny of sin (1:18-23, cf. 1 Cor 1:20-25), even Israel’s apprehension of God’s intentions in the law has fallen victim to sin’s death-dealing deception. It is only by taking this insight with full seriousness that we can begin to make sense of the attribution of violence to God in the biblical record. Arguably part of sin’s deception is the identification of human violence with the will of God. Jesus Christ explodes this deception, and in him a new humanity emerges that is now in the process of “being renewed in knowledge according to the image of its Creator” (Col. 3:10).&lt;/p&gt;

&lt;p&gt;In these ways, then, Paul’s grappling with the ambiguities of the law in salvation history furnishes something of a hermeneutical analogy for the approach to divine violence sketched out in this chapter. To be absolutely clear: I am not suggesting that Paul’s ruminations on the law in themselves relate to the issue of God’s recorded violence, only that he models a style of hermeneutical engagement with Scripture that can be usefully reapplied to this question. Whether the biblical stories of violence caused any discomfort for the apostle is hard to say. Certainly he never explicitly disavows a violent God. If anything, the real scandal for Paul was not the violent exclusivity of God’s past actions but the gratuitous inclusiveness of God’s saving activity in the present. When Paul takes refuge in the inscrutability of God’s ways it is not to justify divine violence but to underscore the mystery of God’s indefatigable mercy, a mercy displayed equally toward disobedient Israel and toward wider Gentile humanity (Rom. 11:28-36).&lt;/p&gt;

&lt;p&gt;Paul may never have consciously reflected on how best to reconcile this present experience of God’s mercy with past episodes of grotesque violence. But it was his apprehension of the crucified and risen Christ as the “image of the invisible God” (Col. 1:15) that trans formed Paul from a violent religious zealot into a peacemaker extraordinaire. It was this that led him to speak distinctively of God as “the God of peace, “100 the God who has reconciled the entire world to God’s self not by violent conquest but by self-giving sacrifice. This crucified God is the same God who made the world and everything in it, the same God who called Abraham and chose Israel, the same God that Jesus called Father, and the same God to whom Jesus pointed his followers as the supreme paradigm for the way of nonviolent discipleship.&lt;/p&gt;

&lt;h2 id="endnotes"&gt;ENDNOTES&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; As Dale C. Allison Jr. notes,”… the problem of conflicting theologies was not born with Christianity. The problem was already internal to Judaism …. If, after Marcion, the issue for Christians became which God to a edge, this was only a later variant of the earlier question, Which texts should we sanction?” “Rejecting Violent Judgment: Luke 9:52-56 and its &lt;em&gt;Journal of Biblical Literature&lt;/em&gt; 121/3 (2002): 459-78 (at 478).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; See J. Denny Weaver, &lt;em&gt;The Nonviolent Atonement&lt;/em&gt; (Grand Rapids, Mich: Wm B. Eerdmans, 2001). I am in general sympathy with the direction of Weaver’s argument, though I depart substantially from his reading of the New Testament evidence. See my “Atonement, Violence, and the Will of God,” Mennonite Quarterly Review 76/1 (2003):67-90.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; See for instance. Miroslav Volf, &lt;em&gt;Exclusion and Embrace: A Theological Ex planation of Identity, Otherness, and Reconciliation&lt;/em&gt; (Nashville: Abingdon Press, 1996), esp. 275-306. Cf. also Kenneth R. Chase, “Christian Discourse and the Humility of Peace,” in &lt;em&gt;Must Christianity Be Violent? Reflections on History, Practice, and Theology&lt;/em&gt;, ed. Kenneth R. Chase and Alan Jacobs (Grand Rapids, Mich.: Brazos Press, 2003), 119-34 (esp. 128-31); Dan McKanan, “Is God Violent? Theological Options in the Antislavery Movement,” in &lt;em&gt;Must Christianity Be Violent?&lt;/em&gt;, 50-68&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; Walter Wink, &lt;em&gt;Engaging the Powers: Discernment and Resistance in a World of Domination&lt;/em&gt; (Minneapolis: Fortress Press, 1992), 1-31; idem, The Powers That Be Theology for a New Millennium (New York: Doubleday, 1998), 63-81. 7. Heb 10:31&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; Heb 10:31&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11.&lt;/strong&gt; Col. 1:15, 19, cf. 2 Cor. 4:4&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14.&lt;/strong&gt; Col. 1:16&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15.&lt;/strong&gt; N.T. Wright offers a powerful presentation of Jesus’ nonviolent opposition to Rome in &lt;em&gt;Jesus and the Victory of God&lt;/em&gt; (London: SPCK, 1996). But he stops short of grounding it in any distinctive apprehension of God’s character by Jesus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;16.&lt;/strong&gt; Matt. 5:44-48&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;23.&lt;/strong&gt; John C. Haughey, “Jesus as the Justice of God,” in &lt;em&gt;The Faith that Does Justice: Examining the Christian Sources for Social Change&lt;/em&gt;, ed. J.C. Haughey (New York: Paulist Press, 1922), 279. See also Marshall, &lt;em&gt;Beyond Retribution&lt;/em&gt;, 259-63.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;24.&lt;/strong&gt; Col. 1:16-17&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;26.&lt;/strong&gt; Gen. 4:8; Rev. 21:3-4,24; 22:2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;28.&lt;/strong&gt; Gen. 4:1-16&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;29.&lt;/strong&gt; Gen. 4:23-24;65-7, 11-13&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;30.&lt;/strong&gt; Gen. 7:1-24;8:21-22:9:11-17&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;31.&lt;/strong&gt; This is not to deny that the canonical tradition is full of tensions, disagreements, contradictions, and revisions. Later biblical authors and tradents felt free to disagree with the perspective of their predecessors on certain theological positions even while accepting the authority of the tradition they bequeathed. Indeed, the authority of the tradition is shown precisely in the commitment of later recipients to engage in radical rethinking of its meaning and implications. See further Ellen F. Davis, “Critical Traditioning: Seeking an Inner Biblical Hermeneutic,” &lt;em&gt;Anglican Theological Review&lt;/em&gt; 82/4 (2000): 733 51.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;32.&lt;/strong&gt; Of course, legendary elements may well exist in the tradition. As Simon De Vries notes, “Battle and war passages in the Old Testament range from the legendary to the mythical, to the realistic and immediate, from the schematic and ideological, to the bizarre and apocalyptic.” – “Human Sacrifice in the Old Testament: in Ritual and Warfare,” in &lt;em&gt;Destructive Power of Religion&lt;/em&gt;, ed. Ellens, 1:99-121 (at 120).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;33.&lt;/strong&gt; Exod. 15:1-3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;34.&lt;/strong&gt; Schwager, &lt;em&gt;Must There be Scapegoats?&lt;/em&gt;, 47-71.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;35.&lt;/strong&gt; Jack Miles, “The Disarmament of God,” in &lt;em&gt;Destructive Power of Religion&lt;/em&gt; ed Elkins, 1:123-167 (at 147).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;36.&lt;/strong&gt; Num. 11:1-6; 14:10-12; Deut. 1:34-40&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;37.&lt;/strong&gt; Num. 12:1-16; 14:10-12; 16:41-50, 25:6-9, cf. Exod. 11:4-5; 12:12,13, 23, 37&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;38.&lt;/strong&gt; Mark McEntire, &lt;em&gt;The Blood of Abel: The Violent Plot of the Hebrew Bible&lt;/em&gt; (Macon, Georgia: Mercer University Press, 1999), 61-62.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;39.&lt;/strong&gt; Relevant texts include Exod. 17:8-13; Num. 21:1-3, 23-24; Deut. 2:30-35; 7:2-6; Josh. 6: 1-16,7:1, 24-26a; Judg. 3:16-25; 4:6-7,9-10, 13-15, 17, 21-22; 15:4 8,1 Sam. 17:12-18:2; 31:1-13; 2 Sam. 18-6-9, 14-15; 1 Kings 22:31-38; 2 Kings 9:30-35; Isa. 2:4; Mic. 4:3; Ezek. 38:14-23; Joel 3:9-10; 2 Chron. 149-15&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;40.&lt;/strong&gt; For example, Deut. 7:2: Josh. 11:20.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;41.&lt;/strong&gt; Susan Niditch, &lt;em&gt;War in the Hebrew Bible: A Study in the Ethics of Violence&lt;/em&gt; (New York &amp;amp; Oxford: Oxford University Press, 1993), 28-89. See also John J. Collins, “The Zeal of Phineas, the Bible and the Legitimation of Violence,” in &lt;em&gt;Destructive Power of Religion&lt;/em&gt; 1: 12-33, ed. Ellens (at 13-17).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;42.&lt;/strong&gt; McDonald, &lt;em&gt;God and Violence&lt;/em&gt;, 123, 126, cf. 127, 128, 131.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;43.&lt;/strong&gt; Niditch, &lt;em&gt;War in the Hebrew Bible&lt;/em&gt;, 50. Collins is unconvinced. “Rather than respect for human life, the practice bespeaks a totalistic attitude, which is common to armies and warfare, where the individual is completely subordinated to the interests of the group.” – “Zeal of Phineas,”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;44.&lt;/strong&gt; Nelson-Pallmeyer, &lt;em&gt;Jesus Against Christianity&lt;/em&gt;, 37,&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;45.&lt;/strong&gt; McEntire, &lt;em&gt;Blood of Abel&lt;/em&gt;, 90.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;46.&lt;/strong&gt; Ps. 68:20-23, cf. Habakkuk 3:13-16,&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;47.&lt;/strong&gt; Amos 2:4-16; 5:127; Isa. 43:28. See Niditch, &lt;em&gt;War in the Hebrew Bible&lt;/em&gt;, 78.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;48.&lt;/strong&gt; McEntire, Blood of Abel, 114.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;49.&lt;/strong&gt; See my &lt;em&gt;Crowned with Glory and Honor: Human Rights in the Biblical Tradition&lt;/em&gt; (Telford/Scottdale, Pa.: Pandora Press U.S./Herald Press, 2001).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;50.&lt;/strong&gt; McEntire, &lt;em&gt;Blood of Abel&lt;/em&gt;, 44.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;51.&lt;/strong&gt; Gen. 18:16-33&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;52.&lt;/strong&gt; In &lt;em&gt;Violence, Hospitality, and the Cross: Reappropriating the Atonement Tradition&lt;/em&gt; (Grand Rapids Mich.: Baker Academic, 2004), Hans Boersma argues that in a sinful world violence is necessary to defend the boundaries which enable hospitality to function. In such a world, God employs “redemptive” violence, which is a “good violence” because it serves to uphold monotheism, to punish immorality, and to protect the poor and underprivileged. But Boersma makes little attempt to measure this sweeping defense of divine violence against the concrete suffering endured by its victims. See my critical review in &lt;em&gt;Stimulus&lt;/em&gt; 13/3 (2005), 51-52, which includes my own attempt to spell out the semantic parameters of the term &lt;em&gt;violence&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;53.&lt;/strong&gt; For example, Gen. 13:13; 15:16; Deut. 9:5; 20:17-18. The general perfidy of the Canaanites is pervasive in the biblical tradition, although the purported wickedness of the Canaanites is never actually substantiated within the conquest accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;54.&lt;/strong&gt; McEntire, &lt;em&gt;Blood of Abel&lt;/em&gt;, 118&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;55.&lt;/strong&gt; Matt. 2:16-18&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;56.&lt;/strong&gt; 1 Sam. 15:2-3, cf. Exod. 17:14,16; Num. 24:20; Deut. 25:17-19&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;57.&lt;/strong&gt; So Collins, “Zeal of Phineas,” 24-25.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;58.&lt;/strong&gt; So Anderson, “Genocide or Jesus,” 51.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;59.&lt;/strong&gt; Nelson-Pallmeyer, &lt;em&gt;Jesus Against Christianity&lt;/em&gt;, 16.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;60.&lt;/strong&gt; Collins, “Zeal of Phineas,” 25.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;61.&lt;/strong&gt; Exod. 34:6-7; Num. 14:18-19, Deut. 5:9-10&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;62.&lt;/strong&gt; 2 Kings 1:11-12, cf. Gen. 19:24; Lev. 10:1-2; Job 1:16; Ps. 97:3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;63.&lt;/strong&gt; Luke 9:54-55&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;64.&lt;/strong&gt; So rightly Allison, “Rejecting Violent Judgment,” 476.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;65.&lt;/strong&gt; McDonald, God and Violence, 54, cf. 57.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;66.&lt;/strong&gt; Gen. 12:3, 17; 14:20; 15:14-16; 18:20; 19:15-29; 22:1-19;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;67.&lt;/strong&gt; Gen. 12:7; 13:14-17; 15:16; 17:8; 22:17; 28:3-4, 13-15; 35:12; 48:4;50:24&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;68.&lt;/strong&gt; For example, Exod. 4:24-26; 2 Sam. 6:6-7. Ezek. 38:10&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;69.&lt;/strong&gt; See Lev. 26: 14-39; Exod. 12:29; Ezek, 21:3-4, 9-15; Jer. 25:32-33.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;70.&lt;/strong&gt; For example, Deut. 20:16-17; 1 Sam. 15:2-3; Isa. 19:2; Jer. 51:20-24; 21:31; Ps. 44:11-12; Zech. 8:10.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;71.&lt;/strong&gt; For example, Isa. 50:11; Jer. 44:8; Ps. 7:13-17; Prov. 8:36; 26:27&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;72.&lt;/strong&gt; See, 22:25-26; Isa. 19:2; 13:17 for example, Ezek. 21:31; Jer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;73.&lt;/strong&gt; Schwager, &lt;em&gt;Must There Be Scapegoats?&lt;/em&gt;, 63.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;74.&lt;/strong&gt; For example, Gen. 19:24; Num. 16:29-32.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;75.&lt;/strong&gt; See, for example, Isa. 64:6-7; Ps. 81:11-12.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;76.&lt;/strong&gt; Schwager, &lt;em&gt;Must There Be Scapegoats?&lt;/em&gt;, 63 (emphasis mine).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;77.&lt;/strong&gt; Whether some actual primeval act of violence by God is the reason for God’s subsequent identification with violence, or whether the flood story it self is the product of prior projection of violence onto heaven, is open to discussion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;78.&lt;/strong&gt; This is one of the major findings in McEntire’s study &lt;em&gt;Blood of Abel&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;79.&lt;/strong&gt; Cf. Wink, Engaging the Powers, 146-47.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;80.&lt;/strong&gt; God’s sovereign self-disclosure is the necessary presupposition for all and any knowledge of God. But divine revelation is also necessarily filtered through fallible human language and categories, which can distort as well as report God’s truth, and often both at the same time. For a helpful discussion of the relationship between revelation and Scripture, see William C. Placher &lt;em&gt;The Domestication of Transcendence&lt;/em&gt; (Louisville, Ky: Westminster John Knox, 1996), 181-200.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;81.&lt;/strong&gt; Jesus is condemned to death on the charge of blasphemy (Mark 14-55 64), a capital crime in Mosaic law (Lev. 24:15-16).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;82.&lt;/strong&gt; Schwager, &lt;em&gt;Must There Be Scapegoats?&lt;/em&gt;, 67.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;83.&lt;/strong&gt; Collins is clear that what makes violent religious texts dangerous is not their violent content but the certitude with which they are received by readers as divine revelation, “Zeal of Phineas,” 23-26. So too Nelson-Pallmeyer, &lt;em&gt;Jesus Against Christianity&lt;/em&gt;, 277,&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;84.&lt;/strong&gt; 1 borrow this term from Ellen Davis, “Critical Traditioning.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;85.&lt;/strong&gt; Exod. 34:6-7; Num. 14:18-19; Deut. 5:9-10&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;86.&lt;/strong&gt; Jer. 31:29-30&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;87.&lt;/strong&gt; Ezek. 18:1-4&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;88.&lt;/strong&gt; Rom. 3:21; 8:1-4&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;89.&lt;/strong&gt; Gal. 1:13; Phil. 3:6; 1 Cor. 15:9; cf. Acts 9:1-2; 22:4; 26:9-11. On the violent implications of “zeal,” see Terence L. Donaldson, “Zealot and Convert: The Origins of Paul’s Christ-Torah Antithesis,” &lt;em&gt;Catholic Biblical Quarterly&lt;/em&gt; 51/4 (1989): 655-82; Vincent M. Smiles, “The Concept of ‘Zeal’ in Second-Temple Judaism and Paul’s Critique of It in Romans 10:2,” &lt;em&gt;Catholic Biblical Quarterly&lt;/em&gt; 64/2 (2002): 282-99; Mark R. Fairchild, “Paul’s Pre-Christian Zealot Associations: A Re-Examination of Gal 1:14 and Acts 22:3,” &lt;em&gt;New Testament Studies&lt;/em&gt; 45/4 (1999): 514-32. For a thematizing of the place of zeal in the biblical tradition and American self-understanding, see Robert Jewett and John Shelton Lawrence, &lt;em&gt;Captain America and the Crusade Against Evil: The Dilemma of Zealous Nationalism&lt;/em&gt; (Grand Rapids, Mich: Wm B. Eerdmans, 2003).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;90.&lt;/strong&gt; See 2 Cor. 3:12-18; Rom. 10:2-4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;91.&lt;/strong&gt; Rom. 7:5,9-10; 10:5; Gal. 2:19, cf. 3:12, 21; 1 Cor. 15:56; 2 Cor. 3:6-7&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;92.&lt;/strong&gt; Rom. 3:20, 27-28; Gal. 2:16; 32,5,10, 12, cf. Rom. 3:27:4:2,6;9:12, 32; 11:6; Eph. 29&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;93.&lt;/strong&gt; Rom. 1:18-32; 3:9-20; 5:12-21; 612-14, 20-21;7:7-25; etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;94.&lt;/strong&gt; Rom. 4:16; 5:20-21; 6:1,14; 11:5; 1 Cor. 15:10; Gal. 1:6; 2:21; 5:4; cf. Eph. 2.5-6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;95.&lt;/strong&gt; Rom. 5:6-21; 1 Cor. 15:42-49; Col. 1:10; 3:10&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;96.&lt;/strong&gt; Rom. 5:5; 13:8-10; 14:15; Gal. 5:6, 13-14, 22-23; Phil. 1:9; 1 Cor. 8:1; 13:1-8; 16:14; Col. 3:14, cf. Eph. 4:32-35.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;97.&lt;/strong&gt; Gal. 1:13; Phil. 3:5-6; 1 Cor. 15:9; cf. Acts 9:1-2; 22:4; 26:5, 9-11.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;98.&lt;/strong&gt; See Rom. 11:28-36.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;99.&lt;/strong&gt; Cf. Rom. 4:23-24; 15:4; 1 Cor. 10:11; 9:10.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;100.&lt;/strong&gt; The phrase &lt;em&gt;God of peace&lt;/em&gt; is a favorite of Paul’s (Rom. 15:33; 16:20; 2 Cor. 13:11; Phil. 4:9; 1 Thess. 5:23; 2 Thess. 3:16, cf. Eph. 2:14), though he did not in vent it (Heb. 13:20). In early Jewish literature the phrase occurs only once (&lt;em&gt;Test. Daniel 5:2&lt;/em&gt;).&lt;/p&gt;

&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:1"&gt;
      &lt;p&gt;Paul N. Anderson, “Genocide or Jesus: A God of Conquest or Pacifism,” in &lt;em&gt;The Destructive Power of Religion: Violence in Judaism, Christianity, and Islam&lt;/em&gt;, ed. J. H. Ellens (Westport, Connecticut &amp;amp; London, Praeger 2004), 4:31-52 (at 31). &lt;a class="reversefootnote" href="#fnref:1"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:2"&gt;
      &lt;p&gt;See D. Andrew Kille, “The Bible Made Me Do It’: Text, Interpretation, and Violence,” in &lt;em&gt;Destructive Power of Religion&lt;/em&gt;, ed. Ellens, 1:55-73. See also Charles Mabee, “Reflections on Monotheism and Violence,” in &lt;em&gt;Destructive Power of Religion&lt;/em&gt;, ed. Ellens, 4:111-18. Cf. also Karen Armstrong, “Unholy Strictures,” &lt;em&gt;Guardian Weekly&lt;/em&gt; (Aug. 19-25, 2005): 13. &lt;a class="reversefootnote" href="#fnref:2"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:8"&gt;
      &lt;p&gt;I attempt to construct a non-retributive view of final judgment in my &lt;em&gt;Beyond Retribution: A New Testament Vision for Justice, Crime and Punishment&lt;/em&gt; (Grand Rapids, Mich: Wm B. Eerdmans, 2001). &lt;a class="reversefootnote" href="#fnref:8"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:9"&gt;
      &lt;p&gt;John H. Yoder, &lt;em&gt;The Politics of Jesus: Vicit Agnus Noster&lt;/em&gt; (Grand Rapids, Mich: Wm B. Eerdmans, 1994), 228-47. &lt;a class="reversefootnote" href="#fnref:9"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:10"&gt;
      &lt;p&gt;J. Harold Ellens, “Revenge, Justice and Hope: Laura Blumenfeld’s Journey,” in idem, &lt;em&gt;Destructive Power of Religion&lt;/em&gt; 4:227-235 (at 235). &lt;a class="reversefootnote" href="#fnref:10"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:12"&gt;
      &lt;p&gt;Heb. 1:2-3 &lt;a class="reversefootnote" href="#fnref:12"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:13"&gt;
      &lt;p&gt;John 1:3-4 &lt;a class="reversefootnote" href="#fnref:13"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:17"&gt;
      &lt;p&gt;Luke 6:35-36 &lt;a class="reversefootnote" href="#fnref:17"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:18"&gt;
      &lt;p&gt;Raymund Schwager, &lt;em&gt;Must There Be Scapegoats? Violence and Redemption in the Bible&lt;/em&gt; (New York: Harper &amp;amp; Row, Gracewing &amp;amp; Crossroad Publishing Company, 1987), 207. So too Wink, &lt;em&gt;Powers that Be&lt;/em&gt;, 89. Jack Nelson-Pallmeyer observes that “Jesus was not the first Jew to promote or use nonviolence when resisting injustice…. Jesus may have been the first, however, to specifically reject the violence of God as the foundation for nonviolent resistance. Rather than rooting nonviolence in the assurance of God’s ultimate and re deeming violence, Jesus saw nonviolent action as a faithful embodiment of a nonviolent God, that is, as reflective of the very Spirit that is God.” – &lt;em&gt;Jesus against Christianity: Reclaiming the Missing Jesus&lt;/em&gt; (Harrisburg, Penn.: Trinity Press International, 2001), 320. &lt;a class="reversefootnote" href="#fnref:18"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:19"&gt;
      &lt;p&gt;Cf. Ulrich Mauser, &lt;em&gt;The Gospel of Peace: A Scriptural Message for Today’s World&lt;/em&gt; (Louisville: Westminster John Knox, 1992), 183-84. &lt;a class="reversefootnote" href="#fnref:19"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:20"&gt;
      &lt;p&gt;Col. 1:15, 20 &lt;a class="reversefootnote" href="#fnref:20"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:21"&gt;
      &lt;p&gt;Rom. 1:16-17;3:20 &lt;a class="reversefootnote" href="#fnref:21"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:22"&gt;
      &lt;p&gt;Rom. 5:1 &lt;a class="reversefootnote" href="#fnref:22"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:25"&gt;
      &lt;p&gt;Eph. 1:10 &lt;a class="reversefootnote" href="#fnref:25"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:27"&gt;
      &lt;p&gt;Wink, &lt;em&gt;The Powers That Be&lt;/em&gt;, 44-48, at 46. Also Patricia M. McDonald, &lt;em&gt;God and Violence: Biblical Resources for Living in a Small World&lt;/em&gt; (Scottdale, Pa. and Waterloo, Ont.: Herald Press, 2004), 35-49. &lt;a class="reversefootnote" href="#fnref:27"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:rephrase"&gt;
      &lt;p&gt;Or, to slightly reformat the prior sentences:&lt;/p&gt;

      &lt;blockquote&gt;
        &lt;p&gt;Suffice it to say that any satisfactory explanation of Paul’s critique of the law must, in my opinion, do justice to three main realities:&lt;/p&gt;
        &lt;ol&gt;
          &lt;li&gt;the diversity of views on the Torah in first-century Judaism&lt;/li&gt;
          &lt;li&gt;Paul’s radical commitment to Gentile inclusion in the messianic community&lt;/li&gt;
          &lt;li&gt;the apostle’s darkly pessimistic view of the human condition.&lt;/li&gt;
        &lt;/ol&gt;

        &lt;p&gt;All three inform and shape his reconsideration of the role of the law in God’s purposes, now that the Messiah has appeared.&lt;/p&gt;
      &lt;/blockquote&gt;
      &lt;p&gt;&lt;a class="reversefootnote" href="#fnref:rephrase"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Sun, 08 Aug 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/the-violence-of-god-and-the-hermeneutics-of-paul</guid></item><item><title>Building Through Uncertainty</title><link>https://camhashemi.com/essays/building-through-uncertainty/</link><description>&lt;p&gt;Product-building is just not as easy as we think it is, both before and after it’s done.&lt;/p&gt;&lt;p&gt;Before we start, &lt;a href="https://camhashemi.com/posts/accurate-estimations" rel="noopener" target="_blank"&gt;it seems easier than it will be&lt;/a&gt;. Once we’re done, it seems easier than it was.&lt;/p&gt;&lt;h2 id="cognitive-biases"&gt;Cognitive Biases&lt;/h2&gt;&lt;p&gt;Whenever we build something, we face constraints:&lt;/p&gt;&lt;p&gt;We don&amp;rsquo;t know what problems to solve.And even if we knew that, we don&amp;rsquo;t know how to solve them.And even if we knew &lt;em&gt;that&lt;/em&gt;, we don&amp;rsquo;t know how well we can deliver those solutions.And even if we knew &lt;em&gt;&lt;strong&gt;that&lt;/strong&gt;&lt;/em&gt;, we don&amp;rsquo;t know how our users will receive them.&lt;/p&gt;&lt;p&gt;Each successive level of uncertainty is dependent on the previous level. Solving the wrong problem endangers the entire project; choosing the wrong solution endangers the implementation; and building a poor quality solution endangers its reception. The uncertainties don’t simply add together independently &amp;ndash; they multiply. And on top of it all, we have limited resources and time to figure everything out.&lt;/p&gt;&lt;p&gt;Yet in hindsight, we tend to discount the uncertainty and constraints of the past. You’ll hear this whenever someone says &amp;ldquo;we should have built Y, not X”, &amp;ldquo;whoever built X was stupid&amp;rdquo;, or &amp;ldquo;whoever built X was lazy.&amp;rdquo; But the truth is more often that &amp;ldquo;whoever built X faced constraints that I can&amp;rsquo;t see right now.&amp;rdquo; This discounting tendency is known as the hindsight bias.&lt;/p&gt;&lt;p&gt;This bias is usually accompanied by another prevalent bias: the fundamental attribution error. We tend to criticize &lt;em&gt;others’&lt;/em&gt; decisions as wrong, stupid, or lazy; whereas &lt;em&gt;our&lt;/em&gt; decisions are &amp;ldquo;the best we could do, given our constraints at the time&amp;rdquo;. We see our own challenges better than anyone else&amp;rsquo;s; similar to how we see all chores that we do, and not all the chores done by our roommates.&lt;/p&gt;&lt;p&gt;The consequences of these biases aren&amp;rsquo;t superficial: they’re harmful. For fear of &amp;ldquo;getting it wrong&amp;rdquo; in hindsight, we&amp;rsquo;re encouraged to demand full certainty and resources before starting a project. Since resources and certainty are always limited, this demand only wastes time and good will. These biases also encourage us to build everything we could possibly ever need, so that our future critics can&amp;rsquo;t call us lazy or stupid. But everything we could possibly need is usually much less than what we&amp;rsquo;ll actually need (and &lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it" rel="noopener" target="_blank"&gt;you aren't gonna need it&lt;/a&gt;), so this approach forces us to bear that extra weight unnecessarily. And lastly, these biases hurt team morale. People dislike even the most valid criticism, so critiques stemming from our own cognitive biases lead to the worst of defensiveness and endless counterfactual arguments..&lt;/p&gt;&lt;p&gt;An oversimplification of the point is that we&amp;rsquo;re unfair to our past selves. But our past selves aren’t completely off the hook&amp;hellip;&lt;/p&gt;&lt;h3 id="less-but-better"&gt;Less, but Better&lt;/h3&gt;&lt;p&gt;When we look at our past, we shouldn&amp;rsquo;t criticize ourselves for what we didn&amp;rsquo;t build, but for what we didn’t build well.&lt;/p&gt;&lt;p&gt;There’s an age-old tradeoff between scope, quality, and resources. But on many teams, resources are always constrained. We can&amp;rsquo;t simply double our team’s productivity overnight, and we can’t double the length of deadlines without significant cost. Therefore, we’re left to choose between scope and quality. &lt;strong&gt;Given that choice, the best approach is to build less, but better.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Why not build more, but of lower quality?&lt;/p&gt;&lt;p&gt;First, “less but better” conserves our limited resources. Poor quality doesn&amp;rsquo;t just take energy from today; it taxes our future as well. We have all these constraints and uncertainty, and poor quality just makes the problem worse. That could actually be a definition of poor quality: that which produces more constraints and uncertainty down the road.&lt;/p&gt;&lt;p&gt;Second, “less but better” helps us to iterate, reducing uncertainty over time. Building through uncertainty is like playing a game of dice with the future. We ship, and the future rolls its dice, resolving some uncertainty from before. With each roll of the dice, we learn which features were worth it and which weren&amp;rsquo;t. When the &amp;ldquo;worth it&amp;rdquo; features are built on solid ground, we&amp;rsquo;re capable of expanding on them before the next roll. And when the “not worth it” features have limited scope and high quality, they tax us very little going forward. On the contrary, high quality work (and at the very least, our learnings from it) can more easily be repurposed than low quality work.&lt;/p&gt;&lt;p&gt;And third, “less but better” maximizes our chances of capturing potential value. It’s so frustrating when we feel like we’ve found the right problem and chose the right solution, only to botch the implementation and get ambushed by an embarrassing release. Rather than gaining an iteration to collect information and reduce uncertainty, we lose an iteration and our resources get further constrained for the next roll of the dice.&lt;/p&gt;&lt;h3 id="time-based-design-and-probabilistic-decision-making"&gt;Time-Based Design and Probabilistic Decision Making&lt;/h3&gt;&lt;p&gt;All of the best product builders I’ve worked have a secret trick that make all of their design decisions better: they include &lt;strong&gt;time&lt;/strong&gt; as a variable. They make decisions that perform the best over time, adapt the best over time, and fail the best over time.&lt;/p&gt;&lt;p&gt;Naive builders go all-in on the single moment in front of them, and then get sent back to the drawing board when the dice doesn&amp;rsquo;t roll their way. They don&amp;rsquo;t account for the inevitable, unlucky rolls of the dice. Instead, they fight to their last breath to convince you that their solution is the most likely to be right, instead of asking themselves “and what will we do if I’m wrong?”&lt;/p&gt;&lt;p&gt;By contrast, skilled builders show how their decision can adapt to any roll of the dice. Like a chess master, each decision is evaluated several moves ahead. The best decisions are those that perform best across all future rolls of the dice. No matter how the dice rolls, it’s no surprise: just look at the contingency plan they included in their proposal.&lt;/p&gt;&lt;p&gt;And contingency plans are best when each step maintains optionality. As Jeff Bezos wrote, decisions can either open two-way doors or one-way doors. You can always walk back through a two-way door if you don&amp;rsquo;t like what you see. One-way doors aren&amp;rsquo;t so: you&amp;rsquo;re stuck with them and are forward to move forward. One-way doors aren’t always avoidable. But under uncertainty, two-way doors buy us the extra dice rolls we need to eventually find our way.&lt;/p&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Each team I’ve been on has sought to build long-term, high-value solutions to uncertain problems. Some teams, like design labs, may alternatively seek to build quick and short-lived solutions. And other teams, like contractors, may have very clearly demanded and scoped problems to solve. But in most product-centric software teams, our goal is to build the right thing to solve the right problem. Ideally, we build up a variety of these solutions over time, which form a product we&amp;rsquo;re proud to ship to our users. The principles I’ve shared here apply to that context, and not others.&lt;/p&gt;&lt;p&gt;So whenever we find ourselves complaining about our past decisions, we can ask: are we discounting how tough this was, and how little we knew, at the time? When we&amp;rsquo;re in the trenches, faced with the question of building more or building better, we can focus on better. And when we find ourselves looking towards the future, we can ask: if the dice rolls in different ways, how can our plan evolve?&lt;/p&gt;&lt;p&gt;Whenever we build through uncertainty, the best builders aren&amp;rsquo;t those who can perfectly predict the future, building everything that could be needed, and going all-in on. The best are rather those who make the most of the little resources and certainty we have.&lt;/p&gt;</description><author>Cam Hashemi</author><pubDate>Sun, 08 Aug 2021 14:31:26 GMT</pubDate><guid isPermaLink="true">https://camhashemi.com/essays/building-through-uncertainty/</guid></item><item><title>Why I'm not a VC (yet)</title><link>https://www.swyx.io/not-vc-yet</link><description>There is too much money chasing too few operators.</description><author>swyx's site RSS Feed</author><pubDate>Sun, 08 Aug 2021 09:26:46 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/not-vc-yet</guid></item><item><title>DisplayLink Docks and Ubuntu</title><link>https://www.mikekasberg.com/blog/2021/08/07/displaylink-docks-and-ubuntu.html</link><description>&lt;p&gt;Can you use a DisplayLink dock with Ubuntu? And, more importantly, &lt;em&gt;should you&lt;/em&gt;?
I recently researched this for a dock I wanted to buy for myself, and here’s
what I found.&lt;/p&gt;

&lt;p&gt;If you’re thinking about getting a DisplayLink dock for your laptop, you might
not even be sure if the dock will work with Ubuntu. Well, there’s good news! I
used a &lt;a href="https://plugable.com/products/ud-6950h"&gt;Plugable UD-6950H&lt;/a&gt; with Ubuntu
without problems, and it seems reasonable to expect similar docks to work as
well. In fact, there’s a good chance your DisplayLink dock works with Ubuntu
even if it says it doesn’t. That’s because, fundamentally, any DisplayLink dock
uses a DisplayLink chip, and &lt;a href="https://www.displaylink.com/"&gt;DisplayLink&lt;/a&gt; (the
company that develops DisplayLink technology and chips for various dock
manufacturers) offers &lt;a href="https://www.displaylink.com/downloads"&gt;Ubuntu drivers&lt;/a&gt;
themselves. If you have the DisplayLink software installed, connecting a
DisplayLink dock is just like connecting anything else over USB, so it’s likely
to work without problems, even on Ubuntu, on a wide variety of laptops.&lt;/p&gt;

&lt;p&gt;DisplayLink docks fit certain uses really well (and others not so much). Their
biggest advantage is that they can send video over a normal USB 3.0 (or USB 2.0
or USB-C) connection, so you can connect the dock to almost any laptop on the
planet! (Including laptops running Ubuntu!) You can connect your keyboard,
mouse, audio, ethernet, monitor, and other peripherals to the dock, and plug a
single USB cable into your laptop to get all those connections. DisplayLink
docks are also particularly well suited for connecting different laptops with
different display outputs to the same dock. But of course, you have to make
trade-offs.  Because DisplayLink compresses your video to be able to send it
over USB, it doesn’t do well with video playback, gaming, or other
high-framerate applications. (I tried watching a fullscreen 1080p video, and
while it played it was stuttering just enough visually to feel disorienting.)
I should mention that there’s no noticeable slowness aside from video/gaming –
if only a small part of the screen is changing, the compression works incredibly
well. I was able to do a full day of work (programming, web browsing, Slack,
etc) on a DisplayLink dock and wouldn’t have known the difference from a DVI
monitor connection.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Plugable UD-6950H box" src="https://www.mikekasberg.com/images/displaylink-docks-and-ubuntu/plugable.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;The specific DisplayLink dock I recently bought is a &lt;a href="https://plugable.com/products/ud-6950h"&gt;Plugable
UD-6950H&lt;/a&gt;. I chose this model
specifically because it’s a relatively modern dock (supports higher resolutions)
that’s compatible with USB-3.0, and I wanted to use it with a laptop that
doesn’t have USB-C. But purchasing a dock like this for Ubuntu is certainly a
little confusing. It isn’t advertised with Ubuntu compatibility. In fact, on the
product page, it literally says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Not compatible with: Linux/Unix&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, as I mentioned above, it actually &lt;em&gt;is&lt;/em&gt; compatible with Linux (and Ubuntu
specifically) because it uses DisplayLink, and DisplayLink provides Ubuntu
software. I’m not sure why it isn’t advertised as compatible with Ubuntu, but my
hunch is that Plugable doesn’t want to guarantee support for Linux – if it
doesn’t work, it’s not their problem. That’s fine; I don’t really fault them for
it because I’m sure Ubuntu’s market share is small enough that it doesn’t really
make sense for them to officially support. But the dock &lt;em&gt;does&lt;/em&gt; work great with
Ubuntu. I’m actually typing this blog post on my UD-6950H on Ubuntu, and the
experience has been incredibly smooth! So, as far as I can tell, DisplayLink
docks should generally be compatible with Ubuntu, and they fill a nice niche for
someone who wants to regularly connect different laptops to their desk monitor,
keyboard, and mouse. (Though, of course, I haven’t personally tested every
DisplayLink dock myself, so the usual YMMV caveat applies here.)&lt;/p&gt;

&lt;p&gt;&lt;img alt="My desk, with a docked monitor" src="https://www.mikekasberg.com/images/displaylink-docks-and-ubuntu/desk.jpg" /&gt;&lt;/p&gt;

&lt;h3 id="how-do-i-connect-a-displaylink-dock-with-ubuntu"&gt;How do I connect a DisplayLink dock with Ubuntu?&lt;/h3&gt;

&lt;div class="warning"&gt;
&lt;p&gt;&lt;b&gt;Updated:&lt;/b&gt; As of June 2023, Synaptics provides an apt repository that contains the
drivers. The instructions below have been updated to recommend that as the
easiest installation method.&lt;/p&gt;
&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Download the Synaptics APT Repository package from
&lt;a href="https://www.synaptics.com/products/displaylink-graphics/downloads/ubuntu"&gt;Synaptics&lt;/a&gt;.
(The instructions below should roughly match the instructions you see there.)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Install the repository package:
    &lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;$ sudo apt install synaptics-repository-keyring.deb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Update apt:
    &lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;$ sudo apt update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Install the DisplayLink driver:
    &lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;$ sudo apt install displaylink-driver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In summary, DisplayLink docks work great with Ubuntu. The driver is easy to
install and works well, and the overall experience is smooth. The biggest
downside you’ll notice is that videos, games, and other high framerate
applications don’t look great. This isn’t a problem with Ubuntu – it’s just the
way DisplayLink works. But if you want to primarily use your dock for web
browsing, writing, programming, spreadsheets, or any other office-like use, it
will probably work great, and you’re unlikely to notice any “slowness” at all!&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Sun, 08 Aug 2021 03:30:00 GMT</pubDate><guid isPermaLink="true">https://www.mikekasberg.com/blog/2021/08/07/displaylink-docks-and-ubuntu.html</guid></item><item><title>My Top 10 Money Rules</title><link>https://jonpauluritis.com/articles/my-top-10-money-rules/</link><description>&lt;p&gt;This is my response to &lt;a href="https://www.collaborativefund.com/blog/$/"&gt;Morgan Housel's Top 10 Money Rules&lt;/a&gt; and &lt;a href="https://ritholtz.com/2021/07/top-10-rules-for-money/"&gt;Barry Ritholtz's Top 10 Money Rules&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Earn More. Spend less.&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;A penny saved is 1.4 pennies earned if you consider taxes&amp;quot; -from the internet&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This sounds like a &amp;quot;duh&amp;quot; rule but it's not. The two concepts work against each other and require different soft skills.&lt;/p&gt;
&lt;p&gt;To increase your earning potential you need to free your time for activities that make more money- this often requires spending money (e.g. eating out to save time, paying to have your shirts pressed, etc). Reducing your expenses requires the elimination of excess, or being conservative with your existing resources... this also has a cost. It's a mindset that will keep you from making more money.&lt;/p&gt;
&lt;p&gt;To put this another way: &lt;strong&gt;High earners are usually big spenders, and fiscally conservative people often have difficulty increasing how much they earn.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If there was only one rule to wealth creation: you need more coming in than going out. Being good at both is a paradox that you must master.&lt;/p&gt;
&lt;h3&gt;Keep it Secret, Keep it Safe&lt;/h3&gt;
&lt;p&gt;My grandfather is friends with a billionaire. I'm not sure what he's driving these days but for ~15 years his daily commuter was a beat-up Ford P.O.S. Most days he wears jeans and a flannel shirt/work shirt. He has no problem going to the grocery store, gas station, or sandwich shop. He can do these things because, like several other wealthy people I know, he hides his wealth/ money intentionally.&lt;/p&gt;
&lt;p&gt;Wealth, and having money, are about freedom or having options. When you display wealth publicly (cars, houses, clothes, consumables, etc), you trade your options. For some people, this is desirable (such as celebrities) but for most people, especially those with REAL money, this is a nightmare. You lose your humanity (not to mention it's dangerous). So if you start accumulating wealth, keep it to yourself. You will be thankful you did. As Morgan Housel says: &amp;quot;Wealth is everything you don't see&amp;quot; (paraphrased).&lt;/p&gt;
&lt;h3&gt;&amp;quot;Enough&amp;quot; is Hard. Very Hard.&lt;/h3&gt;
&lt;p&gt;There's a famous quote by Coco Channel: &amp;quot;The best things in life are free, the second-best things are very very expensive.&amp;quot; What Coco is talking about is something that we all inherently know: it gets progressively harder to deliver higher and higher quality, and so the cost to do so increases as well (&lt;em&gt;or put into mathematical terms: quality is logarithmic, cost is exponential&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;For most people, this experience is why they can never get out of the rat race- a better human experience always costs more, takes more time, requires more knowledge, and is often compared to others around you (i.e. &amp;quot;Man's reach exceeds his grasp&amp;quot;).&lt;/p&gt;
&lt;p&gt;This is why having &amp;quot;enough&amp;quot; is hard. It requires that you decrease your expectations, appreciate what you have, and find the middle road with these concepts in your culture/ community. With that said having &amp;quot;enough&amp;quot; is incredibly powerful. It's a form of freedom and something that should be strived for.&lt;/p&gt;
&lt;h3&gt;Most Pop-Financial Advice is Useless or Bullshit&lt;/h3&gt;
&lt;p&gt;Financial gurus (Tony Robbins, Dave Ramsey, Ramit Sethi, Money Moustache, etc) are trying to sell you content and build their audience. &amp;quot;Famous investors&amp;quot; can neither relate to your financial world nor do they want you educated on theirs. Financial advisors provide a service... they need you coming back and spending money to stay in business. In short, just know that you're swimming in a pool of sharks - you generally have to form your own opinions the hard way: research and experimentation.&lt;/p&gt;
&lt;h3&gt;Be Wary of Consensus&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Whenever &lt;strong&gt;you&lt;/strong&gt; &lt;strong&gt;find&lt;/strong&gt; yourself on &lt;strong&gt;the&lt;/strong&gt; &lt;strong&gt;side&lt;/strong&gt; &lt;strong&gt;of&lt;/strong&gt; &lt;strong&gt;the&lt;/strong&gt; &lt;strong&gt;majority&lt;/strong&gt;, it is time to pause and reflect.&amp;quot;&lt;/p&gt;
&lt;p&gt;- Mark Twain&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My &amp;quot;first investment&amp;quot; was a mutual fund in the 90s. Of course, it wasn't really my investment- my parents picked it out because mutual funds were &amp;quot;the thing&amp;quot; at the time. I used my birthday card money to buy half - my parents covered the other half. If I remember correctly it was a $1200 minimum for the mutual fund (a LOT at the time). ~12 years later when I went to college it was worth ​$1500 (&lt;em&gt;in a good market&lt;/em&gt;). My story of course wasn't unique. For a period in the late 80s to the 90s mutual funds picked by rockstar managers were the consensus advice to retail investors. No one was paying attention to expense ratio/ fees and &amp;quot;survivorship bias&amp;quot; just wasn't talked about like it is today. And for that short period, &lt;strong&gt;the mutual fund industry made a lot of money&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In financial history &amp;quot;the 90's mutual fund&amp;quot; period isn't the only time this has happened. These financial movements aren't just about &amp;quot;bubbles&amp;quot; either. Any time there is a &amp;quot;consensus&amp;quot; among investors, opportunists take advantage because they know those investors aren't paying attention. I don't care if it's 2000's real estate or a 2020 total stock market index fund - it's fitness. When everyone is doing something and not paying attention - cookies are about to get stolen.&lt;/p&gt;
&lt;h3&gt;Invest in Yourself and Things That You Know&lt;/h3&gt;
&lt;p&gt;Pound for pound reading books, courses, training, seminars, tutoring et al. is one of the best investments you can make. In growing industries, you can readily add +30% to +40% to your income every 16-18 months. Almost no other investments out there can keep up with that kind of return.&lt;/p&gt;
&lt;p&gt;...BUT the knowledge you gain can give you more than just a bigger paycheck. It also gives you a deeper understanding of an industry, relevant technologies, and upcoming trends. You can use this knowledge to choose investments that will also have higher returns than laypersons will. Just be sure to make those bets when you get there.&lt;/p&gt;
&lt;h3&gt;Be Aware of &amp;quot;Shadow&amp;quot; Financial Decisions&lt;/h3&gt;
&lt;p&gt;Some decisions impact you a lot more than you expect. Some decisions are structured so you can't easily quantify how it will financially impact you. I call these &amp;quot;shadow&amp;quot; financial decisions. Outsized impact on your finances, but hard to understand or predict.&lt;/p&gt;
&lt;p&gt;As an example, your marriage will probably impact your financial life (for better or worse) more than any other decision you make, except maybe your career. Being in good physical shape is probably the &lt;strong&gt;most underrated financial investment&lt;/strong&gt; - it impacts earning potential, healthcare spending, energy levels, and much much more. Moving, travel, friends, networking, hobbies, are all examples of &amp;quot;shadow&amp;quot; financial decisions. So don't forget to think through these decisions as actual financial decisions and how they might impact you before you make them.&lt;/p&gt;
&lt;h3&gt;Avoid Taxes like the Plague&lt;/h3&gt;
&lt;p&gt;Tax avoidance (NOT evasion) can make a massive difference in your financial situation (think millions over a lifetime). I can't recommend enough spending some time to learn how the system works and how to take advantage of ways to avoid paying taxes (I'm still learning, but it's been &lt;em&gt;very&lt;/em&gt; valuable).&lt;/p&gt;
&lt;p&gt;(&lt;em&gt;Note: I also would heavily recommend reading up on where our government spends our money. It's very sobering how little regard our elected officials have for our tax dollars.&lt;/em&gt;)&lt;/p&gt;
&lt;h3&gt;Creativity Starts When You Remove Two Zeros from the Budget&lt;/h3&gt;
&lt;p&gt;&amp;quot;Anyone who lives within their means suffers from a lack of imagination.&amp;quot; (Oscar Wilde)&lt;/p&gt;
&lt;p&gt;Generally speaking, I like money. Not because of the things I can use it for, but because the lack of money creates conditions where better solutions can be found... and sometimes better problems. Rory Sutherland provides a great example in &lt;a href="https://www.youtube.com/watch?v=uXKilrFGd2U&amp;amp;t=369s"&gt;this video&lt;/a&gt;, where he discusses a different approach to fixing a transportation problem. His approach reduces the money needed to 10% of the original budget - but comes up with better results. This is why I think &lt;strong&gt;capitalism is more fun&lt;/strong&gt; - because money doesn't do that much to make things better - people do. People make things better through creativity.&lt;/p&gt;
&lt;h3&gt;The narrative isn't reality.&lt;/h3&gt;
&lt;p&gt;Early in my career, I worked for a tiny little billion-dollar company that you've never heard of. It was a great business that was bootstrapped by a solo founder and grown for 20 years from a mall kiosk, to roughly ~10,000 employees and something like $3 billion today. The founder was self-made and achieved all of this through hustle and grit.&lt;/p&gt;
&lt;p&gt;It's a great story. It's an especially good story for a bunch of mid-20s salespeople hungry for success. The problem is the narrative is missing some details.&lt;/p&gt;
&lt;p&gt;The sole founder wasn't solo, he wasn't even the majority owner of the business. The majority owner of the business was actually his wife, who had plunged her life savings into that business to get it off the ground. Her current occupation is &amp;quot;homemaker&amp;quot; ... which is probably less inspiring if you're trying to get a sales guy to work 80 hrs a week for you.&lt;/p&gt;
&lt;p&gt;There are a lot of narratives out there, but &lt;strong&gt;don't confuse a good story for reality&lt;/strong&gt;. &lt;a href="https://twitter.com/EpsilonTheory/status/1096542052047826944"&gt;Warren Buffet's returns have pretty much been the same as the S&amp;amp;P total return for the last 10+ years&lt;/a&gt;. &lt;a href="https://www.salon.com/2000/06/14/love_7/"&gt;A Rockstar that signs a Million dollar record deal might take home $45,000 that year&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But its not just about other people. We also tell ourselves stories that don't match reality: When you start a business you should be working 80 hours a week and never sleeping. If you want to have a successful career you don't have time to be a good parent. These are all false narratives.&lt;/p&gt;
&lt;h2&gt;Bonus Answer!&lt;/h2&gt;
&lt;p&gt;I asked my wife what her top Money Rules were... and she answered:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Find a husband who likes this stuff and offload it on to him!&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;</description><author>JonPaulUritis.com</author><pubDate>Sun, 08 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jonpauluritis.com/articles/my-top-10-money-rules/</guid></item><item><title>Successful Slack</title><link>https://justingarrison.com/blog/2021-08-08-succesful-slack/</link><description>Tips for using Slack in a work environment.</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 08 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/blog/2021-08-08-succesful-slack/</guid></item><item><title>Minutes - Building the Settings Page</title><link>https://trigonaminima.github.io/2021/08/flutter_app_2/</link><description>In the previous post, I had talked about the Quantified Self and how I started working on an app for that. I had shown the bottom navigation with the following five screens on the homepage:</description><author>Playground</author><pubDate>Sun, 08 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://trigonaminima.github.io/2021/08/flutter_app_2/</guid></item><item><title>Stray Studio and Command Line Toolkit</title><link>/project/2021/08/07/stray-studio.html</link><description>The Stray CLI and Studio is a software toolkit for solving computer vision problems. It allows you to reconstruct 3D scenes from RGB-D video, annotate the scenes with high-level semantic information and fit computer vision algorithms to the annotated scenes.</description><author>keke.dev</author><pubDate>Sat, 07 Aug 2021 22:00:00 GMT</pubDate><guid isPermaLink="true">/project/2021/08/07/stray-studio.html</guid></item><item><title>Houses should be homes, not gold mines</title><link>https://stop.zona-m.net/2021/08/houses-should-be-homes-not-gold-mines/</link><description>&lt;p&gt;But that exactly what they become on opaque digital platforms.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 07 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/houses-should-be-homes-not-gold-mines/</guid></item><item><title>Ducklings Can't Scale Stairs</title><link>https://nonhuman.party/post/ducks/</link><description>Ducklings are often seen walking through our civilisation, eagerly following a leader who might not have sufficient understanding of their followers, but they might also be following their mother duck.</description><author>Non-Human Party</author><pubDate>Sat, 07 Aug 2021 20:09:00 GMT</pubDate><guid isPermaLink="true">https://nonhuman.party/post/ducks/</guid></item><item><title>AMD Ryzen 7 5700G: first impressions on Fedora 34</title><link>https://ounapuu.ee/posts/2021/08/07/amd-ryzen-7-5700-first-impressions/</link><description>Or _'How to downgrade your CPU at great expense!'_</description><author>./techtipsy</author><pubDate>Sat, 07 Aug 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/08/07/amd-ryzen-7-5700-first-impressions/</guid></item><item><title>What is Google tensor, and why you want to know</title><link>https://stop.zona-m.net/2021/08/what-is-google-tensor-and-why-you-want-to-know/</link><description>&lt;p&gt;Here is a phone chip that may have a very interesting effect.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 07 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/what-is-google-tensor-and-why-you-want-to-know/</guid></item><item><title>Reducing variance in conversion A/B testing with CUPED</title><link>https://bytepawn.com/reducing-variance-in-conversion-ab-testing-with-cuped.html</link><description>&lt;p&gt;I use Monte Carlo simulations of conversion A/B tests to demonstrate how CUPED reduces measurement variance in conversion experiments.&lt;br /&gt;&lt;br /&gt; &lt;img alt="Historic lift" src="/images/cuped_conversion_2.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 07 Aug 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/reducing-variance-in-conversion-ab-testing-with-cuped.html</guid></item><item><title>Note 57</title><link>https://qubyte.codes/notes/1628256883634</link><description>&lt;p&gt;An interesting path close to home.&lt;/p&gt;

    &lt;img alt="Steps in the shade between houses." src="https://qubyte.codes/images/1628256856626.jpeg" /&gt;</description><author>Qubyte Codes</author><pubDate>Fri, 06 Aug 2021 16:34:43 GMT</pubDate><guid isPermaLink="true">https://qubyte.codes/notes/1628256883634</guid></item><item><title>Cssmin is Unmaintained &amp;amp; has a Bug with Complex :is Selectors</title><link>https://duncanlock.net/blog/2021/08/05/cssmin-is-unmaintained-and-has-a-bug-with-complex-is-selectors/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;I was using &lt;code&gt;cssmin&lt;/code&gt; to compress the &lt;span class="caps"&gt;CSS&lt;/span&gt; on this site during publishing. This has been working fine since April 2013, when I created this new iteration of the site using &lt;a href="https://blog.getpelican.com/"&gt;Pelican&lt;/a&gt;. Unbeknownst to me, cssmin has been unmaintained since October 2013. This didn&amp;#8217;t cause any problems until recently, when I revamped the &lt;span class="caps"&gt;CSS&lt;/span&gt; and started using the new-ish &lt;a href="https://duncanlock.net/blog/2021/07/19/the-css-is-selector-is-pretty-neat/"&gt;:is selector&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So a selector like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;figure&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.align-left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;.align-right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;would get the spaces removed and get output like this, breaking&amp;nbsp;it:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nc"&gt;.content&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;figure&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.align-left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;.align-right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;My solution was to change to &lt;code&gt;rcssmin&lt;/code&gt;, literally a one character change to the &lt;a href="https://pypi.org/project/pelican-webassets/"&gt;webassets&lt;/a&gt;&amp;nbsp;call:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="nv"&gt;assets&lt;/span&gt; &lt;span class="nv"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rcssmin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"css/style.min.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and then just update the python modules in the&amp;nbsp;venv:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip uninstall cssmin
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;rcssmin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a class="bare" href="https://github.com/zacharyvoase/cssmin"&gt;https://github.com/zacharyvoase/cssmin&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Fri, 06 Aug 2021 08:19:08 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/08/05/cssmin-is-unmaintained-and-has-a-bug-with-complex-is-selectors/</guid></item><item><title>Deploying django-tailwind to Heroku</title><link>https://www.khanna.law/blog/deploying-django-tailwind-to-heroku</link><description>My preferred technology for rapidly prototyping ideas is Django, styled with Tailwind CSS and deployed on Heroku.</description><author>Khanna Law Blog</author><pubDate>Fri, 06 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.khanna.law/blog/deploying-django-tailwind-to-heroku</guid></item><item><title>WhatsApp still struggling to catch up with the 1990s</title><link>https://stop.zona-m.net/2021/08/whatsapp-still-struggling-to-catch-up-with-the-1990s/</link><description>&lt;p&gt;Will that app ever live in THIS century?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 06 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/whatsapp-still-struggling-to-catch-up-with-the-1990s/</guid></item><item><title>Rust on Maemo</title><link>https://dorotac.eu/posts/rust_maemo/</link><description>&lt;h1&gt;Rust on Maemo&lt;/h1&gt;
&lt;p&gt;Everyone wants to run Rust on their phone, right?&lt;/p&gt;
&lt;p&gt;But it's not so easy when your pocket device is the Nokia N900. You'd think that I should be using the &lt;a href="https://puri.sm/products/librem-5/"&gt;Librem 5&lt;/a&gt;, but my pockets disagree. Actually, I agree with them, too, at least as long as I use the device to collect GPS tracks.&lt;/p&gt;
&lt;p&gt;The phone is over 10 years old by this point, and it's not open, which means it's running outdated software. Nokia stopped supporting Maemo around 2011, and the last &lt;a href="https://wiki.maemo.org/Community_SSU"&gt;community update&lt;/a&gt; happened a couple of years ago too. Despite that, I'd like to run some of my own code on it. The canonical way to do it is to use the SDK via a &lt;a href="https://github.com/accupara/docker-images/tree/master/mobile/maemo"&gt;Docker image&lt;/a&gt;, but it's rather clumsy.&lt;/p&gt;
&lt;h2&gt;GNU libc&lt;/h2&gt;
&lt;p&gt;My first attempt to compile code for the N900 was to use a recent version of Debian, running on an emulated ARM CPU. It didn't go far: after copying to the phone, the program failed to run. Glibc is too old.&lt;/p&gt;
&lt;p&gt;Easy, I thought! I can always package up glibc together with the program via static linking. As I tried it out, I was greeted with a crash message along the lines of:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This kernel is too old.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Jolly, so Linux version 2.6.28 doesn't meet modern standards. It seems my road is blocked, and this was just a "hello world" C program.&lt;/p&gt;
&lt;h2&gt;Musl&lt;/h2&gt;
&lt;p&gt;A while later, I realized that the kernel complaint probably came from glibc as well. But Rust doesn't have to use glibc, it can also use musl as the C library! Let's check Rust's &lt;a href="https://doc.rust-lang.org/nightly/rustc/platform-support.html"&gt;platform support page&lt;/a&gt;. The CPU on the N900 supports the ARMv7 instruction set, so we're looking for &lt;code&gt;armv7-unknown-linux-&lt;/code&gt;. And here we go: apart from &lt;code&gt;armv7-unknown-linux-gnueabi&lt;/code&gt;, indicating the usage of glibc, there's also &lt;code&gt;armv7-unknown-linux-musleabi&lt;/code&gt; for musl.&lt;/p&gt;
&lt;p&gt;Looking closer at musl's &lt;a href="https://www.musl-libc.org/faq.html"&gt;FAQ page&lt;/a&gt; and the section on requirements:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux 2.6 or later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Awesome! That means our ancient kernel is supported.&lt;/p&gt;
&lt;p&gt;Rust has the ability to cross-compile, which makes testing the solution easy. I picked up [my earlier project] related to the N900, added a &lt;code&gt;.cargo/config&lt;/code&gt; file, and typed in the following commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dnf install -y gcc # needed for syn at compile time
dnf install -y lld # no armel gcc toolchain for Fedora, so use clang linker cause it does armel out of the box
curl https://sh.rustup.rs -sSf | sh -s -- -t armv7-unknown-linux-musleabi
cd /mnt/src/grconverter
source $HOME/.cargo/env
cargo build --target armv7-unknown-linux-musleabi
cp target/armv7-unknown-linux-musleabi/debug/read /mnt/n900 # copy onto the phone
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…and it's alive!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./read MyDocs/gpsrecorder/gpstrack-20210804-140132.gpsr | head
Header(
    Header {
        magic: [
            71,
            80,
            83,
            82,
        ],
        format: 3,
        time: 1628078492,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or at least half-alive. Linking to external libraries is still an open question, but the standard library works, which means pure-Rust crates are good to go.&lt;/p&gt;
&lt;p&gt;Happy hacking!&lt;/p&gt;</description><author>dcz's posts</author><pubDate>Thu, 05 Aug 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://dorotac.eu/posts/rust_maemo/</guid></item><item><title>TIL - Organize a Folder of Thousands of Images Into Subfolders</title><link>https://ingo-richter.io/post/2021/organize-a-folder-of-thousands-of-images-into-subfolders/</link><description>&lt;h2 id="reorg-time"&gt;Reorg Time&lt;/h2&gt;
&lt;p&gt;I use &lt;a href="https://nextcloud.com/" title="Nextcloud"&gt;Nextclouds&lt;/a&gt; image upload feature to backup all photos from my phone. I recently discovered that you could upload pictures from your phone into subfolders in the &lt;code&gt;YEAR/MONTH/DAY/picturefile&lt;/code&gt; format.&lt;/p&gt;
&lt;p&gt;After switching this feature on, I realized that all my previously uploaded 3000+ images and short videos lie flat in the Pictures folder. I needed a solution to organize all existing photos into the same subfolder structure.&lt;/p&gt;</description><author>Ingo Richter on Today is Tomorrow's Past</author><pubDate>Thu, 05 Aug 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ingo-richter.io/post/2021/organize-a-folder-of-thousands-of-images-into-subfolders/</guid></item><item><title>Second Beach</title><link>https://sam.hooke.me/trip/2021/08/second-beach/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Thu, 05 Aug 2021 09:25:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/08/second-beach/</guid></item><item><title>Practical? Common Lisp on the JVM: A quick intro to ABCL for modern web apps</title><link>http://notes.eatonphil.com/practical-common-lisp-on-the-jvm.html</link><description>&lt;p&gt;In a ridiculous attempt to &lt;a href="https://news.ycombinator.com/item?id=28036679"&gt;prove an internet
wrong&lt;/a&gt; about the
practicality of Lisp (Common Lisp specifically), I tried to get a
simple (but realistic) web app running. After four days and &lt;a href="https://github.com/armedbear/abcl/pull/379"&gt;a patch
to ABCL&lt;/a&gt; I got something
working.&lt;/p&gt;
&lt;p&gt;The code I had in mind would look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;My index!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/search&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;search.tmpl&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                                &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;results&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cat&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dog&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mouse&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;code&gt;search.tmpl&lt;/code&gt; would be some Jinja-like text file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Version {{ version }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  {% for item in results %}
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ item }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  {% endfor %}
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The source code for this post can be found &lt;a href="https://github.com/eatonphil/jvm-lisp-examples"&gt;on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="picking-a-language,-libraries"&gt;Picking a language, libraries&lt;/h3&gt;&lt;p&gt;&lt;a href="https://abcl.org"&gt;Armed Bear Common Lisp&lt;/a&gt; (ABCL) is the only Common Lisp
implementation I'm aware of that can hook into a major ecosystem of
libraries like the JVM or CLR has. In theory, this makes it a safe
suggestion for folks who want the stability and resources of the
ecosystem even if they aren't using its flagship language.&lt;/p&gt;
&lt;p&gt;I wanted to use some micro web framework like
&lt;a href="https://sparkjava.com/"&gt;Spark&lt;/a&gt; or &lt;a href="https://micronaut.io/"&gt;Micronaut&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem with libraries like Micronaut (and
&lt;a href="https://eclipse-ee4j.github.io/jersey/"&gt;Jersey&lt;/a&gt;) is that they do a
lot of dynamic inspection to figure out how to register controllers
and whatnot. This is certainly convenient for developers using the
library in Java. But it becomes an ordeal when you're trying to use
the library through a foreign function interface (FFI) in another
language. An example of this is if a framework scans all files in a
directory for a &lt;code&gt; @GET&lt;/code&gt; annotation.&lt;/p&gt;
&lt;p&gt;On the other hand, Spark had a seeming hard-requirement about bringing
in a Websocket library which caused some issues during
configuration. So I ended up going with &lt;a href="https://jooby.io/"&gt;Jooby&lt;/a&gt; and
&lt;a href="https://netty.io/"&gt;Netty&lt;/a&gt; (as the underlying server).&lt;/p&gt;
&lt;p&gt;Finally, I looked into a few Jinja-like template libraries and settled
on &lt;a href="https://pebbletemplates.io/"&gt;Pebble&lt;/a&gt; since
&lt;a href="https://github.com/HubSpot/jinjava"&gt;Jinjava&lt;/a&gt; &lt;a href="https://github.com/HubSpot/jinjava/issues/317"&gt;wouldn't load for
me&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="3rd-party-jars-and-foreign-function-calls"&gt;3rd-party jars and foreign function calls&lt;/h3&gt;&lt;p&gt;So you've got your maven dependencies and ran &lt;code&gt;mvn
install&lt;/code&gt;. Your &lt;code&gt;pom.xml&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;project&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;modelVersion&amp;gt;&lt;/span&gt;4.0.0&lt;span class="nt"&gt;&amp;lt;/modelVersion&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.github.eatonphil&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;abcl-rest-api-hello-world&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.jooby&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jooby&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.10.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.jooby&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jooby-netty&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.10.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.pebbletemplates&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;pebble&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.5&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ABCL has a package called &lt;code&gt;abcl-asdf&lt;/code&gt; that helps you resolve dependencies through Maven and your filesystem. We'll import it and a package it depends on (&lt;code&gt;abcl-contrib&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:abcl-contrib&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:abcl-asdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All our code will go into a single &lt;code&gt;main.lisp&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;To import a specific package from Maven you
call &lt;code&gt;abcl-asdf:resolve&lt;/code&gt; with a colon-separated string
containing the Maven package group id and artifact id. Then you pass
that result to &lt;code&gt;abcl-asdf:as-classpath&lt;/code&gt; and pass that
result to &lt;code&gt;java:add-to-classpath&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It will look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;imports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby:jooby&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby:jooby-netty&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.pebbletemplates:pebble&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;imports&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nb"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:add-to-classpath&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;abcl-asdf:as-classpath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;abcl-asdf:resolve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;import&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can call functions within these packages. If you want to call
a Java method using only builtins it looks like &lt;code&gt;(jcall "method"
"com.organization.package.Class" object arg1 arg2 ... argN)&lt;/code&gt;. If
you want to call a static Java method you use &lt;code&gt;(jstatic
...)&lt;/code&gt; instead of &lt;code&gt;(jcall ...)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It seems that ABCL will automatically convert simple types from their
Lisp representation to Java but it will not turn a list into an
array. If a Java function requires an array you'll have to do that
explicitly with a function like &lt;code&gt;(java:jnew-array-from-list
"java.lang.String" my-string-list)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When using the builtin Java FFI you always need to use the fully
qualified name for classes like &lt;code&gt;java.lang.Object&lt;/code&gt;
for &lt;code&gt;Object&lt;/code&gt; or &lt;code&gt;java.util.Array&lt;/code&gt;
for &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Alternatively you can &lt;code&gt;(require :jss)&lt;/code&gt; to get access to a
simpler syntax for making Java calls. A method call looks
like &lt;code&gt;(#"method" object arg1 arg2 ... argN)&lt;/code&gt;. Creating a
new instance of an object is calling &lt;code&gt;(jss:jnew
'className)&lt;/code&gt;. When you use JSS you don't need to fully qualify a
class name unless there are more than one class with the same
name. For example to create a new Jooby application instance we can
call &lt;code&gt;(jss:jnew 'Jooby)&lt;/code&gt;. As long as the class can be found
in the class path JSS will resolve it.&lt;/p&gt;
&lt;h3 id="some-real-code"&gt;Some real code&lt;/h3&gt;&lt;p&gt;The real code will look similar to the pseudo-code at the top of this
article. We'll stub out the library-specific wrappers for rendering a
template and for registering a route.&lt;/p&gt;
&lt;p&gt;Fumbling around the &lt;a href="https://github.com/jooby-project/jooby/blob/2.x/jooby/src/main/java/io/jooby/Server.java#L35"&gt;Jooby source code&lt;/a&gt; we see this snippet of Java:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Netty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// or Jetty or Utow&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Netty&lt;/code&gt; comes from the &lt;code&gt;jooby-netty&lt;/code&gt; artifact in
the &lt;code&gt;io.jooby&lt;/code&gt; group on Maven. And &lt;code&gt;App&lt;/code&gt; is some
object that extends &lt;code&gt;io.jooby.Jooby&lt;/code&gt;. Since we're not using
an OOP language though we're going to try avoiding classes as much as
possible. So we'll just create a new instance
of &lt;code&gt;io.jooby.Jooby&lt;/code&gt; and add routes directly to it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;register-endpoints&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;An index!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/search&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;search.tmpl&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.0.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;results&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:jarray-from-list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cat&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dog&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;mouse&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/hello-world&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello world!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'Netty&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'Jooby&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;register-endpoints&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;setOptions&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;setPort&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'ServerOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;join&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Easy enough. Now we just need to implement &lt;code&gt;route&lt;/code&gt;
and &lt;code&gt;template&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="implementing-java-classes-in-abcl"&gt;Implementing Java classes in ABCL&lt;/h3&gt;&lt;p&gt;We are again not going the happy path with fancy Java syntax (which is
fine if you're using Java) like the Jooby documentation
suggests. Scouring the &lt;a href="https://github.com/jooby-project/jooby/blob/2.x/jooby/src/main/java/io/jooby/Jooby.java#L546"&gt;Jooby source code
again&lt;/a&gt;
it looks like we can call &lt;code&gt;route&lt;/code&gt; on the &lt;code&gt;Jooby&lt;/code&gt;
class with a method string, a path string, and an instance of an
object implementing the &lt;code&gt;io.jooby.Route.Handler&lt;/code&gt; interface.&lt;/p&gt;
&lt;p&gt;Since this handler argument is an interface, we cannot cheat again by
creating an instance of it we'll have to actually create a new class
in Lisp that extends it. Thankfully there's only one method we need to
implement to satisfy this interface,
&lt;a href="https://github.com/jooby-project/jooby/blob/2.x/jooby/src/main/java/io/jooby/Route.java#L256"&gt;apply&lt;/a&gt;. It
accepts a &lt;code&gt;io.jooby.Context&lt;/code&gt; object and returns
a &lt;code&gt;java.lang.Object&lt;/code&gt;. The framework then does introspection
to figure out what exactly the object is and if it needs to transform
it into a string to be returned as an HTTP response body.&lt;/p&gt;
&lt;p&gt;To create a new class in ABCL we call &lt;code&gt;(java:jnew-runtime-class
"classname" :interfaces '("an interface name") :methods '(("method
name 1" "return type" ("first parameter type" ...) (lambda (this arg1
  ...) body))))&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;route&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nc"&gt;method&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:jnew-runtime-class&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;substitute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;substitute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="ss"&gt;:interfaces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby.Route$Handler&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="ss"&gt;:methods&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;apply&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;java.lang.Object&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby.Context&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;funcall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;handler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;))))))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One thing to note is that when referring to a subclass within a file
we need to address it with the &lt;code&gt;io.jooby.Route$Handler&lt;/code&gt;
syntax rather than as you might refer to it in Java
as &lt;code&gt;io.jooby.Route.Handler&lt;/code&gt;. In the latter case ABCL
thinks &lt;code&gt;Route&lt;/code&gt; is a package when in fact it's just a class.&lt;/p&gt;
&lt;p&gt;If you run this now with &lt;code&gt;abcl --load main.lisp&lt;/code&gt;. It will
work until you hit an endpoint. The problem is how Jooby tries to
figure out the real type of the returned object.&lt;/p&gt;
&lt;p&gt;The app will crash somewhere around
&lt;a href="https://github.com/jooby-project/jooby/blob/2.x/jooby/src/main/java/io/jooby/internal/RouterImpl.java#L560"&gt;here&lt;/a&gt;
calling &lt;code&gt;analyzer.returnType(route.getHandle())&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this case it tries to &lt;a href="https://github.com/jooby-project/jooby/blob/f47eda4500bc4b76b23d24d4d77aa2ab3cc19e95/jooby/src/main/java/io/jooby/internal/RouteAnalyzer.java#L44"&gt;open and parse the (Java) source
code&lt;/a&gt;
of our application to try to find the return type for
this &lt;code&gt;apply&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;That's a problem since our code isn't Java. Through trial and error I
realized we can trick Jooby/Java/somebody into figuring out the
correct return type by adding another implementation
of &lt;code&gt;apply&lt;/code&gt; that returns a &lt;code&gt;String&lt;/code&gt; to our class.&lt;/p&gt;
&lt;p&gt;The full &lt;code&gt;route&lt;/code&gt; code now looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;route&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nc"&gt;method&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:jnew-runtime-class&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;substitute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;substitute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;#\-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="ss"&gt;:interfaces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby.Route$Handler&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="ss"&gt;:methods&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="c1"&gt;;; Need to define this one to make Jooby figure out the return type&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="c1"&gt;;; Otherwise it tries to read &amp;quot;this file&amp;quot; which isn't a Java file so cannot be parsed&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;apply&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;java.lang.String&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby.Context&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="c1"&gt;;; This one actually gets called&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;apply&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;java.lang.Object&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;io.jooby.Context&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;funcall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;handler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;))))))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You may wonder, why keep the original method around? Well it's because
during reflection, ABCL says no such method that
returns &lt;code&gt;String&lt;/code&gt; exists in the &lt;code&gt;Handler&lt;/code&gt;
interface. That's fair I guess.&lt;/p&gt;
&lt;h3 id="implementing-the-template"&gt;Implementing the template&lt;/h3&gt;&lt;p&gt;The Java example on the &lt;a href="https://pebbletemplates.io/"&gt;Pebble homepage&lt;/a&gt;
is perfect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;PebbleEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PebbleEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;PebbleTemplate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compiledTemplate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;home.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Mitchell&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StringWriter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;compiledTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can easily translate this into Lisp:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hashmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;alist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'HashMap&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;alist&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nb"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;put&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;car&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cadr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;context-alist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;hashmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;context-alist&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:jstatic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;of&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;java.nio.file.Path&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;readString&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'java.nio.file.Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'PebbleEngine$Builder&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;compiledTmpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;getTemplate&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'java.io.StringWriter&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;evaluate&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;compiledTmpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;toString&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But if you run this &lt;code&gt;abcl --load main.lisp&lt;/code&gt; and hit this
&lt;code&gt;/search&lt;/code&gt; endpoint, it will blow up saying "no such method"
exists at the call to &lt;code&gt;Path.of(filename)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After digging around I saw it was because
&lt;a href="https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/nio/file/Path.html#of(java.lang.String,java.lang.String...%29"&gt;Path.of&lt;/a&gt;
is a variadic function.&lt;/p&gt;
&lt;p&gt;And while there are &lt;a href="https://abcl.org/trac/changeset/15234"&gt;examples
of&lt;/a&gt; using variadic functions
when the function only has a single parameter like
&lt;code&gt;java.util.Arrays.asList(T ...)&lt;/code&gt;, employing that same
technique here continued to result in "no such method":&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;         (path (java:jstatic &amp;quot;of&amp;quot; &amp;quot;java.nio.file.Path&amp;quot; filename (jnew-array &amp;quot;java.lang.String&amp;quot; 0)))
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Eventually I found an &lt;a href="https://stackoverflow.com/questions/20440839/cant-invoke-method-with-varargs-parameters-with-reflection-nosuchmethodexcept"&gt;example of someone doing reflect/invoke on this
kind of a function
call&lt;/a&gt;
and tried this logic on a local copy of the ABCL source code.&lt;/p&gt;
&lt;p&gt;It worked. So I opened a &lt;a href="https://github.com/armedbear/abcl/pull/379"&gt;pull request&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So the full working code for &lt;code&gt;template&lt;/code&gt; is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;context-alist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;hashmap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;context-alist&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:jstatic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;of&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;java.nio.file.Path&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;java:jnew-array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;java.lang.String&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;readString&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'java.nio.file.Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'PebbleEngine$Builder&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;compiledTmpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;getTemplate&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;jss:new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;'java.io.StringWriter&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;evaluate&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;compiledTmpl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="l l-Other"&gt;#&amp;quot;toString&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And to get this diff running locally:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;~/vendor
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/vendor
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/eatonphil/abcl
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;abcl
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;pe/more-variadic
$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;dnf/brew/apt&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ant&lt;span class="w"&gt; &lt;/span&gt;maven
$&lt;span class="w"&gt; &lt;/span&gt;ant&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;build.xml
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And to run &lt;code&gt;main.lisp&lt;/code&gt; using this diff:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;abcl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;abcl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lisp&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And to hit the API:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;localhost:8080/search
&amp;lt;html&amp;gt;
&amp;lt;title&amp;gt;Version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.0.0&amp;lt;/title&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;h2&amp;gt;cat&amp;lt;/h2&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;h2&amp;gt;dog&amp;lt;/h2&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;h2&amp;gt;mouse&amp;lt;/h2&amp;gt;
&amp;lt;/html&amp;gt;
$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;localhost:8080/hello-world
Hello&lt;span class="w"&gt; &lt;/span&gt;world!%
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Phew! Easy peasy.&lt;/p&gt;
&lt;h3 id="next-up"&gt;Next up&lt;/h3&gt;&lt;p&gt;I'm porting this example to Kawa to see how it fares. Blog post to come.&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;In a ridiculous attempt to prove an internet wrong about the practicality of Lisp (Common Lisp specifically), I tried to get a simple (but realistic) web app running. After four days and a patch to ABCL I got something working.&lt;a href="https://t.co/5UUWNR8Wnn"&gt;https://t.co/5UUWNR8Wnn&lt;/a&gt; &lt;a href="https://t.co/cZsx32IlKD"&gt;pic.twitter.com/cZsx32IlKD&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1423345414279942150?ref_src=twsrc%5Etfw"&gt;August 5, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Thu, 05 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/practical-common-lisp-on-the-jvm.html</guid></item><item><title>How to extract PCSO lottery winning combinations using Python</title><link>https://www.jskherman.com/blog/lotto-data-scraper/</link><description>&lt;p&gt;In this post, we&amp;rsquo;ll explore how to scrape and prepare PCSO lottery data in Excel, in order to better inform your next plays to take it to the next level.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;</description><author>jskherman</author><pubDate>Thu, 05 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jskherman.com/blog/lotto-data-scraper/</guid></item><item><title>Prior Writings</title><link>https://ztoz.blog/posts/prior-writings/</link><description>&lt;p&gt;Between Honeywell and Qualtrics, I founded a company called ℤ→ℤ Technologies. The first thing I tried was a
steganography plug-in for Firefox. The plug-in attempted to hide some text using a user-provided corpus to compute
a statistical language model. I &lt;a href="http://ztostartup.blogspot.com/2013/10/mimicking-tweets.html"&gt;documented&lt;/a&gt; the early
research in emulating tweets. Since that wasn&amp;rsquo;t fruitful, I pivoted towards an automated statistical analysis service based on the idea
of switching the problem from a high-skill process of selecting what processing to do to a low-skill process
of searching through the results for something valuable. (I never found a solution to prevent this from being
p-hacking as a service.) When I found that integration tests were taking far longer than on a single computer, simple
batch job system, I developed a distrust in big data technologies that required
&lt;a href="http://ztostartup.blogspot.com/2013/12/hadoop-processing-model.html"&gt;high startup costs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The analysis service project led to two side-efforts: a functional reimplementation of the
&lt;a href="https://github.com/jeffreystarr/SquarifiedTreemap"&gt;square treemap visualization&lt;/a&gt; and a Python library for
inferring the &lt;a href="https://github.com/jeffreystarr/dateinfer"&gt;format of a date&lt;/a&gt;. (Although I forgot about this library
and failed to support it for seven years, it has seen a number of forks and improvements by others.)&lt;/p&gt;
&lt;p&gt;At Qualtrics, I wrote two articles for our engineering blog. One of the more popular articles was about using
&lt;a href="https://www.qualtrics.com/eng/wargames-on-call-training/"&gt;wargames for training&lt;/a&gt;. A wargame, in this sense, is
a simulation of one or more scenarios which requires the trainees to respond using real-world tools. For example,
causing high load on a box and having the trainees diagnose and kill the malignant process. I was often asked if
we still used this practice. The answer is yes, multiple teams did create artificial outages or degraded situations
in our staging environments to train new on-call engineers. Over time, the training tended to become more
specialized. As the Qualtrics infrastructure moved from virtual machines to orchestration environments, we stopped
training for certain failure modes. For instance, operators did not need to train on restarting processes as the
orchestrator handled that for them.&lt;/p&gt;
&lt;p&gt;The less popular article was a discussion on &lt;a href="https://www.qualtrics.com/eng/characterizing-system-design-of-experiments/"&gt;design of experiments&lt;/a&gt;,
a technique I learned and used at Honeywell. Design of Experiments is a way to find what parameters matter most
for a system. DoE is meant to be used by humans, not computers, so many features of the practice are meant to keep
the costs down. (DoE is often used in domains with destructive testing.) Those in the machine learning community
would see this as a type of hill-climbing algorithm.&lt;/p&gt;
&lt;p&gt;Although I was not the author, one of my co-workers wrote a detailed article on our &lt;a href="https://www.qualtrics.com/eng/indexing-text-for-both-effective-search-and-accurate-analysis/"&gt;language processing and indexing pipeline&lt;/a&gt;
which serves as the de-facto public technical document on the subject. As such, it is worthy of another link.&lt;/p&gt;</description><author>ℤ→ℤ</author><pubDate>Thu, 05 Aug 2021 00:43:07 GMT</pubDate><guid isPermaLink="true">https://ztoz.blog/posts/prior-writings/</guid></item><item><title>Reminder of who really rules Facebook</title><link>https://stop.zona-m.net/2021/08/reminder-of-who-really-rules-facebook/</link><description>&lt;p&gt;It&amp;rsquo;s easy. It&amp;rsquo;s just one person.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 04 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/reminder-of-who-really-rules-facebook/</guid></item><item><title>Introducing JfrUnit 1.0.0.Alpha1</title><link>https://www.morling.dev/blog/introducing-jfrunit-1-0-0-alpha1/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_getting_started_with_jfrunit"&gt;Getting Started With JfrUnit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_groovier_tests_with_spock"&gt;Groovier Tests With Spock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_outlook"&gt;Outlook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;em&gt;Unit testing, for performance&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;It’s with great pleasure that I’m announcing the first official release of JfrUnit today!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;&lt;a href="https://github.com/moditect/jfrunit"&gt;JfrUnit&lt;/a&gt; is an extension to JUnit which allows you to assert &lt;a href="https://openjdk.java.net/jeps/328"&gt;JDK Flight Recorder&lt;/a&gt; events in your unit tests.
This capability opens up a number of interesting use cases in the field of testing JVM-based applications:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You can use JfrUnit to ensure your application produces the &lt;a href="blog/rest-api-monitoring-with-custom-jdk-flight-recorder-events/"&gt;custom JFR events&lt;/a&gt; you expect it to emit&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use JfrUnit to identify potential performance regressions of your application by means of tracking JFR events e.g. for garbage collection, memory allocation and network I/O&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use JfrUnit together with &lt;a href="https://wiki.openjdk.java.net/display/jmc/The+JMC+Agent"&gt;JMC Agent&lt;/a&gt; for whitebox tests of your application, ensuring specific methods are invoked with the expected parameters and return values&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Wed, 04 Aug 2021 20:10:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/introducing-jfrunit-1-0-0-alpha1/</guid></item><item><title>Tokyo Olympics</title><link>https://www.bajkowski.com/tokyo-olympics/</link><description>My take on the Tokyo Olympics and Olympics in general, and how they are a an uplifting event rather than a money sink as often times portrayed</description><author>Bajkowski.com</author><pubDate>Wed, 04 Aug 2021 19:57:05 GMT</pubDate><guid isPermaLink="true">https://www.bajkowski.com/tokyo-olympics/</guid></item><item><title>We need to rethink tech hiring..</title><link>https://ankitag9.substack.com/p/we-need-to-rethink-tech-hiring</link><description>Is our process too influenced by FAANG</description><author>Perspective by Ankit Agarwal</author><pubDate>Wed, 04 Aug 2021 15:32:36 GMT</pubDate><guid isPermaLink="true">https://ankitag9.substack.com/p/we-need-to-rethink-tech-hiring</guid></item><item><title>Snowballing the Angel of Death</title><link>http://evantravers.com/articles/2021/08/04/snowballing-the-angel-of-death/</link><description>&lt;p&gt;In &lt;a href="https://www.workfutures.io/p/wrestling-with-the-angel-of-death"&gt;Wrestling With The Angel of Death&lt;/a&gt;, Stowe Boyd addresses &lt;a href="https://www.wired.com/story/to-do-apps-failed-productivity-tools/"&gt;an essay in Wired by Clive Thompson&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I tend to agree with Mr. Boyd's points. He does a great job of walking through many of the problems… including the problem of being overwhelmed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“We call it snowballing,” says Amir Salihefendić, who founded the app Todoist in 2007; it currently has 30 million users. “They keep postponing stuff. And then suddenly you have a hundred tasks that you need to do.” Weeks or months later, your Todoist app is a teetering ziggurat of tasks, too painful even to behold. Omer Perchik, the creator of another app—Any.do—calls this problem “the List of Shame.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I do believe that Stowe is a little uncharitable to those behind him on the work-life maturity. After all, he spends his professional life imagining healthier and better information work! As Stowe mocks some of the strategies people use to avoid snowballing, I believe Stowe is forgetting the personal growth, personality, or work-culture that teach people to not treat their task lists as &amp;quot;must do.&amp;quot; It's not natural to most folks. I had to &amp;quot;be enlightened:&amp;quot; a list of tasks is a map of things I &lt;em&gt;could&lt;/em&gt; do. I can &lt;a href="http://evantravers.com/articles/2019/10/09/sisyphus-and-the-focus-boulder/"&gt;stop pushing the sisyphean boulder&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I am always a fan of learning about other people's systems, so I enjoyed Stowe's comments and description of &lt;a href="https://workings.substack.com/p/the-taskora-convention"&gt;his &amp;quot;Taskora&amp;quot; system&lt;/a&gt;. It seems tailored to his work and writing, and I would personally characterize it as a &amp;quot;&lt;a href="http://evantravers.com/articles/series/creativity-systems/"&gt;creativity system&lt;/a&gt;.&amp;quot;&lt;/p&gt;
&lt;p&gt;Stowe correctly identifies that the connective tissues between context and tasks are often lost in many task systems. I think that is part of why I love Things.app… and why emacs org-mode persists through the decades.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thompson describes various approaches he's used to manage tasks and they share one glaring characteristic: they were isolated from the tools and methods he uses to keep track of everything other than tasks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think this is the missing link between the essays: Thompson blames the snowball effect, claiming that overwhelmed users declare bankruptcy and trying a new silver bullet tool, there fore all the tools are useless. Stowe scoffs because he has built a Stowe-shaped system based on basic principles that work for him. I too believe in principles over tools. Bullet Journaling, Omnifocus, GTD, won't fix your problem, but the principles they employ.&lt;/p&gt;
&lt;p&gt;If you read my blog, you'll probably enjoy both essays. They are worth reading… but look to cherry-pick principles that apply to where you are in your productivity journey. Wholesale adoption of systems doesn't work. Understanding and building your own based on useful principles is very worthwhile.&lt;/p&gt;&lt;p&gt;&lt;a class="u-in-reply-to  is-reply" href="https://www.workfutures.io/p/wrestling-with-the-angel-of-death"&gt;Wrestling With The Angel of Death - www.workfutures.io&lt;/a&gt;&lt;/p&gt;</description><author>trv.rs</author><pubDate>Wed, 04 Aug 2021 13:29:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/08/04/snowballing-the-angel-of-death/</guid></item><item><title>Hoh River Trail</title><link>https://sam.hooke.me/trip/2021/08/hoh-river-trail/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Wed, 04 Aug 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/08/hoh-river-trail/</guid></item><item><title>Hoh Hall of Mosses</title><link>https://sam.hooke.me/trip/2021/08/hoh-hall-of-mosses/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Wed, 04 Aug 2021 10:30:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/08/hoh-hall-of-mosses/</guid></item><item><title>Download OpenStreetMap bounding box PNG</title><link>https://allanrbo.blogspot.com/2021/08/download-openstreetmap-bounding-box-png.html</link><description>&lt;p&gt;For a project where I was plotting some locations using Matplotlib, I needed a way to get a PNG map from a bounding box lat-long pair. Here's a Python function I came up with.&lt;/p&gt;

&lt;pre class="python" name="code"&gt;
from os import listdir, mkdir
from os.path import exists
from PIL import Image
from random import uniform
from time import sleep
from urllib.request import Request, urlopen
import math

# Similar to https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Python .
def deg2float(lat_deg, lon_deg, zoom):
    lat_rad = math.radians(lat_deg)
    n = 2.0 ** zoom
    xtile = (lon_deg + 180.0) / 360.0 * n
    ytile = (1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n
    return (xtile, ytile)

def download_map(zoom, lat1, lon1, lat2, lon2):
    lon_start, lon_end = min(lon1, lon2), max(lon1, lon2)
    lat_start, lat_end = max(lat1, lat2), min(lat1, lat2)

    # Top left corner of bounding box.
    x1, y1 = deg2float(lat_start, lon_start, zoom)
    x1i, y1i = math.floor(x1), math.floor(y1)

    # Bottom right corner of bounding box.
    x2, y2 = deg2float(lat_end, lon_end, zoom)
    x2i, y2i = math.ceil(x2), math.ceil(y2)

    x_cnt, y_cnt = abs(x1i - x2i), abs(y1i - y2i)
    if x_cnt*y_cnt &amp;gt; 250:
        err = &amp;quot;Too many tiles. Probably too big an area at too high a zoom level.&amp;quot;
        err += &amp;quot; See https://operations.osmfoundation.org/policies/tiles/ .&amp;quot;
        raise Exception(err)

    if not exists(&amp;quot;maptiles&amp;quot;):
        mkdir(&amp;quot;maptiles&amp;quot;)

    for x in range(x_cnt):
        for y in range(y_cnt):
            xt, yt = x + x1i, y + y1i
            path = &amp;quot;maptiles/{}_{}_{}.png&amp;quot;.format(zoom, xt, yt)

            if not exists(path):
                sleep(uniform(0.5, 1.5))
                url = &amp;quot;https://tile.openstreetmap.org/{}/{}/{}.png&amp;quot;.format(zoom, xt, yt)
                print(&amp;quot;Downloading tile {}&amp;quot;.format(url))
                req = Request(url)
                ua = &amp;quot;Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:90.0) Gecko/20100101 Firefox/90.0&amp;quot;
                req.add_header(&amp;quot;User-Agent&amp;quot;, ua)  # OSM seems to not like Python's default UA.
                resp = urlopen(req)
                body = resp.read()
                with open(path, &amp;quot;wb&amp;quot;) as f:
                    f.write(body)

    im = Image.open(&amp;quot;maptiles/{}_{}_{}.png&amp;quot;.format(zoom, x1i, y1i))
    tile_w, tile_h = im.size
    total_w = x_cnt*tile_w
    total_h = y_cnt*tile_h

    new_im = Image.new(&amp;quot;RGB&amp;quot;, (total_w, total_h))

    for x in range(x_cnt):
        for y in range(y_cnt):
            xt, yt = x + x1i, y + y1i
            im = Image.open(&amp;quot;maptiles/{}_{}_{}.png&amp;quot;.format(zoom, xt, yt))
            new_im.paste(im, (x*tile_w, y*tile_h))

    cropped_w = round((x2 - x1)*tile_w)
    cropped_h = round((y2 - y1)*tile_h)
    cropped_im = Image.new(&amp;quot;RGB&amp;quot;, (cropped_w, cropped_h))
    translate_x = round(-(x1 - x1i)*tile_w)
    translate_y = round(-(y1 - y1i)*tile_h)
    cropped_im.paste(new_im, (translate_x, translate_y))
    cropped_im.save(&amp;quot;map.png&amp;quot;)

# Download a map of the SF Bay Area at zoom level 12. Approx 3000*3000px.
download_map(12, 38, -122.7, 37.2, -121.7)

&lt;/pre&gt;</description><author>Allan's Blog</author><pubDate>Wed, 04 Aug 2021 07:28:32 GMT</pubDate><guid isPermaLink="true">https://allanrbo.blogspot.com/2021/08/download-openstreetmap-bounding-box-png.html</guid></item><item><title>Bye, Bye, Gmail</title><link>https://www.marginalia.nu/log/12-bye-bye-gmail/</link><description>&lt;p&gt;I finally got around to moving @marginalia.nu off gmail. I&amp;rsquo;ve been planning to do so for a while, and vascillated between self-hosting and using a provider. I ended up going for the latter. Even though I do like to self-host my stuff as much as possible, email servers seem like a lot of work. And I got a VPS account included in the price, which is nice. Means I can use it to do some off-site backups without having to use dropbox or similar.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 04 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/12-bye-bye-gmail/</guid></item><item><title>Dying, Every Day (Re: Last times)</title><link>https://www.marginalia.nu/log/11-dying-every-day/</link><description>&lt;p&gt;Dece&amp;rsquo;s post &amp;ldquo;last times&amp;rdquo; made me associate to one of my favorite thoughts from Roman philosopher Seneca, who was counting his days having fallen out of favor with Emperor Nero.&lt;/p&gt;
&lt;p&gt;In the first of his moral epistles to Lucilius, he asks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt; Quem mihi dabis [...] qui intellegat se cotidie mori?
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt; Who can you show me [...] that understands he is dying every day?
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;rsquo;s a fascinating and useful reversal of perspective. Our final day is not the day our life is suddenly taken away from us, but the last of our allotted days. Every preceding day has already been marked by our dying. Every day is a day that will never return, every moment is sand irreversibly flowing through the hour glass.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 04 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/11-dying-every-day/</guid></item><item><title>The Culture Map: Breaking Through the Invisible Boundaries of Global Business</title><link>https://olshansky.info/book/the_culture_map/</link><description>Olshansky's review of The Culture Map: Breaking Through the Invisible Boundaries of Global Business by Erin Meyer</description><author>🦉 olshansky 🦁</author><pubDate>Wed, 04 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/book/the_culture_map/</guid></item><item><title>What scientists must know about hardware to write fast code</title><link>https://viralinstruction.com/posts/hardware/</link><description>An introduction to the most essential elements of low-level computing and how it affects code performance.</description><author>viralinstruction</author><pubDate>Wed, 04 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://viralinstruction.com/posts/hardware/</guid></item><item><title>Copyleft is dying</title><link>https://jasoneckert.github.io/myblog/copyleft-is-dying/</link><description>&lt;p&gt;&lt;img alt="Copyleft" src="copyleft.jpg#right" title="Copyleft" /&gt;&lt;/p&gt;
&lt;p&gt;Software licensing is the catalyst that drives the modern tech industry. This is because reusable software code released under an &lt;em&gt;&lt;strong&gt;open source&lt;/strong&gt;&lt;/em&gt; license (permitting its reuse) is the norm for nearly all software development today. After all, if we couldn&amp;rsquo;t reuse existing code in new software projects, it would take far longer to bring technologies to market, and the overall software industry wouldn&amp;rsquo;t be able to evolve as quickly. Moreover, there are two major types of open source licenses.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Wed, 04 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/copyleft-is-dying/</guid></item><item><title>Just refuse books that expire</title><link>https://stop.zona-m.net/2021/08/just-refuse-books-that-expire/</link><description>&lt;p&gt;You just don&amp;rsquo;t want them, for you or anybody else.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 04 Aug 2021 02:59:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/just-refuse-books-that-expire/</guid></item><item><title>Frame, wtf?</title><link>https://river.me/blog/frame-wtf/</link><description>Frame objects in Scribunto can be confusing, and they do a lot of work. Normally I say &amp;ldquo;don&amp;rsquo;t worry, just copy the copyable code,&amp;rdquo; but here&amp;rsquo;s an indepth guide to everything they do.</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 03 Aug 2021 11:46:02 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/frame-wtf/</guid></item><item><title>Finland's intriguing take on the homelessness problem</title><link>https://nicolaiarocci.com/finlands-intriguing-take-on-the-homelessness-problem/</link><description>&lt;blockquote&gt;
&lt;p&gt;In Finland, the number of homeless people has fallen sharply. The reason: The
country applies the “Housing First” concept. Those affected by homelessness
receive a small apartment and counselling – without any preconditions. 4 out
of 5 people affected thus make their way back into a stable life. And: All
this is cheaper than accepting homelessness.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Finland&amp;rsquo;s &lt;a href="https://scoop.me/housing-first-finland-homelessness/"&gt;take on the homelessness problem&lt;/a&gt; is remarkable and gives hope.
I live in a small town where the problem is not as apparent as in, say, San
Francisco. Whenever I think back to my short time in San Francisco, I shudder
at the terrifying homelessness situation I met there. There, in San Francisco,
I realized the magnitude of &amp;ldquo;true liberalism&amp;rdquo;&amp;rsquo;s failure.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 03 Aug 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/finlands-intriguing-take-on-the-homelessness-problem/</guid></item><item><title>Hurricane Hill</title><link>https://sam.hooke.me/trip/2021/08/hurricane-hill/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Tue, 03 Aug 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/08/hurricane-hill/</guid></item><item><title>SvelteKit &amp;amp; Prisma - A match made in digital heaven</title><link>https://www.mootoday.com/blog/svelte-kit-prisma-a-match-made-in-digital-heaven</link><description>At some point,  your SvelteKit app needs to persist data. Learn how to use Prisma to achieve exactly that.</description><author>www.mootoday.com - RSS Feed</author><pubDate>Tue, 03 Aug 2021 06:06:14 GMT</pubDate><guid isPermaLink="true">https://www.mootoday.com/blog/svelte-kit-prisma-a-match-made-in-digital-heaven</guid></item><item><title>The Astrolabe Part II: The Magic Power of Sampling Bias</title><link>https://www.marginalia.nu/log/10-astrolabe-2-sampling-bias/</link><description>&lt;p&gt;As I have mentioned earlier, perhaps the biggest enemy of PageRank is the hegemony of PageRank-style algorithms. Once an algorithm like that becomes not only dominant, but known, it also creates a market for leveraging its design particulars.&lt;/p&gt;
&lt;p&gt;Homogenous ecosystems are almost universally bad. It doesn&amp;rsquo;t really matter if it&amp;rsquo;s every computer running Windows XP, or every farmer planting genetically identical barley, what you get is extreme susceptibility to exploitation.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Tue, 03 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/10-astrolabe-2-sampling-bias/</guid></item><item><title>Ode to Used Book Stores</title><link>https://lambdaland.org/posts/2021-08-03_used_books/</link><description>&lt;blockquote&gt;
&lt;p&gt;When I get a little money I buy books; and if any is left I buy food and clothes.&lt;br /&gt;
— Erasmus of Rotterdam&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Used bookstores are my arch nemesis.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Tue, 03 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-08-03_used_books/</guid></item><item><title>Seamless web analytics without Javascript</title><link>https://notes.npilk.com/analytics-without-javascript</link><description>Using GoAccess and shell scripts for easy, Javascript-free, and privacy-respecting site analytics.</description><author>npilk // Notes</author><pubDate>Tue, 03 Aug 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://notes.npilk.com/analytics-without-javascript</guid></item><item><title>Email as only way to cope with productivity madness</title><link>https://stop.zona-m.net/2021/08/email-as-only-way-to-cope-with-productivity-madness/</link><description>&lt;p&gt;One more proof that we can&amp;rsquo;t make it. Not for long, surely.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 02 Aug 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/08/email-as-only-way-to-cope-with-productivity-madness/</guid></item><item><title>Develop your first decentralized ecommerce application with Python Flask and MetaMask</title><link>https://blog.adnansiddiqi.me/develop-your-first-decentralized-ecommerce-application-with-python-flask-and-metamask/</link><description>&lt;p&gt;A Step By Step guide on how you can integrate Ethereum blockchain to accept ETH and other ERC20 tokens.&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/develop-your-first-decentralized-ecommerce-application-with-python-flask-and-metamask/"&gt;Develop your first decentralized ecommerce application with Python Flask and MetaMask&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Mon, 02 Aug 2021 15:32:24 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/develop-your-first-decentralized-ecommerce-application-with-python-flask-and-metamask/</guid></item><item><title>Rialto Beach</title><link>https://sam.hooke.me/trip/2021/08/rialto-beach/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Mon, 02 Aug 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/08/rialto-beach/</guid></item><item><title>Application Driven Environments</title><link>https://world.hey.com/sunjain/application-driven-environments-9fa2d1aa</link><description>&lt;div class="trix-content"&gt;
  &lt;div&gt;When we think of&amp;nbsp; environments from infrastructure point of view, we normally think in terms of its use case - Production, Test, and so forth.&amp;nbsp; It's probably more useful to think in terms of application, with collection of its components, flowing thru different environments. &lt;br /&gt;&lt;br /&gt;That is, if you have several applications, each with its own collection of components. And if we can have declarative representation of this collection, there are several benefits to it, which I will get to shortly.&amp;nbsp; Please note I am not talking about Google or Netflix scale here, I am talking about 99% of rest of use cases.&lt;br /&gt;&lt;br /&gt;In so far as dealing with environments is concerned, it's always important to think in terms of&amp;nbsp; applications first, then environment next.&amp;nbsp; Environments exist for application/s and not vice-versa. What good is an environment if the application is not functional?&lt;br /&gt;&lt;br /&gt;It's the application that moves between environments yet that relationship between its collection of dependencies pretty much remains the same - number of servers an application needs may change depending on if it is a test environment or production. In a perfect world every application component would be a service that can be developed and tested fully without needing anything else.. But we are talking reality that exist in most companies, most of the time. And not theoretical state only possible at Google or Amazon scale - and even then there is always dependency on core services.&amp;nbsp; And although possible to have some level of testing done using feature toggle but&amp;nbsp; when it comes to functional and integration testing required to satisfy business functionality, it typically requires other dependent services.&lt;br /&gt;&lt;br /&gt;Now there are plethora of ways to move just ONE application component from one environment to another - for example specifying it as docker image which moves from a laptop, to Kubernetes test cluster to production cluster. And of course CI/CD has been quite effective at this for more than a decade. This write-up is not about what is best way to implement CI/CD for a specific application component.&amp;nbsp; Nor this is about next Kubernetes setup. This also is not another write-up on how we can use service mesh like Istio to solve this problem. &lt;br /&gt;&lt;br /&gt;This is about why and how to define application and infrastructure dependencies declaratively. And how this collection, with its dependencies can be brought up and down seamlessly where application actually works.&amp;nbsp; This is the key - a &lt;em&gt;working application satisfying business functionality&lt;/em&gt;, when that collection, with its dependencies are brought up. This is true even when your entire application is serverless. And yes,&amp;nbsp; you can try and specify all of an application&amp;nbsp; dependencies via docker file to an extent -but it gets messy as dependencies increase.&lt;br /&gt;&lt;br /&gt;Even in a fully decoupled, totally independent microservices world, there always exists that relationship between application and core services it needs. Or to put it in another way, it’s that&amp;nbsp; collection of services that make&amp;nbsp; particular business function work. Assuming these services are fully decoupled (whether you have gone crazy making everything as service or your services are based on domains).&amp;nbsp; Most of the time, you will have collection of services which depend on base core services or on a monolith - this is the reality for 99% of us. Even there, application will have dependency on some core services like databases, Redis/Cache, Kafka, Gateway etc(these need to be up first, for the application to be functional).&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Granted database may not be brought up and down each time application collection is brought up/down but it can be tested that it is available before application is brought up. And this can only happen if that dependency is explicit somewhere.&lt;br /&gt;&lt;br /&gt;From infrastructure point of view, it is important to have this relationship defined explicitly, declaratively. Else it is in someone's head.. and probably in many heads. And each time you deal with the application, as a collection in an environment, the same heads will be needed to make that application functional.. Environments do not exist in isolation. Meaning it is better to think of Application X in an environment (production, development etc) instead of just environment. This has several benefits:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;When you bring up(or down) environment, you can work with a specific&amp;nbsp; application(and its collection) instead of all applications in your environment. Think of this like having "decoupled application sets or verticals"&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;This saves cost - you don't need to keep entire environment up if there is need to test only specific application, only its collection can be brought up as needed, else kept down&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;This saves time - since you are not bringing up/down entire environment, less troubleshooting, faster turnaround time - it is the troubleshooting and testing that takes most time, not actual startup of components. Less moving parts you have in an environment, less&amp;nbsp; troubleshooting.&amp;nbsp; I should put this also in cost bucket - since time is money&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;You provide ability for developers to bring up/down application, with collection of components(or just a subset of it) on as need basis - as opposed to having these up all the time. Very useful in a project or test environment&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Testing and keeping your disaster recovery site up-to-date and functional(instead of being surprised when you need one)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Institutional knowledge is made explicit in a simple format, easily understandable by new team member... or people unfamiliar with the system&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;The benefits are quite obvious. Next question is how best to capture and maintain this state with its collection and its dependent order. I really like the Wikidata repository format(&lt;a href="https://www.wikidata.org/wiki/Wikidata:Introduction"&gt;https://www.wikidata.org/wiki/Wikidata:Introduction&lt;/a&gt; ), and eventually&amp;nbsp; this collection can evolve to something similar to this format. Also&amp;nbsp; Gitlab's Mermaid markdown looks promising too. To begin with, it is best to start this in a very simple format which is both human readable and machine readable. Which can be stored in Git.&lt;br /&gt;&lt;br /&gt;For example, there can be one file per application. Each line in this file can indicate the application component name and its repos location. That is it. This can be a simple text file and dependency order (if any, can be reflected via a number or level or place in the file).&lt;br /&gt;&lt;br /&gt;It will be very tempting to go down the YAML, JSON or XML&amp;nbsp; or markup rabbit hole. Since readability for humans is equally important, it is probably better to represent this in simplest of text format. Even a markup language may be too much here...&lt;br /&gt;&lt;br /&gt;For example, let us say we have SALES application, which can be represented in a text file called SALES like so:&lt;br /&gt;&lt;br /&gt;#Level1#&lt;br /&gt;postgres:salesdb&lt;br /&gt;registry:main-registry&lt;br /&gt;#End Level1#&lt;br /&gt;&lt;br /&gt;#Level2#&lt;br /&gt;gateway:main-gateway&lt;br /&gt;engine:sales-target-engine&lt;br /&gt;backend:sales-backend&lt;br /&gt;#End Level2#&lt;br /&gt;&lt;br /&gt;#Level3#&lt;br /&gt;frontend1:sales-frontend1&lt;br /&gt;frontend2:saled-fronend2&lt;br /&gt;#End Level3#&lt;br /&gt;&lt;br /&gt;Here first field is application name and second field is Git repos name - this repos holds all information to bring up this component, along with its infrastructure. And Levels are dependency order - Level1 needs to be up first, for Level2 to be up and functional, and so on.&lt;br /&gt;&lt;br /&gt;It's quite obvious that to bring up entire environment for an application, along with its infrastructure, and dependencies down to every single resource, is too simplistic to be defined in simple text files in Git.&amp;nbsp; Here we are talking about bringing all these up(down) in specific order: Front-end and&amp;nbsp; Backend components, Cache/Redis, Gateway, Database etc, along with its infrastructure(hosts, containers etc).&amp;nbsp;&lt;br /&gt;And to represent all those relationships, and every resource they use in a particular dependency order, is probably too simplistic to store via text files in Git. All those resource level details, and relationships, can indeed be stored in a text file via Git. But that will make it practically useless for humans.&lt;br /&gt;&lt;br /&gt;Better to keep the format in Git text file real simple - something like shown above. And use that the as the seed data to represent all the details and relationship details in a database,&amp;nbsp; then do higher order automated operations using that data in the database. Make an API available to access this relationship in database (seeded from Git) to operate at environment level.&lt;br /&gt;&lt;br /&gt;Our eventual goal is this – command level ability to spin up(or tear down) desired application, with all its dependencies. Something like this:&lt;br /&gt;&lt;br /&gt;"&amp;gt;service_application_suite&amp;nbsp; SALES TEST UP"&amp;nbsp; &amp;lt;- this brings up entire SALES application, along with its entire dependent application components and infrastructure in TEST environment&lt;br /&gt;"&amp;gt;service_application_suite&amp;nbsp; CART QA DOWN"&amp;nbsp; &amp;lt;- this brings down entire CART application, along with its entire dependent application components and infrastructure in QA environment&lt;br /&gt;&lt;br /&gt;It is implicit that a production version of this command would never /rarely be. used. But test, qa and project version of this would be very beneficial - where subset of application and its components can be brought up/down quickly - as explained in the benefits above.&lt;/div&gt;
&lt;/div&gt;</description><author>Sunil Jain</author><pubDate>Mon, 02 Aug 2021 07:51:20 GMT</pubDate><guid isPermaLink="true">https://world.hey.com/sunjain/application-driven-environments-9fa2d1aa</guid></item><item><title>Enabling graceful node shutdown on EKS in Kubernetes 1.21</title><link>https://www.skouf.com/posts/enabling-graceful-node-shutdown-on-eks-in-kubernetes-1-21/</link><description>Kubernetes 1.21 supports graceful node shutdown, for gracefully terminating pods when a node is shutting down. This post explains how to configure this feature on EKS.</description><author>skouf.com</author><pubDate>Mon, 02 Aug 2021 03:58:34 GMT</pubDate><guid isPermaLink="true">https://www.skouf.com/posts/enabling-graceful-node-shutdown-on-eks-in-kubernetes-1-21/</guid></item><item><title>Anxious People</title><link>https://arunmani.in/library/anxious-people/</link><description>A refreshing tale of our human lives.</description><author>Arun Mani</author><pubDate>Mon, 02 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/anxious-people/</guid></item><item><title>Hyperbole and a Half</title><link>https://arunmani.in/library/hyperbole-and-a-half/</link><description>Beautiful narration of various scenarios.</description><author>Arun Mani</author><pubDate>Mon, 02 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/hyperbole-and-a-half/</guid></item><item><title>Prefer UUID for your Primary Key</title><link>https://backendhance.com/blog/2021/uuid-primary-key/</link><description>&lt;p&gt;In my &lt;a href="https://backendhance.com/en/blog/2021/numerical-id-consequence/"&gt;last post&lt;/a&gt;
I discussed the downsides of using numerical types
for the primary key of an entity. We should avoid these issues all together by using &lt;code&gt;UUID&lt;/code&gt; instead and in this post I
will discuss the up- and downsides of this approach.&lt;/p&gt;
&lt;p&gt;Using a UUID as the primary ID is simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-kotlin"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Flat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@Id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="generate-the-id-on-the-application"&gt;
&lt;a class="anchor" href="#generate-the-id-on-the-application"&gt;Generate the ID on the application&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Similar to numerical ids we can generate it on the database as well. But for &lt;code&gt;UUID&lt;/code&gt; this is not necessary. The reason we
did this in the first place is that we needed to make sure that an ID is not used twice. A collision of &lt;code&gt;UUID&lt;/code&gt; is very
unlikely to occur.&lt;/p&gt;</description><author>Backendhance</author><pubDate>Mon, 02 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://backendhance.com/blog/2021/uuid-primary-key/</guid></item><item><title>Blizzard is dead to me.</title><link>https://benovermyer.com/blog/2021/08/blizzard-is-dead-to-me/</link><description>&lt;p&gt;The last three years has been a slow, awful death spiral for Blizzard. I don't know where all the trouble started, though I suspect Activision played a part in it. I have been playing Blizzard games since the mid-1990s, so with over two decades spent in their creations, I'm pretty sad to let it all go. I still enjoyed playing Overwatch for several hours a day.&lt;/p&gt;
&lt;p&gt;But now, it's gotten to be too much. The proverbial straw has shattered the camel's spine.&lt;/p&gt;
&lt;p&gt;I uninstalled Overwatch and the Blizzard launcher. They will never return to my computer unless something truly drastic happens to fix the current state of the company.&lt;/p&gt;
&lt;p&gt;Off I go to... something else. Not sure what yet. But something.&lt;/p&gt;</description><author>Ben Overmyer's Site</author><pubDate>Mon, 02 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://benovermyer.com/blog/2021/08/blizzard-is-dead-to-me/</guid></item><item><title>Københavns Luftkvalitet og Cykling</title><link>https://thomascountz.com/2021/08/02/copenhagen-air</link><description>An exploration of Copenhagen's air quality data through the lens of urban cycling. Using Python, GeoPandas, and the Google Maps API, this analysis examines black carbon, nitrogen dioxide, and ultrafine particle measurements collected by Google Street View vehicles across the city. Includes geospatial data processing, route-based pollution exposure calculations, and visualization of air quality correlations along cycling paths.</description><author>Thomas Countz</author><pubDate>Mon, 02 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://thomascountz.com/2021/08/02/copenhagen-air</guid></item><item><title>Building Developer Tools</title><link>https://explog.in/notes/devtools/index.html</link><description>&lt;p&gt;
    After spending a large part of my career building tools, I realized I
    had unconsciously followed a consistent framework for
    identifying and building developer tools. It seemed like a good idea
    to make it concrete -- both for sharing and collecting feedback --
    leading to this series of posts.
    &lt;/p&gt;

    &lt;p&gt;
    I've been tweaking this series for a few months at this point, and
    it's really exciting to finally call it done (for now).
    &lt;/p&gt;</description><author>RSS</author><pubDate>Mon, 02 Aug 2021 02:46:00 GMT</pubDate><guid isPermaLink="true">https://explog.in/notes/devtools/index.html</guid></item><item><title>Not everyone can be an entrepreneur</title><link>https://javiergarmon.com/not-everyone-can-be-an-entrepreneur/</link><description>&lt;p&gt;Do you want to be your own boss? Or achieve the desired economic freedom? Or perhaps change the world? These are totally respectable attitudes, but they define a narcissist much better than an entrepreneur.&lt;/p&gt;&lt;p&gt;An entrepreneur is someone in love with a problem to solve. Maybe they are an expert&lt;/p&gt;</description><author>Javier Garmon</author><pubDate>Sun, 01 Aug 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://javiergarmon.com/not-everyone-can-be-an-entrepreneur/</guid></item><item><title>Book Review: Turn the Ship Around</title><link>https://james-carr.org/posts/turn-the-ship-around/</link><description>Turn the Ship Around!: A True Story of Turning Followers into Leaders is a book written by David Marquet, a retired submarine commander in the United States Navy. The book details Marquet&amp;rsquo;s experience of turning a failing submarine crew into a highly effective team by implementing a leader-leader model instead of the traditional leader-follower model. My spouse, Than, picked this book up a few years back as part of a large collection of management books to help the shift from engineer to engineering management and I finally got around to ready it.</description><author>James Carr</author><pubDate>Sun, 01 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://james-carr.org/posts/turn-the-ship-around/</guid></item><item><title>Five Minute Journal</title><link>https://briansunter.com/five-minute-journal</link><description>A quick daily practice with prompts for gratitude, intentions, and reflection.</description><author>Brian Sunter</author><pubDate>Sun, 01 Aug 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://briansunter.com/five-minute-journal</guid></item><item><title>Cognitive Confinement In Narrative Prisons</title><link>https://venam.net/blog/psychology/2021/08/01/cognitive-diversity.html</link><description>After writing a whole book about internet and narrative control I want to share what I personally do, in my own limited ways, to try not to get stuck in bubbles. The book covers a lot of ground on this used up road, especially in the last part, but let's be practical instead. This isn't meant to be didactic but only informative of my reflection on how I attempt to achieve this and the issues I've encountered.</description><author>Venam's Blog — Patrick Louis (Lebanon)</author><pubDate>Sun, 01 Aug 2021 00:00:00 GMT</pubDate><guid isPermaLink="true">https://venam.net/blog/psychology/2021/08/01/cognitive-diversity.html</guid></item><item><title>August</title><link>https://gyani.net/blog/august/</link><description>.. 8th month of the year - 
		  Okay August is here, 5 months left. This is where the weight story turns around I hope. Retrospective Running: I wanted to run 150k I ran 150k. ✅ Reading: I wanted to finish 4 parts of Musashi, I did. I also finished Project Hail Mary by Andy Weir. ✅ Weight:...</description><author>Gyanendra Mishra</author><pubDate>Sat, 31 Jul 2021 21:30:00 GMT</pubDate><guid isPermaLink="true">https://gyani.net/blog/august/</guid></item><item><title>Looking between the lines: how media is control.</title><link>https://apurva-shukla.me/blog/media-and-propaganda/</link><description>Over the past few years, we’ve seen populism rise across the globe. Underpinning each of these movements is this gnawing feeling that the…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Sat, 31 Jul 2021 12:42:58 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/blog/media-and-propaganda/</guid></item><item><title>Porting Takua Renderer to 64-bit ARM- Part 2</title><link>https://blog.yiningkarlli.com/2021/07/porting-takua-to-arm-pt2.html</link><description>&lt;p&gt;&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;

&lt;div class="tableofcontents"&gt;
    &lt;div class="tableofcontents-row"&gt;
        &lt;div class="tableofcontents-column2"&gt;
            &lt;div class="tableofcontents-content"&gt;
                1. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-introduction"&gt;Introduction&lt;/a&gt;&lt;br /&gt;
                2. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-porting-to-arm64-macos"&gt;Porting to arm64 macOS&lt;/a&gt;&lt;br /&gt;
                3. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-universal-binaries"&gt;Universal Binaries&lt;/a&gt;&lt;br /&gt;
                4. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-rosetta2"&gt;Rosetta 2: Running x86-64 on Apple Silicon&lt;/a&gt;&lt;br /&gt;
                5. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-tso-memory-ordering"&gt;TSO Memory Ordering on the M1 Processor&lt;/a&gt;&lt;br /&gt;
                6. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-embree-on-arm64-using-sse2neon"&gt;Embree on arm64 using sse2neon&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column2"&gt;
            &lt;div class="tableofcontents-content"&gt;
                7. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-more-differences-in-arm64-vs-x86-64"&gt;(More) Differences in arm64 versus x86-64&lt;/a&gt;&lt;br /&gt;
                8. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-perftesting"&gt;(More) Performance Testing&lt;/a&gt;&lt;br /&gt;
                9. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-conclusion"&gt;Conclusion to Part 2&lt;/a&gt;&lt;br /&gt;
                10. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-acknowledgements"&gt;Acknowledgements&lt;/a&gt;&lt;br /&gt;
                11. &lt;a href="/2021/07/porting-takua-to-arm-pt2#2021-07-31-references"&gt;References&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div id="2021-07-31-introduction"&gt;&lt;/div&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;This post is the second half of my two-part series about how I ported my hobby renderer (Takua Renderer) to 64-bit ARM and what I learned from the process.
In the &lt;a href="https://blog.yiningkarlli.com/2021/05/porting-takua-to-arm-pt1.html"&gt;first part&lt;/a&gt;, I wrote about my motivation for undertaking a port to arm64 in the first place and described the process I took to get Takua Renderer up and running on an arm64-based Raspberry Pi 4B.
I also did a deep dive into several topics that I ran into along the way, which included floating point reproducibility across different processor architectures, a comparison of arm64 and x86-64’s memory reordering models, and a comparison of how the same example atomic code compiles down to assembly in arm64 versus in x86-64.
In this second part, I’ll write about developments and lessons learned after I got my initial arm64 port working correctly on Linux.&lt;/p&gt;

&lt;p&gt;We’ll start with how I got Takua Renderer up and running on arm64 macOS, and discuss various interesting aspects of arm64 macOS, such as Universal Binaries and Apple’s Rosetta 2 binary translation layer for running x86-64 binaries on arm64 macOS.
As noted in the first part of this series, my initial port of Takua Renderer to arm64 did not include Embree; after the initial port, I added Embree support using Syoyo Fujita’s embree-aarch64 project (which has since been superseded by official arm64 support in Embree v3.13.0).
In this post I’ll look into how Embree, a codebase containing tons of x86-64 assembly and SSE and AVX intrinsics, was ported to arm64.
I will also use this exploration of Embree as a lens through which to compare x86-64’s SSE vector extensions to arm64’s Neon vector extensions.
Finally, I’ll wrap up with some additional important details to keep in mind when writing portable code between x86-64 and arm64, and I’ll also provide some more performance comparisons featuring the Apple M1 processor.&lt;/p&gt;

&lt;div id="2021-07-31-porting-to-arm64-macos"&gt;&lt;/div&gt;
&lt;h2 id="porting-to-arm64-macos"&gt;Porting to arm64 macOS&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/takua_macos_arm64.jpg"&gt;&lt;img alt="Figure 1: Takua Renderer running on arm64 macOS 11, on an Apple Silicon Developer Transition Kit." src="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/takua_macos_arm64.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At WWDC 2020 last year, Apple announced that Macs would be transitioning from using x86-64 processors to using custom Apple Silicon chips over a span of two years.
Apple Silicon chips package together CPU cores, GPU cores, and various other coprocessors and controllers onto a single die; the CPU cores implement arm64.
Actually, Apple Silicon implements a &lt;em&gt;superset&lt;/em&gt; of arm64; there are some interesting extra special instructions that Apple has added to their arm64 implementation, which I’ll get to a bit later.
Similar to how Apple provided developers with preview hardware during the previous Mac transition from PowerPC to x86, Apple also announced that for this transition they would be providing Developer Transition Kits (DTKs) to developers in the form of special Mac Minis based on the iPad Pro’s A12Z chip.
I had been anticipating a Mac transition to arm64 for some time, so I ordered a Developer Transition Kit as soon as they were made available.&lt;/p&gt;

&lt;p&gt;Since I had already gotten Takua Renderer up and running on arm64 on Linux, getting Takua Renderer up and running on the Apple Silicon DTK was very fast!
By far the most time consuming part of this process was just getting developer tooling set up and getting Takua’s dependencies built; once all of that was done, building and running Takua basically Just Worked™.
The only reason that getting developer tooling set up and getting dependencies built took a bit of work at the time was because this was just a week and a half after the entire Mac arm64 transition had even been announced.&lt;/p&gt;

&lt;p&gt;Interestingly, the main stumbling block I ran into for most things on Apple Silicon macOS wasn’t the change to arm64 under the hood at all; the main stumbling block was… the macOS version number!
For the past 20 years, modern macOS (or Mac OS X as it was originally named) has used 10.x version numbers, but the first version of macOS to support arm64, macOS Big Sur, bumps the version number to 11.x.
This version number bump threw off a surprising number of libraries and packages!
Takua’s build system uses &lt;a href="https://cmake.org"&gt;CMake&lt;/a&gt; and &lt;a href="https://ninja-build.org"&gt;Ninja&lt;/a&gt;, and on macOS I get CMake and Ninja through &lt;a href="https://www.macports.org"&gt;MacPorts&lt;/a&gt;.
At the time, a lot of stuff in MacPorts wasn’t expecting an 11.x version number, so a bunch of stuff wouldn’t build, but fixing all of this just required manually patching build scripts and portfiles to expect an 11.x version number.
All of this pretty much got fixed within weeks of DTKs shipping out (and Apple actually contributed a huge number of patches themselves to various projects and stuff), but I didn’t want to wait at the time, so I just charged ahead.&lt;/p&gt;

&lt;p&gt;Only three of Takua’s dependencies needed some minor patching to get working on arm64 macOS: &lt;a href="https://github.com/oneapi-src/oneTBB"&gt;TBB&lt;/a&gt;, &lt;a href="https://github.com/AcademySoftwareFoundation/openexr"&gt;OpenEXR&lt;/a&gt;, and &lt;a href="https://github.com/wdas/ptex"&gt;Ptex&lt;/a&gt;.
TBB’s build script just had to be updated to detect arm64 as a valid architecture for macOS; I submitted a pull request for this fix to the TBB Github repo, but I guess Intel doesn’t really take pull requests for TBB.
It’s okay though; the fix has since shown up in newer releases of TBB.
OpenEXR ‘s build script had to be patched so that inlined AVX intrinsics wouldn’t be used when building for arm64 on macOS; I submitted a pull request for this fix to OpenEXR that got merged, although this fix was later rendered unnecessary by a fix in the final release of Xcode 12.
Finally, Ptex just needed an extra include to pick up the &lt;code class="language-plaintext highlighter-rouge"&gt;unlink()&lt;/code&gt; system call correctly from &lt;code class="language-plaintext highlighter-rouge"&gt;unistd.h&lt;/code&gt; on macOS 11.
This change in Ptex was needed going from macOS Catalina to macOS Big Sur, and it’s also merged into the mainline Ptex repository now.&lt;/p&gt;

&lt;p&gt;Once I had all of the above out of the way, getting Takua Renderer itself building and running correctly on the Apple Silicon DTK took no time at all, thanks to my previous efforts to port Takua Renderer to arm64 on Linux.
At this point I just ran &lt;code class="language-plaintext highlighter-rouge"&gt;cmake&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ninja&lt;/code&gt; and a minute later out popped a working build.
From the moment the DTK arrived on my doorstep, I only needed about five hours to get Takua Renderer’s arm64 version building and running on the DTK with all tests passing.
Considering that at that point, outside of Apple nobody had done any work to get anything ready yet, I was very pleasantly surprised that I had everything up and working in just five hours!
Figure 1 is a screenshot of Takua Renderer running on arm64 macOS Big Sur Beta 1 on the Apple Silicon DTK.&lt;/p&gt;

&lt;div id="2021-07-31-universal-binaries"&gt;&lt;/div&gt;
&lt;h2 id="universal-binaries"&gt;Universal Binaries&lt;/h2&gt;

&lt;p&gt;The Mac has now had three processor architecture migrations in its history; the Mac line began in 1984 based on Motorola 68000 series processors, transitioned from the 68000 series to PowerPC in 1994, transitioned again from PowerPC to x86 (and eventually x86-64) in 2006, and is now in the process of transitioning from x86-64 to arm64.
Apple has used a similar strategy in all three of these processor architecture migrations to smooth the process.
Apple’s general transition strategy consists of two major components: first, provide a “fat” binary format that packages code from both architectures into a single executable that can run on both architecture, and second, provide some way for binaries from the old architecture to run directly on the new architecture.
I’ll look into the second part of this strategy a bit later; in this section, we are interested in Apple’s fat binary format.
Apple calls their fat binary format Universal Binaries; specifically, Apple uses the name “Universal 2 “for the transition to arm64 since the original Universal Binary format was for the transition to x86.&lt;/p&gt;

&lt;p&gt;Now that I had separate x86-64 and arm64 builds working and running on macOS, the next step was to modify Takua’s build system to automatically produce a single Universal 2 binary that could run on both Intel and Apple Silicon Macs.
Fortunately, creating Universal 2 binaries is very easy!
To understand why creating Universal 2 binaries can be so easy, we need to first understand at a high level how a Universal 2 binary works.
There actually isn’t much special about Universal 2 binaries per se, in the sense that multi-architecture support is actually an inherent feature of the Mach-O binary executable code file format that Apple’s operating systems all use.
A multi-architecture Mach-O binary begins with a header that declares the file as a multi-architecture file and declares how many architectures are present.
The header is immediately followed by a list of architecture “slices”; each slice is a struct describing some basic information, such as what processor architecture the slice is for, the offset in the file that instructions begin at for the slice, and so on &lt;a href="https://eclecticlight.co/2020/07/28/universal-binaries-inside-fat-headers/"&gt;[Oakley 2020]&lt;/a&gt;.
After the list of architecture slices, the rest of the Mach-O file is pretty much like normal, except each architecture’s segments are concatenated after the previous architecture’s segments.
Also, Mach-O’s multi-architecture support allows for sharing non-executable resources between architectures.&lt;/p&gt;

&lt;p&gt;So, because Universal 2 binaries are really just Mach-O multi-architecture binaries, and because Mach-O multi-architecture binaries don’t do any kind of crazy fancy interleaving and instead just concatenate each architecture after the previous one, all one needs to do to make a Universal 2 binary out of separate arm64 and x86-64 binaries is to concatenate the separate binaries into a single Mach-O file and set up the multi-architecture header and slices correctly.
Fortunately, &lt;a href="https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary"&gt;a lot of tooling exists&lt;/a&gt; to do exactly the above!
The version of clang that Apple ships with Xcode natively supports building Universal Binaries by just passing in multiple &lt;code class="language-plaintext highlighter-rouge"&gt;-arch&lt;/code&gt; flags; one for each architecture.
The Xcode UI of course also supports building Universal 2 binaries by just adding x86-64 and arm64 to an Xcode project’s architectures list in the project’s settings.
For projects using CMake, CMake has a &lt;code class="language-plaintext highlighter-rouge"&gt;CMAKE_OSX_ARCHITECTURES&lt;/code&gt; flag; this flag defaults to whatever the native architecture of the current system is, but can be set to &lt;code class="language-plaintext highlighter-rouge"&gt;x86_64;arm64&lt;/code&gt; to enable Universal Binary builds.
Finally, since the PowerPC to Intel transition, macOS has included a tool called lipo, which is used to query and create Universal Binaries; I’m fairly certain that the macOS lipo tool is based on the &lt;a href="https://llvm.org/docs/CommandGuide/llvm-lipo.html"&gt;llvm-lipo tool&lt;/a&gt; that is part of the larger LLVM compiler project.
The lipo tool can combine any x86_64 Mach-O file with any arm64 Mach-O file to create a multi-architecture Universal Binary.
The lipo tool can also be used to “slim” a Universal Binary down into a single architecture by deleting architecture slices and segments from the Universal Binary.&lt;/p&gt;

&lt;p&gt;Of course, when building a Universal Binary, any external libraries that have to be linked in also need to be Universal Binaries.
Takua has a relatively small number of direct dependencies, but unfortunately some of Takua’s dependencies pull in many more indirect (relative to Takua) dependencies; for example, Takua depends on &lt;a href="https://www.openvdb.org"&gt;OpenVDB&lt;/a&gt;, which in turn pulls in &lt;a href="https://github.com/Blosc/c-blosc"&gt;Blosc&lt;/a&gt;, &lt;a href="https://www.zlib.net"&gt;zlib&lt;/a&gt;, &lt;a href="https://www.boost.org"&gt;Boost&lt;/a&gt;, and several other dependencies.
While some of these dependencies are built using CMake and are therefore very easy to build as Universal Binaries themselves, some other dependencies use older or bespoke build systems that can be difficult to retrofit multi-architecture builds into.
Fortunately, this problem is where the lipo tool comes in handy.
For dependencies that can’t be easily built as Universal Binaries, I just built arm64 and x86-64 versions separately and then combined the separate builds into a single Universal Binary using the lipo tool.&lt;/p&gt;

&lt;p&gt;Once all of Takua’s dependencies were successfully built as Universal Binaries, all I had to do to get Takua itself to build as a Universal Binary was to add a check in my CMakeLists file to not use a couple of x86-64-specific compiler flags in the event of an arm64 target architecture.
Then I just set the &lt;code class="language-plaintext highlighter-rouge"&gt;CMAKE_OSX_ARCHITECTURES&lt;/code&gt; flag to &lt;code class="language-plaintext highlighter-rouge"&gt;x86_64;arm64&lt;/code&gt;, ran &lt;code class="language-plaintext highlighter-rouge"&gt;ninja&lt;/code&gt;, and out came a working Universal Binary!
Figure 2 shows building Takua Renderer, checking that the current system architecture is an Apple Silicon Mac, using the lipo tool to see and confirm that the output Universal Binary contains both arm64 and x86-64 slices, and finally try running the Universal Binary Takua Renderer build:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/universalbinary.png"&gt;&lt;img alt="Figure 2: Building Takua Renderer as a Universal Binary, checking the current system architecture, checking the output Universal Binary's slices to confirm the presence of arm64 and x86-64 support, and finally running Takua Renderer from the Universal Binary build." src="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/universalbinary.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Out of curiosity, I also tried creating separate x86-64-only and arm64-only builds of Takua and assembling them into a Universal Binary using the lipo tool and comparing the result with the build of Takua that was natively built as a Universal Binary.
In theory natively building as a Universal Binary should be able to produce a slightly more compact output binary compared with using the lipo tool, since a natively built Universal Binary should be able to share non-code resources between different architectures, whereas the lipo tool just blindly encapsulates two separate Mach-O files into a single multi-architecture Mach-O file.
In fact, you can actually use the lipo tool to combine completely different programs into a single Universal Binary; after all, lipo has absolutely no way of knowing whether or not the arm64 and x86-64 code you want to combine is actually even from the same source code or implements the same functionality.
Indeed, the native Universal Binary Takua is slightly smaller than the lipo-generated Universal Binary Takua.
The size difference is tiny (basically negligible) though, likely because Takua’s binary contains very few non-code resources.
Figure 3 shows creating a Universal Binary by combining separate x86-64 and arm64 builds of Takua together using the lipo tool versus a Universal Binary built natively as a Universal Binary; the lipo version is just a bit over a kilobyte larger than the native version, which is negligible relative to the overall size of the files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/lipocomparison.png"&gt;&lt;img alt="Figure 3: Examining the size of a Universal Binary created using the lipo tool versus the size of a Universal Binary built directly as a multi-architecture Mach-O." src="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/lipocomparison.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div id="2021-07-31-rosetta2"&gt;&lt;/div&gt;
&lt;h2 id="rosetta-2-running-x86-64-on-apple-silicon"&gt;Rosetta 2: Running x86-64 on Apple Silicon&lt;/h2&gt;

&lt;p&gt;While getting Takua Renderer building and running as a native arm64 binary on Apple Silicon only took me about five hours, actually running Takua for the first time in &lt;em&gt;any&lt;/em&gt; form on Apple Silicon happened much faster!
Before I did anything to get Takua’s arm64 build up and running on my Apple Silicon DTK, the first thing I did was just copy over the x86-64 macOS build of Takua to see if it would run on Apple Silicon macOS through Apple’s dynamic binary translation layer, Rosetta 2.
I was very impressed to find that the x86-64 version of Takua just worked out-of-the-box through Rosetta 2, and even passed my entire test suite!
I have now had Takua’s native arm64 build up and running as part of a Universal 2 binary for around a year, but I recently circled back to examine how Takua’s x86-64 build works through Rosetta 2.
I wanted to get a rough idea of how Rosetta 2 works, and much like many of the detours that I took on the entire Takua arm64 journey, I stumbled into a good opportunity to compare x86-64 and arm64 and learn more about how the two are similar and how they differ.&lt;/p&gt;

&lt;p&gt;For every processor architecture transition that the Mac had undertaken, Apple has provided some sort of mechanism to run binaries for the outgoing processor architecture on Macs based on the new architecture.
During the 68000 to PowerPC transition, Apple’s approach was to emulate an entire 68000 system at the lowest levels of the operating system on PowerPC; in fact, during this transition, PowerPC Macs even allowed 68000 and PowerPC code to call back and forth to each other and be interspersed within the same binary.
During the PowerPC to x86 transition, Apple introduced Rosetta, which worked by JIT-compiling blocks of PowerPC code into x86 on-the-fly at program runtime.
For the x86-64 to arm64 transition, Rosetta 2 follows in the same tradition as in the previous two architecture transitions.
Rosetta 2 has two modes: the first is an ahead-of-time recompiler that converts an entire x86-64 binary to arm64 upon first run of an x86-64 binary and caches the translated binary for later reuse.
The second mode Rosetta 2 has is a JIT translator, which is used for cases where the target program itself is also JIT-generating x86-64 code; obviously in these cases the target program’s JIT output cannot be recompiled to arm64 through an ahead-of-time process.&lt;/p&gt;

&lt;p&gt;Apple does not publicly provide much information at all about how Rosetta 2 works under the hood.
Rosetta 2 is one of those pieces of Apple technology that basically “Just Works” well enough that the typical user never really has any need to know much about how it works internally, which is great for users but unfortunate for anyone that is more curious.
Fortunately though, Koh Nakagawa recently published &lt;a href="https://ffri.github.io/ProjectChampollion/"&gt;a detailed analysis of Rosetta 2&lt;/a&gt; produced through some careful reverse engineering work.
What I was interested in examining was how Rosetta 2’s output arm64 assembly looks compared with natively compiled arm64 assembly, so I’ll briefly summarize the relevant parts of how Rosetta 2 generates arm64 code.
There’s a lot more cool stuff about Rosetta 2, such as how the runtime and JIT mode works, that I won’t touch on here; if you’re interested, I’d highly recommend checking out Koh Nakagawa’s writeups.&lt;/p&gt;

&lt;p&gt;When a user tries to run an x86-64 binary on an Apple Silicon Mac, Rosetta 2 first checks if this particular binary has already been translated by Rosetta 2 before; Rosetta 2 does this through a system daemon called &lt;code class="language-plaintext highlighter-rouge"&gt;oahd&lt;/code&gt;.
If Rosetta 2 has never encountered this particular binary before, &lt;code class="language-plaintext highlighter-rouge"&gt;oahd&lt;/code&gt; kicks off a new process called &lt;code class="language-plaintext highlighter-rouge"&gt;oahd-helper&lt;/code&gt; that carries out the ahead-of-time (AOT) binary translation process and caches the result in a folder located at &lt;code class="language-plaintext highlighter-rouge"&gt;/var/db/oah&lt;/code&gt;; cached AOT arm64 binaries are stored in subfolders named using a SHA-256 hash calculated from the contents and path of the original x86-64 binary.
If Rosetta 2 has encountered a binary before, as determined by finding an SHA-256 hash collision in &lt;code class="language-plaintext highlighter-rouge"&gt;/var/db/oah&lt;/code&gt;, then &lt;code class="language-plaintext highlighter-rouge"&gt;oahd&lt;/code&gt; just loads the cached AOT binary from before.&lt;/p&gt;

&lt;p&gt;So what do these cached AOT binaries look like?
Unfortunately, &lt;code class="language-plaintext highlighter-rouge"&gt;/var/db/oah&lt;/code&gt; is by default not accessible to users at all, not even admin and root users.
Fortunately, like with all protected components of macOS, access can be granted by disabling System Integrity Protection (SIP).
I don’t recommend disabling SIP unless you have a very good reason to, since SIP is designed to protect core macOS files from getting damaged or modified, but for this exploration I temporarily disabled SIP just long enough to take a look in &lt;code class="language-plaintext highlighter-rouge"&gt;/var/db/oah&lt;/code&gt;.
Well, it turns out that the cached AOT binaries are just regular-ish arm64 Mach-O files named with an &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; extension; I say “regular-ish” because while the &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; files are completely normal Mach-O binaries, they cannot actually be executed on their own.
Attempting to directly run a &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; binary results in an immediate &lt;code class="language-plaintext highlighter-rouge"&gt;SIGKILL&lt;/code&gt;.
Instead, &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; binaries must be loaded by the Rosetta 2 runtime and require some special memory mapping to run correctly.
But that’s fine; I wasn’t interested in running the &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; file, I was interested in learning what it looks like inside, and since the &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; file is a Mach-O file, we can disassemble &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; files just like any other Mach-O file.&lt;/p&gt;

&lt;p&gt;Let’s go through a simple example to compare how the same piece of C++ code compiles to arm64 natively, versus what Rosetta 2 generates from a x86-64 binary.
The simple example C++ code I’ll use here is the same basic atomic float addition implementation that I wrote about in my previous post; since that post already contains an exhaustive analysis of how this example compiles to both x86-64 and arm64 assembly, I figure that means I don’t need to go over all of that again and can instead dive straight into the Rosetta 2 comparison.
To make an actually executable binary though, I had to wrap the example &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; function in a simple &lt;code class="language-plaintext highlighter-rouge"&gt;main()&lt;/code&gt; function:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;#include &amp;lt;atomic&amp;gt;

float addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp; f0, const float f1) {
    do {
        float oldval = f0.load();
        float newval = oldval + f1;
        if (f0.compare_exchange_weak(oldval, newval)) {
            return oldval;
        }
    } while (true);
}

int main() {
    std::atomic&amp;lt;float&amp;gt; t(0);
    addAtomicFloat(t, 1.0f);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 1: Example &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; implementation and a very simple &lt;code class="language-plaintext highlighter-rouge"&gt;main()&lt;/code&gt; function to make a executable program. The &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; implementation is the same one from &lt;a href="https://blog.yiningkarlli.com/2021/05/porting-takua-to-arm-pt1.html#listing2"&gt;Listing 2 in my previous "Porting Takua Renderer to 64-bit ARM- Part 1&lt;/a&gt;" post.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Modern versions of macOS’s Xcode Command Line Tools helpfully come with both otool and with &lt;a href="https://llvm.org/docs/CommandGuide/llvm-objdump.html"&gt;LLVM’s version of objdump&lt;/a&gt;, both of which can be used to disassembly Mach-O binaries.
For this exploration, I used otool to disassemble arm64 binaries and objdump to disassembly x86-64 binaries.
I used different tools for disassembling x86-64 versus arm64 because of slightly different feature sets that I needed on each platform.
By default, Apple’s version of Clang uses newer ARMv8.1-A instructions like &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt;.
However, the version of objdump that Apple ships with the Xcode Command Line Tools only seems to support base ARMv8-a and doesn’t understand newer ARMv8.1-A instructions like &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt;, whereas otool does seem to know about ARMv8.1 instructions, hence using otool for arm64 binaries.
For x86-64 binaries, however, otool outputs x86-64 assembly using AT&amp;amp;T syntax, whereas I prefer reading x86-64 assembly in Intel syntax, which matches what &lt;a href="https://godbolt.org"&gt;Godbolt Compiler Explorer&lt;/a&gt; defaults to.
So, for x86-64 binaries, I used objdump, which can be set to output x86-64 assembly using Intel syntax with the &lt;code class="language-plaintext highlighter-rouge"&gt;-x86-asm-syntax=intel&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;On both x86-64 and on arm64, I compiled the example in Listing 1 using the default Clang that comes with Xcode 12.5.1, which reports its version string as “Apple clang version 12.0.5 (clang-1205.0.22.11)”.
Note that Apple’s Clang version numbers have nothing to do with mainline upstream Clang version numbers; according to &lt;a href="https://en.wikipedia.org/wiki/Xcode#12.x_series"&gt;this table on Wikipedia&lt;/a&gt;, “Apple clang version 12.0.5” corresponds roughly with mainline LLVM/Clang 11.1.0.
Also, I compiled using the &lt;code class="language-plaintext highlighter-rouge"&gt;-O3&lt;/code&gt; optimization flag.&lt;/p&gt;

&lt;p&gt;Disassembling the x86-64 binary using &lt;code class="language-plaintext highlighter-rouge"&gt;objdump -disassemble -x86-asm-syntax=intel&lt;/code&gt; produces the following x86-64 assembly.
I’ve only included the assembly for the &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; function and not the assembly for the dummy &lt;code class="language-plaintext highlighter-rouge"&gt;main()&lt;/code&gt; function.
For readability, I have also replaced the offset for the &lt;code class="language-plaintext highlighter-rouge"&gt;jne&lt;/code&gt; instruction with a more readable label and added the label into the correct place in the assembly code:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&amp;lt;__Z14addAtomicFloatRNSt3__16atomicIfEEf&amp;gt;:     # f0 is dword ptr [rdi], f1 is xmm0
        push          rbp                      # save address of previous stack frame
        mov           rbp, rsp                 # move to address of current stack frame
        nop           word ptr cs:[rax + rax]  # multi-byte no-op, probably to align
                                               #    subsequent instructions better for
                                               #    instruction fetch performance
        nop                                    # no-op
.LBB0_1:
        mov           eax, dword ptr [rdi]     # eax = *arg0 = f0.load()
        movd          xmm1, eax                # xmm1 = eax = f0.load()
        movdqa        xmm2, xmm1               # xmm2 = xmm1 = eax = f0.load()
        addss         xmm2, xmm0               # xmm2 = (xmm2 + xmm0) = (f0 + f1)
        movd          ecx, xmm2                # ecx = xmm2 = (f0 + f1)
        lock cmpxchg  dword ptr [rdi], ecx     # if eax == *arg0 { ZF = 1; *arg0 = arg1 }
                                               #    else { ZF = 0; eax = *arg0 };
                                               #    "lock" means all done exclusively
        jne           .LBB0_1                  # if ZF == 0 goto .LBB0_1
        movdqa        xmm0, xmm1               # return f0 value from before cmpxchg
        pop           rbp                      # restore address of previous stack frame
        ret                                    # return control to previous stack frame address
        nop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 2: The &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; function from Listing 1 compiled to x86-64 using &lt;code class="language-plaintext highligher-rouge"&gt;clang++ -O3&lt;/code&gt; and disassembled using &lt;code class="language-plaintext highligher-rouge"&gt;objdump -disassemble -x86-asm-syntax=intel&lt;/code&gt;, with some minor tweaks for formatting and readability. My annotations are also included as comments.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;If we compare the above code with &lt;a href="https://blog.yiningkarlli.com/2021/05/porting-takua-to-arm-pt1.html#listing5"&gt;Listing 5 in my previous post&lt;/a&gt;, we can see that the above code matches what we got from Clang in Godbolt Compiler Explorer.
The only difference is the stack pointer pushing and popping code that happens in the beginning and end to make this function usable in a larger program; the core functionality in lines 8 through 18 of the above code matches the output from Clang in Godbolt Compiler Explorer exactly.&lt;/p&gt;

&lt;p&gt;Next, here’s the assembly produced by disassembling the arm64 generated using Clang.
I disassembled the arm64 binary using &lt;code class="language-plaintext highlighter-rouge"&gt;otool -Vt&lt;/code&gt;; here’s the relevant &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; function with the same minor changes as in Listing 2 for more readable section labels:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;__Z14addAtomicFloatRNSt3__16atomicIfEEf:
.LBB0_1:
        ldar      w8, [x0]          // w8 = *arg0 = f0, non-atomically loaded
        fmov      s1, w8            // s1 = w8 = f0
        fadd      s2, s1, s0        // s2 = s1 + s0 = (f0 + f1)
        fmov      w9, s2            // w9 = s2 = (f0 + f1)
        mov       x10, x8           // x10 (same as w10) = x8 (same as w8)
        casal     w10, w9, [x0]     // atomically read the contents of the address stored
                                    //    in x0 (*arg0 = f0) and compare with w10;
                                    //    if [x0] == w10:
                                    //       atomically set the contents of the
                                    //       [x0] to the value in w9
                                    //    else:
                                    //       w10 = value loaded from [x0]
        cmp       w10, w8           // compare w10 and w8 and store result in N
        cset      w8, eq            // if previous instruction's compare was true,
                                    //    set w8 = 1
        cmp       w8, #0x1          // compare if w8 == 1 and store result in N
        b.ne      .LBB0_1           // if N==0 { goto .LBB0_1 }
        mov.16b   v0, v1            // return f0 value from ldar
        ret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 3: The &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; function from Listing 1 compiled to arm64 using &lt;code class="language-plaintext highligher-rouge"&gt;clang++ -O3&lt;/code&gt; and disassembled using &lt;code class="language-plaintext highligher-rouge"&gt;otool -Vt&lt;/code&gt;, with some minor tweaks for formatting and readability. &lt;br /&gt;My annotations are also included as comments.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Note the use of the ARMv8.1-A &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; instruction.
Apple’s version of Clang defaults to using ARMv8.1-A instructions when compiling for macOS because the M1 chip implements ARMv8.4-A, and since the M1 chip is the first arm64 processor that macOS supports, that means macOS can safely assume a more advanced minimum target instruction set.
Also, the arm64 assembly output in Listing 3 looks almost exactly identical structurally to the Godbolt Compiler Explorer Clang output in &lt;a href="https://blog.yiningkarlli.com/2021/05/porting-takua-to-arm-pt1.html#listing9"&gt;Listing 9 from my previous post&lt;/a&gt;.
The only differences are in small syntactical differences with how the &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instruction in line 20 specifies a 16 byte (128 bit) SIMD register, some different register choices, and a different ordering of &lt;code class="language-plaintext highlighter-rouge"&gt;fmov&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instructions in lines 6 and 7.&lt;/p&gt;

&lt;p&gt;Finally, let’s take a look at the arm64 assembly that Rosetta 2 generates through the AOT process described earlier.
Disassembling the Rosetta 2 AOT file using &lt;code class="language-plaintext highlighter-rouge"&gt;otool -Vt&lt;/code&gt; produces the following arm64 assembly; like before, I’m only including the relevant &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; function.
Since the code below switches between &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;w&lt;/code&gt; registers a lot, remember that in arm64 assembly, &lt;code class="language-plaintext highlighter-rouge"&gt;x0&lt;/code&gt;-&lt;code class="language-plaintext highlighter-rouge"&gt;x30&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;w0&lt;/code&gt;-&lt;code class="language-plaintext highlighter-rouge"&gt;w30&lt;/code&gt; are really the same registers; &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; just means use the full 64-bit register, whereas &lt;code class="language-plaintext highlighter-rouge"&gt;w&lt;/code&gt; just means use the lower 32 bits of the &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; register with the same register number.
Also, the &lt;code class="language-plaintext highlighter-rouge"&gt;v&lt;/code&gt; registers are 128-bit vector registers that are separate from the &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;y&lt;/code&gt; set of registers; &lt;code class="language-plaintext highlighter-rouge"&gt;s&lt;/code&gt; registers are the bottom 32 bits of &lt;code class="language-plaintext highlighter-rouge"&gt;v&lt;/code&gt; registers.
In my annotations, I’ll use &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; for both &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;w&lt;/code&gt; registers, and I’ll use &lt;code class="language-plaintext highlighter-rouge"&gt;v&lt;/code&gt; for both &lt;code class="language-plaintext highlighter-rouge"&gt;v&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;s&lt;/code&gt; registers.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;__Z14addAtomicFloatRNSt3__16atomicIfEEf:
        str      x5, [x4, #-0x8]!         // store value at x5 to ((address in x4) - 8) and
                                          // write calculated address back into x4
        mov      x5, x4                   // x5 = address in x4
.LBB0_1
        ldr      w0, [x7]                 // x0 = *arg0 = f0, non-atomically loaded
        fmov     s1, w0                   // v1 = x0 = f0
        mov.16b  v2, v1                   // v2 = v1 = f0
        fadd     s2, s2, s0               // v2 = v2 + v0 = (f0 + f1)
        mov.s    w1, v2[0]                // x1 = v2 = (f0 + f1)
        mov      w22, w0                  // x22 = x0 = f0
        casal    w22, w1, [x7]            // atomically read the contents of the address stored
                                          //    in x7 (*arg0 = f0) and compare with x22;
                                          //    if [x7] == x22:
                                          //       atomically set the contents of the
                                          //       [x7] to the value in x1
                                          //    else:
                                          //       x22 = value loaded from [x7]
        cmp      w22, w0                  // compare x22 and x0 and store result in N
        csel     w0, w0, w22, eq          // if N==1 { x0 = x0 } else { x0 = x22 }
        b.ne     .LBB0_1                  // if N==0 { goto .LBB0_1 }
        mov.16b  v0, v1                   // v0 = v1 = f0
        ldur     x5, [x4]                 // x5 = value at address in x4, using unscaled load
        add      x4, x4, #0x8             // add 8 to address stored in x4
        ldr      x22, [x4], #0x8          // x22 = value at ((address in x4) + 8)
        ldp      x23, x24, [x21], #0x10   // x23 = value at address in x21 and
                                          // x24 = value at ((address in x21) + 8)
        sub      x25, x22, x23            // x25 = x22 - x23
        cbnz     x25, .LBB0_2             // if x22 != x23 { goto .LBB0_2 }
        ret      x24
.LBB0_2
        bl       0x4310                   // branch (with link) to address 0x4310
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 4: The x86-64 assembly from Listing 2 translated to arm64 by Rosetta 2's ahead-of-time translator. Disassembled using &lt;code class="language-plaintext highligher-rouge"&gt;otool -Vt&lt;/code&gt;, with some minor tweaks for formatting and readability. My annotations are also included as comments.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;In some ways, we can see similarities between the Rosetta 2 arm64 assembly in Listing 4 and the natively compiled arm64 assembly in Listing 3, but there are also a lot of things in the Rosetta 2 arm64 assembly that look very different from the natively compiled arm64 assembly.
The core functionality in lines 9 through 21 of Listing 4 bear a strong resemblance to the core functionality in lines 5 through 19 of of Listing 3; both versions use a &lt;code class="language-plaintext highlighter-rouge"&gt;fadd&lt;/code&gt;, followed by a &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; instruction to implement the atomic comparison, then follow with a &lt;code class="language-plaintext highlighter-rouge"&gt;cmp&lt;/code&gt; to compare the expected and actual outcomes, and then have some logic about whether or not to jump back to the top of the loop.
However, if we look more closely at the core functionality in the Rosetta 2 version, we can see some oddities.
In preparing for the &lt;code class="language-plaintext highlighter-rouge"&gt;fadd&lt;/code&gt; instruction on line 9, the Rosetta 2 version does a &lt;code class="language-plaintext highlighter-rouge"&gt;fmov&lt;/code&gt; followed by a 16-bit &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; into register &lt;code class="language-plaintext highlighter-rouge"&gt;v2&lt;/code&gt;, and then the &lt;code class="language-plaintext highlighter-rouge"&gt;fadd&lt;/code&gt; takes a value from &lt;code class="language-plaintext highlighter-rouge"&gt;v2&lt;/code&gt;, adds the value to what is in &lt;code class="language-plaintext highlighter-rouge"&gt;v0&lt;/code&gt;, and stores the result back into &lt;code class="language-plaintext highlighter-rouge"&gt;v2&lt;/code&gt;.
The 16-bit move is pointless!
Instead of two &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instructions and an &lt;code class="language-plaintext highlighter-rouge"&gt;fadd&lt;/code&gt; where the first source registers and destination registers are the same, a better version would be to omit the second &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instruction and instead just do &lt;code class="language-plaintext highlighter-rouge"&gt;fadd s2 s1 s0&lt;/code&gt;.
In fact, in Listing 3 we can see that the natively compiled version does in fact just use a single &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; and do &lt;code class="language-plaintext highlighter-rouge"&gt;fadd s2 s1 s0&lt;/code&gt;.
So, what’s going on here?&lt;/p&gt;

&lt;p&gt;Things begin to make more sense once we look at the x86-64 assembly that the Rosetta 2 version is translated from.
In Listing 2’s x86-64 version, the &lt;code class="language-plaintext highlighter-rouge"&gt;addss&lt;/code&gt; instruction only has two inputs because the first source register is always also the destination register.
So, the x86-64 version has no choice but to use a few extra &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instructions to make sure values that are needed later aren’t overwritten by the &lt;code class="language-plaintext highlighter-rouge"&gt;addss&lt;/code&gt; instruction; whatever value needs to be in &lt;code class="language-plaintext highlighter-rouge"&gt;xmm2&lt;/code&gt; during the &lt;code class="language-plaintext highlighter-rouge"&gt;addss&lt;/code&gt; instruction must also be squirreled away in a second location if that value is still needed after &lt;code class="language-plaintext highlighter-rouge"&gt;addss&lt;/code&gt; is executed.
Since the Rosetta 2 arm64 assembly is a direct translation from the x86-64 assembly, the extra &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; needed in the x86-64 version gets translated into the extraneous &lt;code class="language-plaintext highlighter-rouge"&gt;mov.16b&lt;/code&gt; in Listing 4, and the two-operand x86-64 &lt;code class="language-plaintext highlighter-rouge"&gt;addss&lt;/code&gt; gets translated into a strange looking &lt;code class="language-plaintext highlighter-rouge"&gt;fadd&lt;/code&gt; where the same register is duplicated for the first source and destination operands; this duplication is a direct one-to-one mapping to what &lt;code class="language-plaintext highlighter-rouge"&gt;addss&lt;/code&gt; does.&lt;/p&gt;

&lt;p&gt;I think from the above we can see two very interesting things about Rosetta 2’s translation.
On one hand, the fact that the overall structure of the core functionality in the Rosetta 2 and natively compiled versions is so similar is very impressive, especially when considering that Rosetta 2 had absolutely no access to the original high-level C++ source code!
I guess my example function here is a very simple test case, but nonetheless I was impressed that Rosetta 2’s output overall isn’t too bad.
On the other hand though, the Rosetta 2 version does have small oddities and inefficiencies that arise from doing a direct mechanical translation from x86-64.
Since Rosetta 2 has no access to the original source code, no context for what the code does, and has no ability to build any kind of higher-level syntactic understanding, the best Rosetta 2 really can do is a direct mechanical translation with a relatively high level of conservatism with respect to preserving what the original x86-64 code is doing on an instruction-by-instruction basis.
I don’t think that this is actually a fault in Rosetta 2; I think it’s actually pretty much the only reasonable solution.
I don’t know how Rosetta 2’s translator is actually implemented internally, but my guess is that the translator is parsing the x86-64 machine code, generating some kind of IR, and then lowering that IR back to arm64 (who knows, maybe it’s even LLIR).
But, even if Rosetta 2 is generating some kind of IR, that IR at best can only correspond well to the IR that was generated by the last optimization pass in the original compilation to x86-64, and in any last optimization pass, a huge amount of higher level context is likely already lost from the original source program.
Short of doing heroic amounts of program analysis, there’s nothing Rosetta 2 can do about this lost higher level context, and even if implementing all of that program analysis was worthwhile (Which it almost certainly is not) there’s only so much that static analysis can do anyway.
I guess all of the above is a long way of saying: looking at the above example, I think Rosetta 2’s output is really impressive and surprisingly more optimal than I would have guessed before, but at the same time the inherent advantage that natively compiling to arm64 has is obvious.&lt;/p&gt;

&lt;p&gt;However, all of the above is just looking at the core functionality of the original function.
If we look at the arm64 assembly surrounding this core functionality in Listing 4 though, we can see some truly strange stuff.
The Rosetta 2 version is doing a ton of pointer arithmetic and moving around addresses and stuff, and operands seem to be passed into the function using the wrong registers (&lt;code class="language-plaintext highlighter-rouge"&gt;x7&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;x0&lt;/code&gt;).
What is this stuff all about?
The answer lies in how the Rosetta 2 runtime works, and in what makes a Rosetta 2 AOT Mach-O file different from a standard macOS Mach-O binary.&lt;/p&gt;

&lt;p&gt;One key fundamental difference between Rosetta 2 AOT binaries and regular arm64 macOS binaries is that Rosetta 2 AOT binaries use &lt;em&gt;a completely different ABI&lt;/em&gt; from standard arm64 macOS.
On Apple platforms, the ABI used for normal arm64 Mach-O binaries is largely based on the standard ARM-developed arm64 ABI &lt;a href="https://developer.arm.com/documentation/den0024/a/The-ABI-for-ARM-64-bit-Architecture/Register-use-in-the-AArch64-Procedure-Call-Standard/Parameters-in-general-purpose-registers"&gt;[ARM Holdings 2015]&lt;/a&gt;, with some small differences &lt;a href="https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms"&gt;[Apple 2020]&lt;/a&gt; in function calling conventions and how some data types are implemented and aligned.
However, Rosetta 2 AOT binaries use an arm64-ized version of the System V AMD64 ABI, with a direct mapping between x86_64 and arm64 registers &lt;a href="https://ffri.github.io/ProjectChampollion/part1/"&gt;[Nakagawa 2021]&lt;/a&gt;.
This different ABI means that intermixing native arm64 code and Rosetta 2 arm64 code is not possible (or at least not at all practical), and this difference is also the explanation for why the Rosetta 2 assembly uses unusual registers for passing parameters into the function.
In the standard arm64 ABI calling convention, registers &lt;code class="language-plaintext highlighter-rouge"&gt;x0&lt;/code&gt; through &lt;code class="language-plaintext highlighter-rouge"&gt;x7&lt;/code&gt; are used to pass function arguments 0 through 7, with the rest going on the stack.
In the System V AMD64 ABI calling convention, function arguments are passed using registers &lt;code class="language-plaintext highlighter-rouge"&gt;rdi&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;rsi&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;rdx&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;rcx&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;r8&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;r9&lt;/code&gt; for arguments 0 through 5 respectively, with everything else on the stack in reverse order.
In the arm64-ized version of the System V AMD64 ABI that Rosetta 2 AOT uses, the x86-64 &lt;code class="language-plaintext highlighter-rouge"&gt;rdi&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;rsi&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;rdx&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;rcx&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;r8&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;r9&lt;/code&gt; registers map to the arm64 &lt;code class="language-plaintext highlighter-rouge"&gt;x7&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;x6&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;x2&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;x1&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;x8&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;x9&lt;/code&gt;, respectively &lt;a href="https://ffri.github.io/ProjectChampollion/part1/"&gt;[Nakagawa 2021]&lt;/a&gt;.
So, that’s why in line 6 of Listing 4 we see a load from an address stored in &lt;code class="language-plaintext highlighter-rouge"&gt;x7&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;x0&lt;/code&gt;, because &lt;code class="language-plaintext highlighter-rouge"&gt;x7&lt;/code&gt; maps to x86-64’s &lt;code class="language-plaintext highlighter-rouge"&gt;rdi&lt;/code&gt; register, which is the first register used for passing arguments in the System V AMD64 ABI &lt;a href="https://wiki.osdev.org/System_V_ABI"&gt;[OSDev 2018]&lt;/a&gt;.
If we look at the corresponding instruction on line 9 of Listing 2, we can see that the x86-64 code does indeed use a &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instruction from the address stored in &lt;code class="language-plaintext highlighter-rouge"&gt;rdi&lt;/code&gt; to get the first function argument.&lt;/p&gt;

&lt;p&gt;As for all of the pointer arithmetic and address trickery in lines 23 through 28 of Listing 4, I’m not 100% sure what it is for, but I have a guess.
Earlier I mentioned that &lt;code class="language-plaintext highlighter-rouge"&gt;.aot&lt;/code&gt; binaries cannot run like a normal binary and instead require some special memory mapping to work; I think all of this pointer arithmetic may have to do with that.
The way that the Rosetta 2 runtime interacts with the AOT arm64 code is that both the runtime and the AOT arm64 code are mapped into the same memory space at startup and the program counter is set to the entry point of the Rosetta 2 runtime; while running, the AOT arm64 code frequently can jump back into the Rosetta 2 runtime because the Rosetta 2 runtime is what handles things like translating x86_64 addresses into addresses in the AOT arm64 code &lt;a href="https://ffri.github.io/ProjectChampollion/part1/"&gt;[Nakagawa 2021]&lt;/a&gt;.
The Rosetta 2 runtime also directs system calls to native frameworks, which helps improve performance; this property of the Rosetta 2 runtime means that if an x86-64 binary does most of its work by calling macOS frameworks, the translated Rosetta 2 AOT binary can still run very close to native speed (as an interesting aside: Microsoft is adding a much more generalized version of this concept to Windows 11’s counterpart to Rosetta 2: Windows 11 on Arm will allow arbitrary mixing of native arm64 code and translated x86-64 code &lt;a href="https://blogs.windows.com/windowsdeveloper/2021/06/28/announcing-arm64ec-building-native-and-interoperable-apps-for-windows-11-on-arm/"&gt;[Sweetgall 2021]&lt;/a&gt;.
Finally, when a Rosetta 2 AOT binary is run, not only the arm64 and Rosetta 2 runtime are mapped into the running program memory; the original x86-64 binary is mapped in as well.
The AOT binary that Rosetta 2 generates does not actually contain any constant data from the original x86-64 binary; instead, the AOT file references the constant data from the x86-64 binary, which is why the x86-64 binary also needs to be loaded in.
My guess is that the pointer arithmetic stuff happening in the end of Listing 4 is possibly either to calculate offsets to stuff in the x86-64 binary, or to calculate offsets into the Rosetta 2 runtime itself.&lt;/p&gt;

&lt;p&gt;Now that we have a better understanding of what Rosetta 2 is actually doing under the hood and how good the translated arm64 code is compared with natively compiled arm64 code, how does Rosetta 2 actually perform in the real world?
I compared Takua Renderer running as native arm64 code versus as x86-64 code running through Rosetta 2 on four different scenes, and generally running through Rosetta 2 yielded about 65% to 70% of the performance of running as native arm64 code.
The results section at the end of this post contains the detailed numbers and data.
Generally, I’m very impressed with this amount of performance for emulating x86-64 code on an arm64 processor, especially when considering that with high-performance code like Takua Renderer, Rosetta 2 has close to zero opportunities to provide additional performance by calling into native system frameworks.
As can be seen in the &lt;a href="/2021/07/porting-takua-to-arm-pt2.html#2021-07-31-perftesting"&gt;data in the results section&lt;/a&gt;, even more impressive is the fact that even running at 70% of native speed, x86-64 Takua Renderer running on the M1 chip through Rosetta 2 is often on-par with or &lt;em&gt;even faster&lt;/em&gt; than x86-64 Takua Renderer running natively on a contemporaneous current-generation 2019 16-inch MacBook Pro with a 6-core Intel Core i7-9750H processor!&lt;/p&gt;

&lt;div id="2021-07-31-tso-memory-ordering"&gt;&lt;/div&gt;
&lt;h2 id="tso-memory-ordering-on-the-m1-processor"&gt;TSO Memory Ordering on the M1 Processor&lt;/h2&gt;

&lt;p&gt;As I covered extensively in my previous post, one major crucial architectural difference between arm64 and x86-64 is in memory ordering: arm64 is a weakly ordered architecture, whereas x86-64 is a strongly ordered architecture &lt;a href="https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/"&gt;[Preshing 2012]&lt;/a&gt;.
Any system emulating x86-64 binaries on an arm64 processor needs to overcome this memory ordering difference, which means emulating strong memory ordering on a weak memory architecture.
Unfortunately, doing this memory ordering emulation in software is extremely difficult and extremely inefficient. since emulating strong memory ordering on a weak memory architecture means providing stronger memory ordering guarantees than the hardware actually provides.
This memory ordering emulation is widely understood to be one of the main reasons why Microsoft’s x86 emulation mode for Windows on Arm incurs a much higher performance penalty compared with Rosetta 2, even though the two systems have broadly similar architectures &lt;a href="https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm-x86-emulation"&gt;[Hickey et al. 2021]&lt;/a&gt; at a high level.&lt;/p&gt;

&lt;p&gt;Apple’s solution to the difficult problem of emulating strong memory ordering in software was to… just completely bypass the problem altogether.
Rosetta 2 does nothing whatsoever to emulate strong memory ordering in software; instead, Rosetta 2 provides strong memory ordering through &lt;em&gt;hardware&lt;/em&gt;.
Apple’s M1 processor has an unusual feature for an ARM processor: the M1 processor has optional total store memory ordering (TSO) support!
By default, the M1 processor only provides the weak memory ordering guarantees that the arm64 architecture specifies, but for x86-64 binaries running under Rosetta 2, the M1 processor is capable of switching to strong memory ordering in hardware on a core-by-core basis.
This capability is a great example of the type of hardware-software integration that Apple is able to accomplish by owning and building the entire tech stack from the software all the way down to the silicon.&lt;/p&gt;

&lt;p&gt;Actually, the M1 is not the first Apple Silicon chip to have TSO support.
The A12Z chip that was in the Apple Silicon DTK also has TSO support, and the A12Z is known to be a re-binned but otherwise identical variant of the A12X chip from 2018, so we can likely safely assume that the TSO hardware support has been present (albeit unused) as far back as the 2018 iPad Pro!
However, the M1 processor’s TSO implementation does have a significant leg up on the implementation in the A12Z.
Both the M1 and the A12Z implement a version of ARM’s big.LITTLE technology, where the processor contains two different types of CPU cores: lower-power energy-efficient cores, and high-power performance cores.
On the A12Z, hardware TSO support is only implemented in the high-power performance cores, whereas in the M1, hardware TSO support is implement on both the efficiency and performance cores.
As a result, on the A12Z-based Apple Silicon DTK, Rosetta 2 can only use four out of eight total CPU cores on the chip, whereas on M1-based Macs, Rosetta 2 can use all eight CPU cores.&lt;/p&gt;

&lt;p&gt;I should mentioned here that, interestingly, the A12Z and M1 are actually not the first ARM CPUs to implement TSO as the memory model &lt;a href="https://threedots.ovh/blog/2021/02/cpus-with-sequential-consistency/"&gt;[Threedots 2021]&lt;/a&gt;.
Remember, when ARM specifies weak ordering in the architecture, what this actually means is that any arm64 implementation can actually choose to have any kind of stronger memory model since code written for a weaker memory model should also work correctly on a stronger memory model; only going the other way doesn’t work.
NVIDIA’s Denver and Carmel CPU microarchitectures (found in various NVIDIA Tegra and Xaviar system-on-a-chips) are also arm64 designs that implement a sequentially consistency memory model.
If I had to guess, I would guess that Denver and Carmel’s sequential consistency memory model is a legacy of the Denver Projects’s origins as a project to build an x86-64 CPU; the project was shifted to arm64 before release.
Fujitsu’s A64FX processor is another arm64 design that implements TSO as its memory model, which makes sense since the A64FX processor is meant for use in supercomputers as a successor to Fujitsu’s previous SPARC-based supercomputer processors, which also implemented TSO.
However, to the best of my knowledge, Apple’s A12Z and M1 are unique in their ability to execute in &lt;em&gt;both&lt;/em&gt; the usual weak ordering mode and TSO mode.&lt;/p&gt;

&lt;p&gt;To me, probably the most interesting thing about hardware TSO support in Apple Silicon is that switching ability.
Even more interesting is that the switching ability doesn’t require a reboot or anything like that- each core can be &lt;em&gt;independently&lt;/em&gt; switched between strong and weak memory ordering on-the-fly at runtime through software.
On Apple Silicon processors, hardware TSO support is enabled by modifying a special register named &lt;code class="language-plaintext highlighter-rouge"&gt;actlr_el1&lt;/code&gt;; this register is actually &lt;a href="https://developer.arm.com/documentation/100442/0100/register-descriptions/aarch64-system-registers/actlr-el1--auxiliary-control-register--el1"&gt;defined by the arm64 specification&lt;/a&gt; as an implementation-defined auxiliary control register.
Since &lt;code class="language-plaintext highlighter-rouge"&gt;actlr_el1&lt;/code&gt; is implementation-defined, Apple has chosen to use it for toggling TSO and possibly for toggling other, so far publicly unknown special capabilities.
However, the &lt;code class="language-plaintext highlighter-rouge"&gt;actlr_el1 &lt;/code&gt; register, being a special register, cannot be modified by normal code; modifications to &lt;code class="language-plaintext highlighter-rouge"&gt;actlr_el1&lt;/code&gt; can only be done by the kernel, and the only thing in macOS that the kernel enables TSO for is Rosetta 2…&lt;/p&gt;

&lt;p&gt;…at least by default!
Shortly after Apple started shipping out Apple Silicon DTKs last year, &lt;a href="https://saagarjha.com"&gt;Saagar Jha&lt;/a&gt; figured out how to allow any program to toggle TSO mode through &lt;a href="https://github.com/saagarjha/TSOEnabler"&gt;a custom kernel extension&lt;/a&gt;.
The way the TSOEnabler kext works is extremely clever; the kext searches through the kernel to find where the kernel is modifying &lt;code class="language-plaintext highlighter-rouge"&gt;actlr_el1&lt;/code&gt; and then traces backwards to figure out what pointer the kernel is reading a flag from for whether or not to enable TSO mode.
Instead of setting TSO mode itself, the kext then intercepts the pointer to the flag and writes to it, allowing the kernel to handle all of the TSO mode setup work since there’s some other stuff that needs to happen in addition to modifying &lt;code class="language-plaintext highlighter-rouge"&gt;actlr_el1&lt;/code&gt;.
Out of sheer curiosity, I compiled the TSOEnabler kext and installed it on my M1 Mac Mini to give it a try!
I don’t suggest installing and using TSOEnabler casually, and definitely not for normal everyday use; installing a custom self-compiled, unsigned kext on modern macOS requires disabling SIP.
However, I already had SIP disabled due to my earlier Rosetta 2 AOT exploration, and so I figured why not give this a shot before I reset everything and reenable SIP.&lt;/p&gt;

&lt;p&gt;The first thing I wanted to try was a simple test to confirm that the TSOEnabler kext was working correctly.
In my last post, I wrote about a case where weak memory ordering was exposing a bug in some code written around incrementing an atomic integer; the “canonical” example of this specific type of situation is &lt;a href="https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/"&gt;Jeff Preshing’s multithreaded atomic integer incrementer example&lt;/a&gt; using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt;.
I adapted Jeff Preshing’s example for my test; in this test, two threads both increment a shared integer counter 1000000 times, with exclusive access to the integer guarded using an atomic integer flag.
Operations on the atomic integer flag use &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt;.
On strongly-ordered CPUs, using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; works fine and at the end of the program, the value of the shared integer counter is always 2000000 as expected.
However, on weakly-ordered CPUs, weak memory ordering means that two threads can end up in a race condition to increment the shared integer counter; as a result, on weakly-ordered CPUs, at the end of the program the value of the shared integer counter is very often something slightly less than 2000000.
The key modification I made to this test program was to enable the M1 processor’s hardware TSO mode for each thread; if hardware TSO mode is correctly enabled, then the value of the shared integer counter should always end up being 2000000.
If you want to try for yourself, Listing 5 below includes the test program in its entirety; compile using &lt;code class="language-plaintext highlighter-rouge"&gt;c++ tsotest.cpp -std=c++11 -o tsotest&lt;/code&gt;.
The test program takes a single input parameter: &lt;code class="language-plaintext highlighter-rouge"&gt;1&lt;/code&gt; to enable hardware TSO mode, and anything else to leave TSO mode disabled.
Remember, to use this program, you must have compiled and installed the TSOEnabled kernel extension mentioned above.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;#include &amp;lt;atomic&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;thread&amp;gt;
#include &amp;lt;sys/sysctl.h&amp;gt;

static void enable_tso(bool enable_) {
    int enable = int(enable_);
    size_t size = sizeof(enable);
    int err = sysctlbyname("kern.tso_enable", NULL, &amp;amp;size, &amp;amp;enable, size);
    assert(err == 0);
}

int main(int argc, char** argv) {
    bool useTSO = false;
    if (argc &amp;gt; 1) {
        useTSO = std::stoi(std::string(argv[1])) == 1 ? true : false;
    }
    std::cout &amp;lt;&amp;lt; "TSO is " &amp;lt;&amp;lt; (useTSO ? "enabled" : "disabled") &amp;lt;&amp;lt; std::endl;

    std::atomic&amp;lt;int&amp;gt; flag(0);
    int sharedValue = 0;
    auto counter = [&amp;amp;](bool enable) {
        enable_tso(enable);
        int count = 0;
        while (count &amp;lt; 1000000) {
            int expected = 0;
            if (flag.compare_exchange_strong(expected, 1, std::memory_order_relaxed)) {
                // Lock was successful
                sharedValue++;
                flag.store(0, std::memory_order_relaxed);
                count++;
            }
        }
    };

    std::thread thread1([&amp;amp;]() { counter(useTSO); });
    std::thread thread2([&amp;amp;]() { counter(useTSO); });
    thread2.join();
    thread1.join();

    std::cout &amp;lt;&amp;lt; sharedValue &amp;lt;&amp;lt; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 5: Jeff Preshing's weakly ordered atomic integer test program, modified to support using the M1 processor's hardware TSO mode.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Running my test program indicated that the kernel extension was working properly!
In the screenshot below, I check that the Mac I’m running on has an arm64 processor, then I compile the test program and check that the output is a native arm64 binary, and then I run the test program four times each with and without hardware TSO mode enabled.
As expected, with hardware TSO mode disabled, the program counts slightly less than 2000000 increments on the shared atomic counter, whereas with hardware TSO mode enabled, the program counts exactly 2000000 increments every time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/tsotest.png"&gt;&lt;img alt="Figure 4: Building, examining, and running the test program to demonstrate hardware TSO mode disabled and then enabled." src="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/tsotest.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being able to enable hardware TSO mode in a native arm64 binary outside of Rosetta 2 actually does have some practical uses.
After I confirmed that the kernel extension was working correctly, I temporarily hacked hardware TSO mode into Takua Renderer’s native arm64 version, which allowed me to further verify that everything was working correctly with all of the various weakly ordered atomic fixes that I described in my previous post.
As mentioned in my previous post, comparing renders across different processor architectures is difficult for a variety of reasons, and previously comparing Takua Renderer running on a weakly ordered CPU versus on a strongly ordered CPU required comparing renders made on arm64 versus renders made on x86-64.
Using the M1’s hardware TSO mode though, I was able to compare renders made on exactly the same processor, which confirmed that everything works correctly!
After doing this test, I then removed the hardware TSO mode from Takua Renderer’s native arm64 version.&lt;/p&gt;

&lt;p&gt;One silly idea I tried was to &lt;em&gt;disable&lt;/em&gt; hardware TSO mode from inside of Rosetta 2, just to see what would happen.
Rosetta 2 does not support running x86-64 kernel extensions on arm64; all macOS kernel extensions must be native to the architecture they are running on.
However, as mentioned earlier, the Rosetta 2 runtime bridges system framework calls from inside of x86-64 binaries to their native arm64 counterparts, and this includes &lt;code class="language-plaintext highlighter-rouge"&gt;sysctl&lt;/code&gt; calls!
So we can actually call &lt;code class="language-plaintext highlighter-rouge"&gt;sysctlbyname("kern.tso_enable")&lt;/code&gt; from inside of an x86-64 binary running through Rosetta 2, and Rosetta 2 will pass the call along correctly to the native TSOEnabler kernel extension, which will then properly set hardware TSO mode.
For a simple test, I added a bit of code to test if a binary is running under Rosetta 2 or not and compiled the test program from Listing 5 for x86-64.
For the sake of completeness, here is how to check if a process is running under Rosetta 2; this code sample was provided by Apple in &lt;a href="https://developer.apple.com/videos/play/wwdc2020/10686/"&gt;a WWDC 2020 talk about Apple Silicon&lt;/a&gt;:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;// Use "sysctl.proc_translated" to check if running in Rosetta

// Returns 1 if running in Rosetta
int processIsTranslated() {
    int ret = 0;
    size_t size = sizeof(ret);
    // Call the sysctl and if successful return the result
    if (sysctlbyname("sysctl.proc_translated", &amp;amp;ret, &amp;amp;size, NULL, 0) != -1) 
            return ret;
    // If "sysctl.proc_translated" is not present then must be native
    if (errno == ENOENT)
            return 0;
    return -1;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 6: Example code from Apple on how to check if the current process is running through Rosetta 2.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;In Figure 5, I build the test program from Listing 5 as an x86-64 binary, with the Rosetta 2 detection function from Listing 6 added in.
I then check that the system architecture is arm64 and that the compiled program is x86-64, and run the test program with TSO disabled from inside of Rosetta 2.
The program reports that it is running through Rosetta 2 and reports that TSO is disabled, and then proceeds to report slightly less than 2000000 increments to the shared atomic counter:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/tsotest.png"&gt;&lt;img alt="Figure 5: Building, examining, and running the test program to demonstrate hardware TSO mode disabled and then enabled." src="https://blog.yiningkarlli.com/content/images/2021/Jul/takua-on-arm-pt2/tsotest.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, being able to disable hardware TSO mode from inside of Rosetta 2 is only a curiosity; I can’t really think of any practical reason why anyone would ever want to do this.
I guess one possible answer is to try to claw back some performance whilst running through Rosetta 2, since the hardware TSO mode does have a tangible performance impact, but this answer isn’t actually valid, since there is no guarantee that x86-64 binaries running through Rosetta 2 will work correctly with hardware TSO mode enabled.
The simple example here only works precisely because it is extremely simple; I also tried hacking disabling hardware TSO mode into the x86-64 version of Takua Renderer and running that through Rosetta 2.
The result was that this hacked version of Takua Renderer would run for only a fraction of a second before running into a hard crash from somewhere inside of TBB.
More complex x86-64 programs with hardware TSO mode not working correctly or even crashing shouldn’t be surprising, since the x86-64 code itself can have assumptions about strong memory ordering baked into whatever optimizations the code was compiled with.
As mentioned earlier, running a program written and compiled with weak memory ordering assumptions on a stronger memory model should work correctly, but running a program written and compiled with strong memory ordering assumptions on a weaker memory model can cause problems.&lt;/p&gt;

&lt;p&gt;Speaking of the performance of hardware TSO mode, the last thing I tried was measuring the performance impact of enabling hardware TSO mode.
I hacked enabling hardware TSO mode into the native arm64 version of Takua Renderer, with the idea being that by comparing the Rosetta 2, custom TSO-enabled native arm64, and default TSO-disabled native arm64 versions of Takua Renderer, I could get a better sense of exactly how much performance cost there is to running the M1 with TSO enabled, and how much of the performance cost of Rosetta 2 comes from less efficient translated arm64 code versus from TSO-enabled mode.
The &lt;a href="/2021/07/porting-takua-to-arm-pt2.html#2021-07-31-perftesting"&gt;results section at the end of this post&lt;/a&gt; contains the exact numbers and data for the four scenes that I tested; the general trend I found was that native arm64 code with hardware TSO enabled ran about 10% to 15% slower than native arm64 code with hardware TSO disabled.
When comparing with Rosetta 2’s overall performance, I think we can reasonably estimate that on the M1 chip, hardware TSO is responsible for somewhere between a third to a half of the performance discrepancy between Rosetta 2 and native weakly ordered arm64 code.&lt;/p&gt;

&lt;p&gt;Apple Silicon’s hardware TSO mode is a fascinating example of Apple extending the base arm64 architecture and instruction set to accelerate application-specific needs.
Hardware TSO mode to support and accelerate Rosetta 2 is just the start; Apple Silicon is well known to already contain some other interesting custom extensions as well.
For example, Apple Silicon contains an entire new, so far undocumented arm64 ISA extension centered around doing fast matrix operations for Apple’s “Accelerate” framework, which supports various deep learning and image procesing applications &lt;a href="https://gist.githubusercontent.com/dougallj/7a75a3be1ec69ca550e7c36dc75e0d6f/raw/60d491aeb70863363af1d4bdf4b8ade9be486af3/aarch64_amx.py"&gt;[Johnson 2020]&lt;/a&gt;.
This extension, called AMX (for Apple Matrix coprocessor), is separate but likely related to the “Neural Engine” hardware &lt;a href="https://medium.com/swlh/apples-m1-secret-coprocessor-6599492fc1e1"&gt;[Engheim 2021]&lt;/a&gt; that ships on the M1 chip alongside the M1’s arm64 processor and custom Apple-designed GPU.
Recent open-source code releases from Apple &lt;a href="https://mobile.twitter.com/_saagarjha/status/1398959235954745346"&gt;also hint at&lt;/a&gt; future Apple Silicon chips having dedicated built-in hardware for doing branch predicion around Objective C’s objc_msgSend, which would considerably accelerate message passing in Cocoa apps.&lt;/p&gt;

&lt;div id="2021-07-31-embree-on-arm64-using-sse2neon"&gt;&lt;/div&gt;
&lt;h2 id="embree-on-arm64-using-sse2neon"&gt;Embree on arm64 using sse2neon&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, porting Takua and Takua’s dependencies was relatively easy and straightforward and in large part worked basically out-of-the-box, because Takua and most of Takua’s dependencies are written in vanilla C++.
Gotchas like memory-ordering correctness in atomic and multithreaded code aside, porting vanilla C++ code between x86-64 and arm64 largely just involves recompiling, and popular modern compilers such as Clang, GCC, and MSVC all have mature, robust arm64 backends today.
However, for code written using inline assembly or architecture-specific vector SIMD intrinsics, recompilation is not enough to get things working on a different processor architecture.&lt;/p&gt;

&lt;p&gt;A huge proportion of the raw compute power in modern processors is actually located in vector &lt;a href="https://en.wikipedia.org/wiki/SIMD"&gt;SIMD instruction set extensions&lt;/a&gt;, such as the various SSE and AVX extensions found in modern x86-64 processors and the NEON and upcoming SVE extensions found in arm64.
For workloads that can benefit from vectorization, using SIMD extensions means up to a 4x speed boost over scalar code when using SSE or NEON, and potentially even more using AVX or SVE.
One way to utilize SIMD extensions is just to write scalar C++ code like normal and let the compiler auto-vectorize the code at compile-time.
However, relying on auto-vectorization to leverage SIMD extensions in practice can be surprisingly tricky.
In order for compilers to be able to efficiently auto-vectorize code that was written to be scalar, compilers need to be able to deduce and infer an enormous amount of context and knowledge about what the code being compiled actually does, and doing this kind of work is extremely difficult and extremely prone to defeat by edge cases, complex scenarios, or even just straight up implementation bugs.
The end result is that getting scalar C++ code to go through auto-vectorization well in practice ends up requiring a lot of deep knowledge about how the compiler’s auto-vectorization implementation actually works under the hood, and small innocuous changes can often suddenly lead to the compiler falling back to generating completely scalar assembly.
Without a robust performance test suite, these fallbacks can happen unbeknownst to the programmer; I like the term that my friend &lt;a href="https://twitter.com/superfunc"&gt;Josh Filstrup&lt;/a&gt; uses for these scenarios: “real rugpull moments”.
Most high-performance applications that require good vectorization usually rely on at least one of several other options: write code directly in assembly utilizing SIMD instructions, write code using SIMD intrinsics, or write code for use with &lt;a href="https://ispc.github.io"&gt;ISPC: the Intel SPMD Program Compiler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Writing SIMD code directly in assembly is more or less just like writing regular assembly, just with different instructions and wider registers; SSE uses &lt;code class="language-plaintext highlighter-rouge"&gt;XMM&lt;/code&gt; registers and many SSE instructions end in either &lt;code class="language-plaintext highlighter-rouge"&gt;SS&lt;/code&gt; or &lt;code class="language-plaintext highlighter-rouge"&gt;PS&lt;/code&gt;, AVX uses &lt;code class="language-plaintext highlighter-rouge"&gt;ZMM&lt;/code&gt; registers, and NEON uses &lt;code class="language-plaintext highlighter-rouge"&gt;D&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;Q&lt;/code&gt; registers.
Since writing directly in assembly is often not desirable for a variety of readability and ease-of-use reasons, writing vector code directly in assembly is not nearly as common as writing vector code in normal C or C++ using vector intrinsics.
Vector intrinsics are functions that look like regular functions from the outside, but within the compiler have a direct one-to-one or near one-to-one mapping to specific assembly instructions.
For SSE and AVX, vector intrinsics are typically found in headers named using the pattern &lt;code class="language-plaintext highlighter-rouge"&gt;*mmintrin.h&lt;/code&gt;, where &lt;code class="language-plaintext highlighter-rouge"&gt;*&lt;/code&gt; is a letter of the alphabet corresponding to a specific subset or version of either SSE of AVX (for example, &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; for SSE, &lt;code class="language-plaintext highlighter-rouge"&gt;e&lt;/code&gt; for SSE2, &lt;code class="language-plaintext highlighter-rouge"&gt;n&lt;/code&gt; for SSE4.2, &lt;code class="language-plaintext highlighter-rouge"&gt;i&lt;/code&gt; for AVX, etc.).
For NEON, vector intrinsics are typically found in &lt;code class="language-plaintext highlighter-rouge"&gt;arm_neon.h&lt;/code&gt;.
Vector intrinsics are commonly found in many high-performance codebases, but another powerful and increasingly popular way to vectorize code is by using ISPC.
ISPC compiles a special variant of the C programming language using a &lt;a href="https://en.wikipedia.org/wiki/SPMD"&gt;SPMD, or single-program-multiple-data&lt;/a&gt;, programming model compiled to run on SIMD execution units; the idea is that an ISPC program describes what a single lane in a vector unit does, and ISPC itself takes care of making that program run across all of the lanes of the vector unit &lt;a href="https://doi.org/10.1109/InPar.2012.6339601"&gt;[Pharr and Mark 2012]&lt;/a&gt;.
While this may sound superficially like a form of auto-vectorization, there’s a crucial difference that makes ISPC far more reliable in outputting good vectorized assembly: ISPC bakes a vectorization-friendly programming model directly into the language itself, whereas normal C++ has no such affordances that C++ compilers can rely on.
This SPMD model is broadly very similar to how writing a GPU kernel works, although there are some key differences between SPMD as a programming model and the &lt;a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_threads"&gt;SIMT model&lt;/a&gt; that GPU run on (namely, a SPMD program can be at a different point on each lane, whereas a SIMT program keeps the progress across all lanes in lockstep).
A big advantage of using ISPC over vector intrinsics or vector assembly is that ISPC code is basically just normal C code; in fact, ISPC programs can often compile as normal scalar C code with little to no modification.
Since the actual transformation to vector assembly is up to the compiler, writing code for ISPC is far more processor architecture independent than vector intrinsics are; ISPC today includes backends to generate SSE, AVX, and NEON binaries.
&lt;a href="https://pharr.org/matt/blog/2018/04/30/ispc-all"&gt;Matt Pharr has a great blog post series&lt;/a&gt; that goes into much more detail about the history and motivations behind ISPC and the benefits of using ISPC.&lt;/p&gt;

&lt;p&gt;In general, graphics workloads tend to fit the bill well for vectorization, and as a result, graphics libraries often make extensive use of SIMD instructions (actually, a surprisingly large number of problem types can be vectorized, including even &lt;a href="https://github.com/simdjson/simdjson"&gt;JSON parsing&lt;/a&gt;).
Since SIMD intrinsics are architecture-specific, I didn’t fully expect all of Takua’s dependencies to compile right out of the box on arm64; I expected that a lot of them would contain chunks of code written using x86-64 SSE and/or AVX intrinsics!
However, almost all of Takua’s dependencies compiled without a problem either because they provided arm64 NEON or scalar C++ fallback codepaths for every SSE/AVX codepath, or because they rely on auto-vectorization by the compiler instead of using intrinsics directly.
OpenEXR is an example of the former, while OpenVDB and OpenSubdiv are examples of the latter.
Embree was the notable exception: Embree is heavily vectorized using code implemented directly using SSE and/or AVX intrinsics with no alternative scalar C++ or arm64 NEON fallback, and Embree also provides an ISPC interfaces.
Starting with Embree v3.13.0, Embree now provides an arm64 NEON codepath as well, but at the time I first ported Takua to arm64, Embree didn’t come with anything other than SSE and AVX implementations.&lt;/p&gt;

&lt;p&gt;Fortunately, Embree is actually written in such a way that porting Embree to different processor architectures with different vector intrinsics is, at least in theory, relatively straightforward.
The Embree codebase internally is written as several different “layers”, where the bottommost layer is located in &lt;code class="language-plaintext highlighter-rouge"&gt;embree/common/simd/&lt;/code&gt; in the Embree source tree.
As one might be able to guess from the name, this bottommost layer is where all of the core SIMD functionality in Embree is implemented; this part of the codebase implements SIMD wrappers for things like 4/8/16 wide floats, SIMD math operations, and so on.
The rest of the Embree codebase doesn’t really contain many direct vector intrinsics at all; the parts of Embree that actually implement BVH construction and traversal and ray intersection all call into this base SIMD library.
As suggested by &lt;a href="https://ingowald.blog/2018/07/15/cfi-embree-on-arm-power/"&gt;Ingo Wald in a 2018 blog post&lt;/a&gt;, porting Embree to use something other than SSE/AVX mostly requires just reimplementing this base SIMD wrapper layer, and the rest of the Embree should more or less “just work”.&lt;/p&gt;

&lt;p&gt;In his blog post, Ingo mentioned experimenting with replacing all of Embree’s base SIMD layer with scalar implementations of all of the vectorized code.
Back in early 2020, as part of my effort to get Takua up and running on arm64 Linux, I actually tried doing a scalar rewrite of the base SIMD layer of Embree as well as a first attempt at porting to arm64.
Overall the process to rewrite to scalar was actually very straightforward; most things were basically just replacing a function that did something with float4 inputs using SSE instructions with a simple loop that iterates over the four floats in a float4.
I did find that in addition to rewriting all of the SIMD wrapper functions to replace SSE intrinsics with scalar implementations, I also had to replace some straight-up inlined x86-64 assembly with equivalent compiler intrinsics; basically all of this code lives in &lt;code class="language-plaintext highlighter-rouge"&gt;common/sys/intrinsics.h&lt;/code&gt;.
None of the inlined assembly replacement was very complicated either though, most of it was things like replacing an inlined assembly call to x86-64’s &lt;code class="language-plaintext highlighter-rouge"&gt;bsf&lt;/code&gt; bit-scan-forward instruction with a call to the more portable &lt;code class="language-plaintext highlighter-rouge"&gt;__builtin_ctz()&lt;/code&gt; integer trailing zero counter builin compiler function.
Embree’s build system also required modifications; since I was just doing this as an initial test, I just did a terribly hack-job on the CMake scripts and, with some troubleshooting, got things building and running on arm64 Linux.
Unfortunately, the performance of my quick-and-rough scalar Embree port was… very disappointing.
I had hoped that the compiler would be able to do a decent job of autovectorizing the scalar reimplementations of all of the SIMD code, but overall my scalar Embree port on x86-64 was basically between three to four times slower than standard SSE Embree, which indicated that the compiler basically hadn’t effectively autovectorized anything at all.
This level of performance regression basically meant that my scalar Embree port wasn’t actually significantly faster than Takua’s own internal scalar BVH implementation; the disappointing performance combined with how hacky and rough my scalar Embree port was led me to abandon using Embree on arm64 Linux for the time being.&lt;/p&gt;

&lt;p&gt;A short while later in the spring of 2020 though, I remembered that Syoyo Fujita had already succesfully ported Embree to arm64 with vectorization support!
Actually, Syoyo had started his &lt;a href="https://github.com/lighttransport/embree-aarch64"&gt;Embree-aarch64&lt;/a&gt; fork three years earlier in 2017 and had kept the project up-to-date with each new upstream official Embree release; I had just forgotten about the project until it popped up in my Twitter feed one day.
The approach that Syoyo took to getting vectorization working in the Embree-aarch64 fork was by using the &lt;a href="https://github.com/DLTcollab/sse2neon"&gt;sse2neon&lt;/a&gt; project, which implements SSE intrinsics on arm64 using NEON instructions and serves as a drop-in replacement for the various x86-64 &lt;code class="language-plaintext highlighter-rouge"&gt;*mmintrin.h&lt;/code&gt; headers.
Using sse2neon is actually the same strategy that had previously been used by &lt;a href="https://mightynotes.wordpress.com/2017/01/24/porting-intel-embree-to-arm/"&gt;Martin Chang in 2017&lt;/a&gt; to port Embree 2.x to work on arm64; Martin’s earlier effort provided the proof-of-concept that paved the way for Syoyo to fork Embree 3.x into Embree-aarch64.
Building the Embree-aarch64 fork on arm64 worked out-of-the-box, and on my Raspberry Pi 4, using Embree-aarch64 with Takua’s Embree backend produced a performance increase over Takua’s internal BVH implementation that was in the general range of what I expected.&lt;/p&gt;

&lt;p&gt;Taking a look at the process that was taken to get Embree-aarch64 to a production-ready state with results that matched x86-64 Embree exactly provides a lot of interesting insights into how NEON works versus how SSE works.
In my previous post I wrote about how getting identical floating point behavior between different processor architectures can be challenging for a variety of reasons; getting floating point behavior to match between NEON and SSE is even harder!
Various NEON instructions such as &lt;code class="language-plaintext highlighter-rouge"&gt;rcp&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;rsqt&lt;/code&gt; have different levels of accuracy from their corresponding SSE counterparts, which required the Embree-aarch64 project to &lt;a href="https://github.com/lighttransport/embree-aarch64/issues/24"&gt;implement more accurate versions&lt;/a&gt; of some SSE intrinsics than what sse2neon provided at the time; a lot of these improvements were later contributed back to sse2neon.
I originally was planning to include a deep dive into comparing SSE, NEON, ISPC, sse2neon, and SSE instructions running on Rosetta 2 as part of this post, but the writeup for that comparison has now gotten so large that it’s going to have to be its own post as a later follow-up to this post; stay tuned!&lt;/p&gt;

&lt;p&gt;As a bit of an aside: the history of the sse2neon project is a great example of a community forming to build an open-source project around a new need.
The sse2neon project was originally started by John W. Ratcliff at NVIDIA along with a few other NVIDIA folks and &lt;a href="https://github.com/jratcliff63367/sse2neon"&gt;implemented only a small subset of SSE&lt;/a&gt; that was just enough for their own needs.
However, after posting the project to Github with the MIT license, a community gradually formed around sse2neon and fleshed it out into a full project with full coverage of MMX and all versions of SSE from SSE1 all the way through SSE4.2.
Over the years sse2neon has seen &lt;a href="https://github.com/DLTcollab/sse2neon/blob/master/sse2neon.h#L9"&gt;contributions and improvements&lt;/a&gt; from NVIDIA, Amazon, Google, the Embree-aarch64 project, the Blender project, and recently Apple as part of Apple’s larger slew of contributions to various projects to improve arm64 support for Apple Silicon.&lt;/p&gt;

&lt;p&gt;Starting with Embree v3.13.0, released in May 2021, the official main Embree project now has also gained full support for arm64 NEON; I have since switched Takua Renderer’s arm64 builds from using the Embree-aarch64 fork to using the new official arm64 support in Embree v3.13.0.
The approach the official Embree project takes is directly based off of the work that Syoyo Fujita and others did in the Embree-aarch64 fork; sse2neon is used to emulate SSE, and the same math precision improvements that were made in Embree-aarch64 were also adopted upstream by the official Embree project.
Much like Embree-aarch64, the arm64 NEON backend for Embree v3.13.0 does not include ISPC support, even though ISPC has an arm64 NEON backend as well; maybe this will come in the future.
Brecht Van Lommel from the Blender project seems to have done &lt;a href="https://github.com/embree/embree/pull/316"&gt;most of the work&lt;/a&gt; to upstream Embree-aarch64’s changes, with additional work and additional optimizations from Sven Woop on the Intel Embree team.
Interestingly and excitingly, &lt;a href="https://github.com/embree/embree/pull/330"&gt;Apple also recently submitted a patch&lt;/a&gt; to the official Embree project that adds AVX2 support on arm64 by treating each 8-wide AVX value as a pair of 4-wide NEON values.&lt;/p&gt;

&lt;div id="2021-07-31-more-differences-in-arm64-vs-x86-64"&gt;&lt;/div&gt;
&lt;h2 id="more-differences-in-arm64-versus-x86-64"&gt;(More) Differences in arm64 versus x86-64&lt;/h2&gt;

&lt;p&gt;In my previous post and in this post, I’ve covered a bunch of interesting differences and quirks that I ran into and had to take into account while porting from x86-64 to arm64.
There are, of course, far more differences that I didn’t touch on.
However, in this small section, I thought I’d list a couple more small but interesting differences that I ran into and had to think about.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;arm64 and x86-64 handle float-to-int conversions slightly differently for some edge cases. Specifically, for edge values such as a uint32_t set to &lt;code class="language-plaintext highlighter-rouge"&gt;INF&lt;/code&gt;, arm64 will make a best attempt to find the nearest possible integer to convert to, which would be 4294967295. x86-64, on the other hand, treats the &lt;code class="language-plaintext highlighter-rouge"&gt;INF&lt;/code&gt; case as basically undefined behavior and defaults to just zero. In path tracing code where occasional infinite values need to be handled for things like edge cases in sampling Dirac distributions, some care needs to be taken to make sure that the renderer is understanding and processing &lt;code class="language-plaintext highlighter-rouge"&gt;INF&lt;/code&gt; values correctly on both arm64 and x86-64.&lt;/li&gt;
  &lt;li&gt;Similarly, implicit conversion from signed integers to unsigned integers can have some different behavior between the two platforms. On arm64, negative signed integers get trimmed to zero when implicitly converted to an unsigned integer; for code that must cast between signed and unsigned integers, care must be taken to make sure that all conversions are explicitly cast and that the edge case behavior on arm64 and x86-64 are accounted for.&lt;/li&gt;
  &lt;li&gt;The signedness of &lt;code class="language-plaintext highlighter-rouge"&gt;char&lt;/code&gt; is platform specific and defaults to being signed on x86-64 but defaults to being unsigned on ARM architectures &lt;a href="https://www.drdobbs.com/architecture-and-design/portability-the-arm-processor/184405435#"&gt;[Harmon 2003]&lt;/a&gt;, including arm64. For custom string processing functions, this may have to be taken into account.&lt;/li&gt;
  &lt;li&gt;x86-64 is always little-endian, but arm64 is a &lt;a href="https://en.wikipedia.org/wiki/Endianness#Bi-endianness"&gt;bi-endian&lt;/a&gt; architecture that can be either little-endian or big-endian, as set by the operating system at startup time. Most Linux flavors, including Fedora, default to little-endian on arm64, and Apple’s various operating systems all exclusively use little-endian mode on arm64 as well, so this shouldn’t be too much of a problem for most use cases. However, for software that does expect to have to run on both little and big endian systems, endianess has to be taken into account for reading/writing/handling binary data. For example, Takua has a checkpointing system that basically dumps state information from the renderer’s memory straight to disk; these checkpoint files would need to have their endianess checked and handled appropriately if I were to make Takua bi-endian. However, since I don’t expect to ever run my own hobby stuff on a big-endian system, I just have Takua check the endianess at startup right now and refuse to run if the system is big-endian.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details to look out for when porting x86-64 code to arm64 code on macOS specifically, Apple’s developer documentation &lt;a href="https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code"&gt;has a whole article&lt;/a&gt; covering various things to consider.
Another fantastic resource for diving into arm64 assembly is Howard Oakley’s &lt;a href="https://eclecticlight.co/2021/07/27/code-in-arm-assembly-rounding-and-arithmetic/"&gt;“Code in ARM Assembly” series&lt;/a&gt;, which covers arm64 assembly programming on Apple Silicon in extensive detail (the bottom of each article in Howard Oakley’s series contains a table of contents linking out to all of the previous articles in the series).&lt;/p&gt;

&lt;div id="2021-07-31-perftesting"&gt;&lt;/div&gt;
&lt;h2 id="more-performance-testing"&gt;(More) Performance Testing&lt;/h2&gt;

&lt;p&gt;In my previous post, I included performance testing results from my initial port to arm64 Linux, running on a Raspberry Pi 4B.
Now that I have Takua Renderer up and running on a much more powerful M1 Mac Mini with 16 GB of memory, how does performance look on “big” arm64 hardware?
Last time around the machines / processors I compared were a Raspberry Pi 4B, which uses a Broadcom BCM2711 CPU with 4 Cortex-A72 cores dating back to 2015, a 2015 MacBook Air with a 2 core / 4 thread Intel Core i5-5250U CPU, and as an extremely unfair comparison point, my personal workstation with dual Intel Xeon E5-2680 CPUs from 2012 with 8 cores / 16 threads each (16 cores / 32 threads total).
The conclusion last time was that even though the Raspberry Pi 4B’s arm64 processor basically lost in terms of render time on almost every test, the Raspberry Pi 4B was actually the absolute &lt;em&gt;winner&lt;/em&gt; by a wide margin when it came to &lt;em&gt;total energy usage&lt;/em&gt; per render job.&lt;/p&gt;

&lt;p&gt;This time around, since my expectation is that Apple’s M1 chip should be able to perform extremely well, I think my dual-Xeon personal workstation should absolutely be a fair competitor.
In fact, I think the comparison might actually be kind of &lt;em&gt;unfair&lt;/em&gt; towards the dual-Xeon workstation, since the processors are from 2012 and were manufactured on the now-ancient 32 nm process, whereas the M1 is made on TSMC’s currently bleeding edge 5 nm process.
So, to give x86-64 more of a fighting chance, I’m also including a 2019 16 inch MacBook Pro with a 6 core / 8 thread Intel Core i7-9750H processor and 32 GB of memory, a.k.a. one of the fastest Intel-based laptops that Apple currently sells.&lt;/p&gt;

&lt;p&gt;The first three test scenes are the same as last time: a standard Cornell Box, the glass teacup with ice seen in my &lt;a href="https://blog.yiningkarlli.com/2019/05/nested-dielectrics.html"&gt;Nested Dielectrics post&lt;/a&gt;, and the bedroom scene from my &lt;a href="https://blog.yiningkarlli.com/2020/02/shadow-terminator-in-takua.html"&gt;Shadow Terminator in Takua post&lt;/a&gt;.
Last time these three scenes were chosen since they fit in the 4 GB memory constraint that the Raspberry Pi 4B and the 2015 MacBook Air both have.
This time though, since the M1 Mac Mini has a much more modern 16 GB of memory, I’m including one more scene: &lt;a href="https://blog.yiningkarlli.com/2018/02/scandinavian-room-scene.html"&gt;my Scandinavian Room scene&lt;/a&gt;, as seen in Figure 1 of this post.
The Scandinavian Room scene is a much more realistic example of the type of complexity found in a real production render, and has much more interesting and difficult light transport.
Like before, the Cornell Box is rendered to 16 SPP using unidirectional path tracing and at 1024x1024 resolution, the Tea Cup is rendered to 16 SPP using VCM and at 1920x1080 resolution, and the Bedroom is rendered to 16 SPP using unidirectional path tracing and at 1920x1080 resolution.
Because the Scandinavian Room scene takes much longer to render due to being a much more complex scene, I’m rendered the Scandinavian Room scene to 4 SPP using unidirectional path tracing and at 1920x1080 resolution.
I left Takua Renderer’s texture caching system enabled for the Scandinavian Room scene, in order to test that the texture caching system was working correctly on arm64.
Using the texture cache could alter the performance results slightly due to disk latency to fetch texture tiles to populate the texture cache, but the texture cache hit rate after the first SPP on this scene is so close to 100% that it basically doesn’t make a difference after the first SPP, so I actually rendered the Scandinavian Room scene to 5 spp and counted the times for the last 4 and threw out timings for the first SPP.&lt;/p&gt;

&lt;p&gt;Each test’s recorded time below is the average of the three best runs, chosen out of five runs in total for each processor.
For the M1 processor, I actually did three different types of runs, which are presented separately below.
I did one test with the native arm64 build of Takua Renderer, a second test with a version of the native arm64 build hacked to run with the M1’s hardware TSO mode enabled, and a third test running the x86-64 build on the M1 through Rosetta 2.
Also, for the Cornell Box, Tea Cup, and Bedroom scenes, I used Takua Renderer’s internal BVH implementation instead of Embree in order to match the tests from the last post, which were done before I had Embree working on arm64.
The Scandinavian Room tests use Embree as the traverser instead.&lt;/p&gt;

&lt;p&gt;Here are the results:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CORNELL BOX&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1024x1024, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;440.627 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1762.51 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;272.053 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1088.21 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;36.6183 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1139.79 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;41.7408 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 500.890 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;28.0611 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 224.489 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;32.5621 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 260.497 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;42.5824 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 340.658 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;TEA CUP&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, VCM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2205.072 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 8820.32 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2237.136 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 8948.56 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;174.872 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 5593.60 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;158.729 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1904.75 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;115.253 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 922.021 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;128.299 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1026.39 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;164.289 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1314.31 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;BEDROOM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5653.66 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 22614.64 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4900.54 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 19602.18 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;310.35 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 9931.52 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;362.29 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 4347.44 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;256.68 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 2053.46 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;291.69 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 2333.50 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;366.01 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 2928.08 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;SCANDINAVIAN ROOM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;119.16 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 3813.18 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;151.81 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1821.80 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;109.94 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 879.55  s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;124.95 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 999.57  s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;153.66 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1229.32 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The first takeaway from these new results is that Intel CPUs have advanced enormously over the past decade!
My wife’s 2019 16 inch MacBook Pro comes extremely close to matching my 2012 dual Xeon workstation’s performance on most tests and even wins on the Tea Cup scene, which is extremely impressive considering that the Intel Core i7-9750H cost around a tenth as much MSRP than the dual Intel Xeon E5-2680s would have cost new in 2012, and the Intel Core i7-9750H also uses 5 times less energy at peak than the dual Intel Xeon E5-2680s do at peak.&lt;/p&gt;

&lt;p&gt;The real story though, is in the Apple M1 processor.
Quite simply, the Apple M1 processor completely smokes everything else on the list, often by margins that are downright stunning.
Depending on the test, the M1 processor beats the dual Xeons by anywhere between 10% and 30% in wall time and beats the 2019 MacBook Pro’s Core i7 by even more.
In terms of core-seconds, which is a measure of the overall performance of each processor core that approximates how long the render would have taken completely single-threaded, the M1’s wins are simply stunning; each of the M1’s processor cores is somewhere betweeen 4 to 6 times faster than the dual Xeons’ individual cores and between 2 to 3 times faster than the more contemporaneous Intel Core i7-9750H’s individual cores.
The even more impressive result from the M1 though, is that even running the x86-64 version of Takua Renderer using Rosetta 2’s dynamic translation system, the M1 still matches &lt;em&gt;or beats&lt;/em&gt; the Intel Core i7-9750H.&lt;/p&gt;

&lt;p&gt;Below is the breakdown of energy utilization for each test; the total energy used for each render is the wall clock render time multiplied by the maximum TDP of each processor to get watt-seconds, which is then divided by 3600 seconds per hour to get watt-hours.
Maximum TDP is used since Takua Renderer pushes processor utilization to 100% during each render.
As a point of comparison, I’ve also included all of the results from my previous post:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CORNELL BOX&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1024x1024, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.4895 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.1336 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;2.6450 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;45 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.5218 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.1169 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.1357 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.1774 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;TEA CUP&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, VCM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;2.4500 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;9.3214 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;12.6297 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;45 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.9841 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.4802 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.5346 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.6845 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;BEDROOM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;6.2819 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;20.4189 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;22.4142 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;45 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;4.5286 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.0695 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.2154 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.5250 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;SCANDINAVIAN ROOM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;8.606 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i7-9750H:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;45 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.8976 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Native:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.4581 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 TSO-Enabled:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.5206 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Apple M1 Rosetta 2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.6403 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Again the first takeaway from these results is just how much processor technology has improved overall in the past decade; the total energy usage by the modern Intel Core i7-9750H and Apple M1 is leaps and bounds better than the dual Xeons from 2012.
Compared to what was essentially the most powerful workstation hardware that Intel sold a little under a decade ago, a modern Intel laptop chip can now do the same work in about the same amount of time for roughly 5x &lt;em&gt;less&lt;/em&gt; energy consumption.&lt;/p&gt;

&lt;p&gt;The M1 though, once again entirely lives in a class of its own.
Running the native arm64 build, the M1 processor is &lt;em&gt;4 times more energy efficient&lt;/em&gt; than the Intel Core i7-9750H to complete the same task.
The M1’s maximum TDP is only a third of the Intel Core i7-9750H’s maximum TDP, but the actual final energy utilization is a quarter because the M1’s faster performance means that the M1 runs for much less time than the Intel Core i7-9750H.
In other words, running native code, the M1 is both faster &lt;em&gt;and&lt;/em&gt; more energy efficient than the Intel Core i7-9750H.
This result wouldn’t be impressive if the comparison was between the M1 and some low-end, power-optimized ultra-portable Intel chip, but that’s not what the comparison is with.
The comparison is with the Intel Core i7-9750H, which is a high-end, 45 W maximum TDP part that MSRPs for &lt;span&gt;$&lt;/span&gt;395.
In comparison, the M1 is estimated to cost about &lt;span&gt;$&lt;/span&gt;50, and the entire M1 Mac Mini only has a 39 W TDP total at maximum load; the M1 itself is reported to have a 15 W maximum TDP.
Where the comparison between the M1 and the Intel Core i7-9750H gets even more impressive is when looking at the M1’s energy utilization running x86-64 code under Rosetta 2: the M1 is &lt;em&gt;still&lt;/em&gt; about 3 times more energy efficient than the Intel Core i7-9750H to do the same work.
Put another way, the M1 is an arm64 processor that can run emulated x86-64 code &lt;em&gt;faster than a modern native x86-64 processor that cost 5x more and uses 3x more energy can&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Another interesting observation is that the for the same work, the M1 is actually more energy efficient than the Raspberry Pi 4B as well!
In the case of the Raspberry Pi 4B comparison, while the M1’s maximum TDP is 3.75x higher than the Broadcom BCM2711’s maximum TDP, the M1 is also around 20x faster to complete each render; the M1’s massive performance uplift more than offsets the higher maximum TDP.&lt;/p&gt;

&lt;p&gt;Another aspect of the M1 processor that I was curious enough about to test further is the M1’s big.LITTLE implementation.
The M1 has four “Firestorm” cores and four “Icestorm” cores, where Firestorm cores are high-performance but also use a ton of energy, and Icestorm cores are extremely energy-efficient but are also commensurately less performant.
I wanted to know just how much of the overall performance of the M1 was coming from the big Firestorm cores, and just how much slower the Icestorm cores are.
So, I did a simple thread scaling test where I did successive renders using 1 all the way through 8 threads.
I don’t know of a good way on the M1 to explicitly pin which kind of core a given thread runs on on; on the A12Z, the easy way to pin to the high-performance cores is to just enable hardware TSO mode since the A12Z only has hardware TSO on the high-performance cores, but this is no longer the case on the M1.
But, I figured that the underlying operating system’s thread scheduler should be smart enough to notice that Takua Renderer is a job that pushes performance limits, and schedule any available high-performance cores before using the energy-efficiency cores too.&lt;/p&gt;

&lt;p&gt;Here are the results on the Scandinavian Room scene for native arm64, native arm64 with TSO-enabled, and x86-64 running using Rosetta 2:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;M1 Native&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt;Threads:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;WT Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Core-Seconds:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CS Multiplier:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;1 (1 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;575.6787 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;575.6786 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;2 (2 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;292.521 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.9679x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;585.042 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9839x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;3 (3 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;197.04 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2.9216x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;591.1206 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9738x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;4 (4 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;148.9617 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.8646x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;595.8466 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9661x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;5 (4 big, 1 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;137.6307 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.1827x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;688.1536 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8365x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;6 (4 big, 2 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;128.9223 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.4653x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;773.535 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.7442x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;7 (4 big, 3 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;120.496 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.7775x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;843.4713 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.6825x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;8 (4 big, 4 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;109.9437 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.2361x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;879.5476 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.6545x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;M1 TSO-Enabled&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt;Threads:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;WT Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Core-Seconds:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CS Multiplier:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;1 (1 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;643.9846 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;643.9846 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;2 (2 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;323.8036 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.9888x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;647.6073 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9944x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;3 (3 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;220.4093 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2.9217x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;661.2283 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9739x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;4 (4 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;168.9733 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.8111x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;675.8943 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9527x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;5 (4 big, 1 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;153.849 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.1858x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;769.2453 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8371x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;6 (4 big, 2 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;143.7426 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.4801x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;862.4576 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.7466x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;7 (4 big, 3 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;132.7233 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.8520x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;929.0633 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.6931x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;8 (4 big, 4 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;124.9456 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.1541x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;999.5683 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.6442x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;M1 Rosetta 2&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: center;"&gt;Threads:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;WT Speedup:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Core-Seconds:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CS Multiplier:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;1 (1 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;806.6843 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;806.68433 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.0x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;2 (2 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;412.186 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1.9570x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;824.372 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9785x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;3 (3 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;280.875 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2.8720x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;842.625 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9573x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;4 (4 big, 0 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;207.0996 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;3.8951x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;828.39966 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.9737x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;5 (4 big, 1 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;189.322 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.2609x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;946.608 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.8521x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;6 (4 big, 2 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;175.0353 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.6086x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1050.2133 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.7681x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;7 (4 big, 3 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;166.1286 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4.8557x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1162.9033 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.6936x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: center;"&gt;8 (4 big, 4 LITTLE)&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;153.6646 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5.2496x&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;1229.3166 s&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;0.6562x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;In the above table, WT speedup is how many times faster that given test was than the baseline single-threaded render; WT speedup is a measure of multithreading scaling efficiency.
The closer WT speedup is to the number of threads, the better the multithreading scaling efficiency; with perfect multithreading scaling efficiency, we’d expect the WT speedup number to be exactly the same as the number of threads.
The CS Multiplier value is another way to measure multithreading scaling efficiency; the closer the CS Multiplier number is to exactly 1.0, the closer each test is to achieving perfect multithreading scaling efficiency.&lt;/p&gt;

&lt;p&gt;Since this test ran Takua Renderer in unidirectional path tracing mode, and depth-first unidirectional path tracing is largely trivially parallelizable using a simple parallel_for (okay, it’s not so simple once things like texture caching and things like learned path guiding data structures come into play, but close enough for now), my expectation for Takua Renderer is that on a system with homogeneous cores, multithreading scaling should be very close to perfect (assuming a fair scheduler in the underlying operating system).
Looking at the first four threads, which are all using the M1’s high-performance “big” Firestorm cores, close-to-perfect multithreading scaling efficiency is exactly what we see.
Adding the next four threads though, which use the M1’s low-performance energy-efficient “LITTLE” Icestorm cores, the multithreading scaling efficiency drops dramatically.
This drop in multithreading scaling efficiency is expected, since the Icestorm cores are far less performant than the Firestorm cores, but the &lt;em&gt;amount&lt;/em&gt; that multithreading scaling efficiency drops by is what is interesting here, since that drop gives us a good estimate of just how less performant the Icestorm cores are.
The answer is that the Icestorm cores are roughly a quarter as performant as the high-performance Firestorm cores.
However, according to Apple, the Icestorm cores only use a tenth of the energy that the Firestorm cores do; a 4x performance drop for a 10x drop in energy usage is very impressive.&lt;/p&gt;

&lt;div id="2021-07-31-conclusion"&gt;&lt;/div&gt;
&lt;h2 id="conclusion-to-part-2"&gt;Conclusion to Part 2&lt;/h2&gt;

&lt;p&gt;There’s really no way to understate what a colossal achievement Apple’s M1 processor is; compared with almost every modern x86-64 processor in its class, it achieves significantly more performance for much less cost and much less energy.
The even more amazing thing to think about is that the M1 is Apple’s &lt;em&gt;low end&lt;/em&gt; Mac processor and likely will be the slowest arm64 chip to ever power a shipping Mac (the A12Z powering the DTK is slower, but the DTK is not a shipping consumer device); future Apple Silicon chips will only be even faster.
Combined with other extremely impressive high-performance arm64 chips such as Fujistu’s A64FX supercomputer CPU, NVIDIA’s upcoming Grace GPU, Ampere’s monster 80-core Altra CPU, and Amazon’s Graviton2 CPU used in AWS, I think the future for high-end arm64 looks very bright.&lt;/p&gt;

&lt;p&gt;That being said though, x86-64 chips aren’t exactly sitting still either.
In the comparisons above I don’t have any modern AMD Ryzen chips, entirely because I personally don’t have access to any Ryzen-based systems at the moment.
However, AMD has been making enormous advancements in both performance and energy efficiency with their Zen series of x86-64 microarchitectures, and the current Zen 3 microarchitecture thoroughly bests Intel in both performance and energy efficiency.
Intel is not sitting still either, with ambitious plans to fight AMD for the x86-64 performance crown, and I’m sure both companies have no intention of taking the rising threat from arm64 lying down.&lt;/p&gt;

&lt;p&gt;We are currently in a very exciting period of enormous advances in modern processor technology, with multiple large, well funded, very serious players competing to outdo each other.
For the end user, no matter who comes out on top and what happens, the end result is ultimately a win- faster chips using less energy for lower prices.
Now that I have Takua Renderer fully working with parity on both x86-64 and arm64, I’m ready to take advantage of each new advancement!&lt;/p&gt;

&lt;div id="2021-07-31-acknowledgements"&gt;&lt;/div&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;

&lt;p&gt;For both the last post and this post, I owe &lt;a href="https://twitter.com/superfunc"&gt;Josh Filstrup&lt;/a&gt; an enormous debt of gratitude for proofreading, giving plenty of constructive and useful feedback and suggestions, and for being a great discussion partner over the past year on many of the topics covered in this miniseries.
Also an enormous thanks to my wife, &lt;a href="http://harmonymli.com/"&gt;Harmony Li&lt;/a&gt;, who was patient with me while I took ages with the porting work and then was patient again with me as I took even longer to get these posts written.
Harmony also helped me brainstorm through various topics and provided many useful suggestions along the way.
Finally, thanks to you, the reader, for sticking with me through these two giant blog posts!&lt;/p&gt;

&lt;div id="2021-07-31-references"&gt;&lt;/div&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;

&lt;p&gt;Apple. 2020. &lt;a href="https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code"&gt;Addressing Architectural Differences in Your macOS Code&lt;/a&gt;. Retrieved July 19, 2021.&lt;/p&gt;

&lt;p&gt;Apple. 2020. &lt;a href="https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary"&gt;Building a Universal macOS Binary&lt;/a&gt;. Retrieved June 22, 2021.&lt;/p&gt;

&lt;p&gt;Apple. 2020. &lt;a href="https://developer.apple.com/videos/play/wwdc2020/10686/"&gt;Explore the New System Architecture of Apple Silicon Macs&lt;/a&gt;. Retrieved June 15, 2011.&lt;/p&gt;

&lt;p&gt;Apple. 2020. &lt;a href="https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms"&gt;Writing ARM64 Code for Apple Platforms&lt;/a&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2015. &lt;a href="https://developer.arm.com/documentation/den0024/a/The-ABI-for-ARM-64-bit-Architecture/Register-use-in-the-AArch64-Procedure-Call-Standard/Parameters-in-general-purpose-registers"&gt;Parameters in General-Purpose Registers&lt;/a&gt;. In &lt;em&gt;ARM Cortex-A Series Programmer’s Guide for ARMv8-A&lt;/em&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2017. &lt;a href="https://developer.arm.com/documentation/100442/0100/register-descriptions/aarch64-system-registers/actlr-el1--auxiliary-control-register--el1"&gt;ACTLR_EL1, Auxiliary Control Register, EL1&lt;/a&gt;. In &lt;em&gt;ARM Cortex-A55 Core Technical Reference Manual&lt;/em&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;Martin Chang. 2017. &lt;a href="https://mightynotes.wordpress.com/2017/01/24/porting-intel-embree-to-arm/"&gt;Porting Intel Embree to ARM&lt;/a&gt;. In &lt;em&gt;MightyNotes: A Developer’s Blog&lt;/em&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Erik Engheim. 2021. &lt;a href="https://medium.com/swlh/apples-m1-secret-coprocessor-6599492fc1e1"&gt;The Secret Apple M1 Coprocessor&lt;/a&gt;. Retrieved July 23, 2021.&lt;/p&gt;

&lt;p&gt;Trevor Harmon. 2003. &lt;a href="https://www.drdobbs.com/architecture-and-design/portability-the-arm-processor/184405435#"&gt;Portability &amp;amp; the ARM Processor&lt;/a&gt;. In &lt;em&gt;Dr. Dobb’s&lt;/em&gt;. Retrieved July 19, 2021.&lt;/p&gt;

&lt;p&gt;Shawn Hickey, Matt Wojiakowski, Shipa Sharma, David Coulter, Theano Petersen, Mike Jacobs, and Michael Satran. 2021. &lt;a href="https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm-x86-emulation"&gt;How x86 Emulation works on ARM&lt;/a&gt;. In &lt;em&gt;Windows on ARM&lt;/em&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;Saagar Jha. 2020. &lt;a href="https://github.com/saagarjha/TSOEnabler"&gt;TSOEnabler&lt;/a&gt;. Retrieved June 15, 2021.&lt;/p&gt;

&lt;p&gt;Dougall Johnson. 2020. &lt;a href="https://gist.github.com/dougallj/7a75a3be1ec69ca550e7c36dc75e0d6f"&gt;AMX: Apple Matrix Coprocessor&lt;/a&gt;. Retrieved July 23, 2021.&lt;/p&gt;

&lt;p&gt;LLVM Project. 2021. &lt;a href="https://llvm.org/docs/CommandGuide/llvm-lipo.html"&gt;llvm-lipo - LLVM Tool for Manipulating Universal Binaries&lt;/a&gt;. Retrieved June 22, 2021.&lt;/p&gt;

&lt;p&gt;LLVM Project. 2021. &lt;a href="https://llvm.org/docs/CommandGuide/llvm-objdump.html"&gt;llvm-objdump - LLVM’s object file dumper&lt;/a&gt;. Retrieved June 22, 2021.&lt;/p&gt;

&lt;p&gt;Koh M. Nakagawa. 2021. &lt;a href="https://ffri.github.io/ProjectChampollion/part1/"&gt;Reverse-Engineering Rosetta 2 Part 1: Analyzing AOT Files and the Rosetta 2 Runtime&lt;/a&gt;. In &lt;em&gt;Project Champollion&lt;/em&gt;. Retrieved June 23, 2021.&lt;/p&gt;

&lt;p&gt;Koh M. Nakagawa. 2021. &lt;a href="https://ffri.github.io/ProjectChampollion/part2/"&gt;Reverse-Engineering Rosetta 2 Part 2: Analyzing Other aspects of Rosetta 2 Runtime and AOT Shared Cache Files&lt;/a&gt;. In &lt;em&gt;Project Champollion&lt;/em&gt;. Retrieved June 23, 2021.&lt;/p&gt;

&lt;p&gt;Howard Oakley. 2020. &lt;a href="https://eclecticlight.co/2020/07/28/universal-binaries-inside-fat-headers/"&gt;Universal Binaries: Inside Fat Headers&lt;/a&gt;. In &lt;em&gt;The Eclectic Light Company&lt;/em&gt;. Retrieved June 22, 2021.&lt;/p&gt;

&lt;p&gt;Howard Oakley. 2021. &lt;a href="https://eclecticlight.co/2021/07/27/code-in-arm-assembly-rounding-and-arithmetic/"&gt;Code in ARM Assembly Series&lt;/a&gt;. In &lt;em&gt;The Eclectic Light Company&lt;/em&gt;. Retrieved July 19, 2021.&lt;/p&gt;

&lt;p&gt;OSDev. 2018. &lt;a href="https://wiki.osdev.org/System_V_ABI"&gt;System V ABI&lt;/a&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;Matt Pharr. 2018. &lt;a href="https://pharr.org/matt/blog/2018/04/30/ispc-all"&gt;The Story of ISPC&lt;/a&gt;. In &lt;em&gt;Matt Pharr’s Blog&lt;/em&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Matt Pharr and William R. Mark. 2012. &lt;a href="https://doi.org/10.1109/InPar.2012.6339601"&gt;ispc: A SPMD compiler for high-performance CPU programming&lt;/a&gt;. In &lt;em&gt;Proc. of nnovative Parallel Computing (InPar 2012)&lt;/em&gt;. 184-196.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/"&gt;This Is Why They Call It a Weakly-Ordered CPU&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Marc Sweetgall. 2021. &lt;a href="https://blogs.windows.com/windowsdeveloper/2021/06/28/announcing-arm64ec-building-native-and-interoperable-apps-for-windows-11-on-arm/"&gt;Announcing ARM64EC: Building Native and Interoperable Apps for Windows 11 on ARM&lt;/a&gt;. In &lt;em&gt;Windows Developers Blog&lt;/em&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;Threedots. 2021. &lt;a href="https://threedots.ovh/blog/2021/02/cpus-with-sequential-consistency/"&gt;Arm CPUs with Sequential Consistency&lt;/a&gt;. In &lt;em&gt;Random Blog&lt;/em&gt;. Retrieved June 26, 2021.&lt;/p&gt;

&lt;p&gt;Ingo Wald. 2018. &lt;a href="https://ingowald.blog/2018/07/15/cfi-embree-on-arm-power/"&gt;Cfl: Embree on ARM/Power/…?&lt;/a&gt;. In &lt;em&gt;Ingo Wald’s Blog&lt;/em&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Amy Williams, Steve Barrus, Keith Morley, and Peter Shirley. 2005. &lt;a href="https://doi.org/10.1080/2151237X.2005.10129188"&gt;An Efficient and Robust Ray-Box Intersection Algorithm&lt;/a&gt;. _Journal of Graphics Tools) 10, 1 (Jan. 2005), 49-54.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Endianness"&gt;Endianess&lt;/a&gt;. Retrieved July 19, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/SIMD"&gt;SIMD&lt;/a&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_threads"&gt;Single Instruction, Multiple Threads&lt;/a&gt;. Retrieved July 18, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/SPMD"&gt;SPMD&lt;/a&gt;. Retrieved July 18, 2021.&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Sat, 31 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/07/porting-takua-to-arm-pt2.html</guid></item><item><title>Reducing variance in A/B testing with CUPED</title><link>https://bytepawn.com/reducing-variance-in-ab-testing-with-cuped.html</link><description>&lt;p&gt;I use Monte Carlo simulations of A/B tests to demonstrate CUPED, a method to use historic "before" data to reduce the variance in the measurement of the treatment lift.&lt;br /&gt;&lt;br /&gt; &lt;img alt="Historic lift" src="/images/cuped2.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 31 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/reducing-variance-in-ab-testing-with-cuped.html</guid></item><item><title>How to remove a file from Git history</title><link>https://nicolaiarocci.com/how-to-remove-a-file-from-git-history/</link><description>&lt;p&gt;Today I learned how to remove a file from a git repository while also cleaning
it from the history. When you delete it with &lt;code&gt;git rm&lt;/code&gt; or &lt;code&gt;git rm --cached&lt;/code&gt;,
tracks remain in the commit history (the reflog). That might not be a big deal,
but if the file has sensitive contents that you want to disappear from version
control entirely, then you also want it cleaned from the reflog. That&amp;rsquo;s when
&lt;a href="https://git-scm.com/docs/git-filter-branch"&gt;&lt;code&gt;git filter-branch&lt;/code&gt;&lt;/a&gt; comes to the rescue.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 30 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/how-to-remove-a-file-from-git-history/</guid></item><item><title>The System Upgrade</title><link>https://www.marginalia.nu/log/09-system-upgrade/</link><description>&lt;p&gt;Early this winter, when I set up the server that would eventually become marginalia.nu, I did so in order to try out some technology I thought looked cool (proxmox, zfs), and stuff I was exposed to at work and didn&amp;rsquo;t really see the point of so as to see if we could get on better terms with if I had more control (kubernetes).&lt;/p&gt;
&lt;p&gt;I based the system on ProxMox, a Linux based virtualization server, which ran a series of virtual machines and containers.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 30 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/09-system-upgrade/</guid></item><item><title>Glue Code is the Success Condition</title><link>https://blog.metaobject.com/2021/07/glue-code-is-success-condition.html</link><description>My previous post titled &lt;a href="https://blog.metaobject.com/2021/06/glue-dark-matter-of-software.html"&gt;Glue: the Dark Matter of Software&lt;/a&gt; may have given the &lt;a href="https://www.oreilly.com/radar/thinking-about-glue/"&gt;impression&lt;/a&gt; that I see glue code as exclusively a problem.   And I have to admit
that my follow-up (and reaction to Github's copilot) called &lt;a href="https://blog.metaobject.com/2021/06/don-generate-glueexterminate.html"&gt;Don't Generate Glue...Exterminate&lt;/a&gt; may not have done much to dissuade anyone of that 
impression, but I just couldn't resist the Dalek reference.&lt;p&gt;

However, I think it is important to remember that the fact that we have so much glue is a symptom of one of our
biggest &lt;em&gt;successes&lt;/em&gt; in software technology.  Even as recently as the late 80s and early 90s, we just 
didn't have all that much to glue together, and software reuse was the holy grail, the unobtainium of computing, both in its
desirability and unobtainability.&lt;p&gt;

Now we have reuse.  Boy do we have &lt;a href="https://stackoverflow.com/questions/48568097/how-to-count-the-number-of-installed-packages-including-dependencies"&gt;reuse&lt;/a&gt;!  We have so much reuse that we need tool support to manage all
the reuse.  As far as I can tell, all new programming languages now come with such tooling, and are considered
incomplete until they have it.&lt;p&gt;

The price of success is having a new set of problems, problems you never dreamed of before.&lt;p&gt;

So how will we solve these problems? &lt;p&gt; 

Data format adaptation, as suggested by the O'Reilly article?  Yes. Model-driven approaches that allow us or our tools
and languages to generate a lot of the more obvious adapter code?  Sounds good, why not?&lt;p&gt;

This one neat trick (click &lt;a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"&gt;here&lt;/a&gt;!) that will automatically
solve all these problems?  No.&lt;p&gt;

Simpler components, written with composability and minimization of dependencies in mind?  Surely.
Education, so developers get better at writing code that composes well without turning into
architecture astronauts?  Very much yes.&lt;p&gt;

However, my contention is that developers have a hard time with this in large part because our languages only
support implementing such glue, which is a start, but do not support &lt;em&gt;expressing&lt;/em&gt; it directly, or 
abstracting over it, encapsulating it, playing with it.  So new linguistic 
mechanisms like &lt;a href="http://objective.st"&gt;Objective-S&lt;/a&gt; are needed to help developers write better and
thus less glue code so we can better enjoy the fruits of our reusability success.&lt;p&gt;</description><author>metablog</author><pubDate>Thu, 29 Jul 2021 10:12:35 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/07/glue-code-is-success-condition.html</guid></item><item><title>Selfhost Plausible with a preexisting PostgreSQL Database</title><link>https://hartenfeller.dev/blog/selfhosted-plausible-existing-postgres</link><description>Out of the box self-hosting plausible expects you to create a new separate Postgres database. Here is my journey to make it work with a preexisting one.</description><author>Philipp Hartenfeller Blog RSS Feed</author><pubDate>Thu, 29 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://hartenfeller.dev/blog/selfhosted-plausible-existing-postgres</guid></item><item><title>Using software engineering principles to create joyful, autonomous teams | Tech Leaders Hub</title><link>https://adamcraven.com/writing/using-software-engineering-principles/</link><description/><author>Writing on Adam Craven</author><pubDate>Thu, 29 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/using-software-engineering-principles/</guid></item><item><title>Depression</title><link>https://josh.works/depression</link><description>&lt;p&gt;I’m starting to write more regularly these days. For a long time, I’ve hardly written anything, or only written when external circumstances required me to write something. For example, when I give a talk, I always create a page to “support” the talk, that I can link to in slides, like &lt;a href="https://josh.works/railsconf"&gt;https://josh.works/railsconf&lt;/a&gt; and &lt;a href="https://josh.works/gasb"&gt;https://josh.works/gasb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don’t apologize (usually) when replying to email “slowly”, and when others apologize to &lt;em&gt;me&lt;/em&gt; for their own “slow response”, I send them this article: &lt;a href="https://www.thecut.com/2017/04/stop-apologizing-for-the-delayed-response-in-our-emails.html"&gt;Let’s All Stop Apologizing for the Delayed Response in Our Emails&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve always written primarily for myself (current and future versions of me) and this post is no exception. It’s about depression, and my experience with it.&lt;/p&gt;

&lt;p&gt;A year ago, I began noticing unavoidable signs of depression in myself, and noticed that those things had been going on for about a year.&lt;/p&gt;

&lt;p&gt;There were a range of trying circumstances I found myself dealing with. The standard kit of “downsides of the human experience”: Death, injury, sickness, strife, despair. Obviously these unwanted influences were affecting the entire world in very public and well-discussed ways, but in specific ways (and in ways unrelated to Covid), these ill winds made intrusions into my own life.&lt;/p&gt;

&lt;p&gt;I also found that it felt like my depression was &lt;em&gt;choosing for me&lt;/em&gt; certain pathways that made it harder for me to do the kinds of self-care things I usually do. As this happened, my capacity to absorb difficulty diminished, and my ability to provide care for others also diminished. These phenomena were working together in ways that caused a negative feedback cycle.&lt;/p&gt;

&lt;p&gt;A few months ago, this thought started passing through my head:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hey, I should take a stab at writing about this depression thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, immediately after that, I’d think:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ew, I don’t want to do that. That’s uncomfortable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And of course, as is true in many situations, it’s healthy to do that which is uncomfortable.&lt;/p&gt;

&lt;p&gt;My depression is/was, in many ways, a reasonable response to certain circumstances (death in family, acute &lt;em&gt;and&lt;/em&gt; chronic health problems). But my depression &lt;em&gt;also&lt;/em&gt; has explanatory power for other visible phenomena. It is a response to some things, and it has &lt;em&gt;caused&lt;/em&gt; other things.&lt;/p&gt;

&lt;p&gt;I’ve always found difficult problems to be made more manageable by writing them down. Many other people, much wiser than I am, underscore the same phenomena.&lt;/p&gt;

&lt;h2 id="why-i-am-writing-about-depression-current-and-future-me-will-benefit-and-maybe-others-will-too"&gt;Why I am writing about depression: Current and future me will benefit (and maybe others will too)&lt;/h2&gt;

&lt;p&gt;I’ve not written very much in the last four years, and I’ve written almost nothing at all for the last year and a half. I’ve written 23 posts since 2020, and most of them were things that I “had” to write, to support conference talks I gave, presentations, or “explainers” for papers, tutorials, etc. Nothing where I wrote for the sake of making sense of my own life or the world around me.&lt;/p&gt;

&lt;p&gt;For contrast, I wrote over 50 posts in the prior two-year period; many of those posts &lt;em&gt;were&lt;/em&gt; contributing to me making sense of the world around me.&lt;sup id="fnref:comparing-trends"&gt;&lt;a class="footnote" href="#fn:comparing-trends" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;There’s other trends and concepts I want to write about, but the “story” doesn’t make sense until I can explain “Oh, yeah, I was super depressed for a long time, and that’s why I did these things, and didn’t do these other things.”&lt;/p&gt;

&lt;p&gt;I write these words primarily for myself, right now, while knowing that because of how the internet works, a future version of me might benefit when I re-read these words, or others will benefit.&lt;/p&gt;

&lt;p&gt;I’ve read a few posts on the topic, where people have expressed some degree of surprise, or they’ll have an “ahah” moment, when they realize they’ve been depressed (or exhibiting signs of depression) for an extended period of time.&lt;/p&gt;

&lt;p&gt;I’ve felt a sense of kinship to how others have talked about their own experiences with depression. For example: &lt;a href="https://zellwk.com/"&gt;https://zellwk.com/&lt;/a&gt;, &lt;a href="https://twitter.com/patio11/"&gt;https://twitter.com/patio11/&lt;/a&gt;, and many others. Most of the articles, talks, conversations I’ve read on the topic of experiencing depression, I don’t even remember many specific details. Just a sense of “Hm, this resonates”.&lt;/p&gt;

&lt;p&gt;Their words have been of benefit to me. In recent conversations with dear friends about depression, I’ve been finding the benefit and power of establishing a clear narrative, or a correlatory/causal chain.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;{circumstances} primed the pump for me experiencing depression. That depression was a reasonable response to those circumstances. It also has caused other changes in my life. Some of those changes are benign, some changes are self-sabotaging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, there is a “right time” to capture certain thoughts:&lt;/p&gt;

&lt;p&gt;&lt;img alt="write it now" src="/images/2020-07-21-write-it-now.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Anyway, all this said, I’m spending some time writing this all out. I’ve now started/edited this post a half-dozen or dozen times, and am finding my own thoughts around this topic to be getting clarified and improved as I try to make them legible and coherent. I’m already reaping benefit!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id="causal-arrows"&gt;Causal Arrows&lt;/h2&gt;

&lt;p&gt;It’s hard for me to offer enough qualifying statements about everything I’m writing here. Certain circumstances contributed to my depression. My depression, in turn, contributed to other circumstances. What follows is a bit of me free-associating through a narrative response to:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;what led to my depression&lt;/li&gt;
    &lt;li&gt;how did it manifest in my life?&lt;/li&gt;
    &lt;li&gt;how did I come to eventually observe it&lt;/li&gt;
    &lt;li&gt;now what?&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id="january-2020"&gt;January 2020&lt;/h3&gt;

&lt;p&gt;A few days after we got back to Colorado from spending Christmas working in the family bakery in NJ, Kristi’s dad died suddenly and unexpectedly.&lt;/p&gt;

&lt;p&gt;It was a tremendous loss, both in obvious ways, and in ways that are difficult to enumerate. Kristi was 30. We’d expected and hoped for many more years with her dad. He and his wife were a few months away from retiring. It’s a painful loss, and will always be painful.&lt;/p&gt;

&lt;p&gt;Her dad (who was a 3rd-generation baker, and had run a bakery in the town his entire life) was a fixture of his town; there was well over 500 people at his funeral. He (and by extension the bakery, and his entire family) positively impacted thousands of people.&lt;/p&gt;

&lt;p&gt;I spent a lot of time reflecting on my own life and my eventual death.&lt;/p&gt;

&lt;p&gt;Kristi and I were able to spend two months back east, in NJ, to help her mom and family pick up the pieces. An advantage of working in the tech industry: we’ve both worked remotely for many years, we have near infinite location flexibility.&lt;/p&gt;

&lt;h3 id="february-march-2020"&gt;February-March 2020&lt;/h3&gt;

&lt;p&gt;Shortly after spending those two months in NJ, which was a rather trying time, and after getting my annual performance bonus for 2019, I gave my notice at Proofpoint.&lt;/p&gt;

&lt;p&gt;There are always many reasons people change jobs, or in this case, quit jobs with no explicit plans to go back to full-time, W2 work, and this was no exception. I loved the team I was on, but it was the right time to move on to something else.&lt;/p&gt;

&lt;p&gt;Shortly after I gave my notice, but before my last day, Covid started being a thing, and the lockdowns started. (I was given many opportunities to &lt;em&gt;not&lt;/em&gt; quit, and instead ride out Covid with a well-paying engineering job. Proofpoint was in a strong spot, and was one of the companies that &lt;em&gt;grew&lt;/em&gt; because of the pandemic)&lt;/p&gt;

&lt;h3 id="april-june-2020"&gt;April-June, 2020&lt;/h3&gt;

&lt;p&gt;My last day of work was the last day of March; I’d had a climbing trip to Cuba with good friends planned, returning to Viñales, and that didn’t happen.&lt;/p&gt;

&lt;p&gt;I did some contract work for Turing, the software development program I went to in 2017. I have been mentoring students in the backend program for years, and I reached out to see if they were open to more of help, since I suddenly had a lot of time on my hands, and I always really enjoy helping others be successful in the industry. Turing was eager for help.&lt;/p&gt;

&lt;p&gt;I also did some engineering work on internal Turing school applications, like their enrollment and payments applications.&lt;/p&gt;

&lt;p&gt;I always take detailed notes, like when I added Rubocop to two of their applications. &lt;a href="https://github.com/josh-works/intermediate_ruby_obstacle_course/tree/main/rubocop"&gt;These notes will help anyone else who needs to do something similar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There were huge ups and downs doing this work. I had a material impact on both the short-term and long-term success of quite a few students. (Their words, not mine.)&lt;/p&gt;

&lt;p&gt;This kind of work has always been the most fulfilling work I’ve ever done. Despite extensive conversations with the leadership at Turing, I wasn’t able to make the small, meaningful, systematic improvements I’d wanted to make to some spots of the curriculum that I had strong visibility into, but no one else did.&lt;/p&gt;

&lt;p&gt;In the course of providing free, scalable help to Turing students, I’d made many video walk-throughs (&lt;a href="https://www.youtube.com/watch?v=aeAkLxr5diE"&gt;example&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=uYGS-DCNR-0&amp;amp;t=12s"&gt;example&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=hzFW_BRdHLY"&gt;example&lt;/a&gt;), received dozens of thankful messages from students, who said things like&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;These videos are why I made it through mod 1 and Turing. Thank you!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And I was consistently rebuffed by Turing leadership in attempts to formalize and upgrade these resources. This was discouraging, and has led to me stopping providing any aid to Turing students. All my work has been deleted from Turing repositories, and it continues to suffer from an extremely high dropout rate of enrolled students. I wish them the best, I think they were very close to being a reliably excellent software development training program.&lt;/p&gt;

&lt;h3 id="july-2020"&gt;July 2020&lt;/h3&gt;

&lt;p&gt;I remember training on a Tension block in Ocean City, NJ, early July 2020, using a very specific right hand grip, working on rehearsing one of the last hard moves on &lt;a href="https://www.mountainproject.com/route/115133440/apocalypse"&gt;Apocalypse&lt;/a&gt; in Rifle, CO.&lt;/p&gt;

&lt;p&gt;I’d &lt;em&gt;casually&lt;/em&gt; done hard-for-me boulder problems that spring&lt;sup id="fnref:hard-for-me-boulder-problems"&gt;&lt;a class="footnote" href="#fn:hard-for-me-boulder-problems" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;, and was excited/hopeful to see some of the bouldering power I’d obtained get me up some harder routes.&lt;/p&gt;

&lt;p&gt;And then in July, I got home, and just… stopped. I’ve been climbing for over 15 years now, and never have I ever taken and unforced break from climbing, other than a few weeks here and there, with some “active rest” to keep the tendons from thinking they’re not needed anymore, usually worked in between some training/performance cycles.&lt;/p&gt;

&lt;p&gt;My depression was particularly bad then, &lt;em&gt;but I didn’t even realize I was depressed&lt;/em&gt;; I just felt extremely anxious and sad most of my waking hours.&lt;/p&gt;

&lt;h3 id="august-october-2020"&gt;August-October 2020&lt;/h3&gt;

&lt;p&gt;When the pandemic started, Kristi and I had hoped for a small (or large) correction to the price of property in Golden.&lt;/p&gt;

&lt;p&gt;This did not happen. Golden continues to remain extremely unaffordable, for clear, obvious reasons, mostly related to continuing to have laws on the books that were written by actual racists, with the intent of keeping black people out of neighborhoods populated by white people.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://josh.works/full-copy-of-1922-atlanta-zone-plan"&gt;In 1922, politically powerful racists introduced ‘The Atlanta Zone Plan’&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This “Atlanta Zone Plan” is the template for all modern zoning regulations, and zoning regulations are one of the most powerful forces shaping cities (and all that happens within cities) today.&lt;/p&gt;

&lt;p&gt;The folks that created the Atlanta Zone Plan had been trying for a while to create some means of segregation that wouldn’t get struck down by the supreme court. Slavery was made illegal, a lot of Jim Crowe laws were eventually made illegal, redlining was made illegal, and this zoning thing was the first attempt by white supremacists to preserve their cultural and economic dominance that &lt;em&gt;wasn’t&lt;/em&gt; struck down by the supreme court.&lt;/p&gt;

&lt;p&gt;And this zoning paradigm exists today, perfectly preserved, across the entire country. (And the US has exported this paradigm to the rest of the world. Yaaaaayyyyy).&lt;/p&gt;

&lt;p&gt;Why do I talk about all this in a post about depression?&lt;/p&gt;

&lt;p&gt;Because Denver is hideously unaffordable, has zero good public transit, and most of the urban infrastructure is extremely hostile to individuals. All of this is a result of following racist laws.&lt;/p&gt;

&lt;p&gt;It’s demoralizing to have ones life and wellbeing so affected by such immoral and evil laws. Kristi and I eventually ended up paying almost $600k for a house in terrible condition in downtown Golden. I’ll be spending much time and money getting it to a tolerable condition.&lt;/p&gt;

&lt;h3 id="november-2020"&gt;November 2020&lt;/h3&gt;

&lt;p&gt;I injured my back in a significant way. I was unable to stand up straight for at least two months. My normal means of self-care and mood repair were taken from me, replaced with a painful reminder of the frailty of the human experience.&lt;/p&gt;

&lt;p&gt;Everyone kept saying “oh, back injuries suck, but you’ll be better soon.”. Almost no one had an encouraging word about simply &lt;em&gt;being&lt;/em&gt; injured. It was an extremely alienating and isolating time.&lt;/p&gt;

&lt;p&gt;Since I couldn’t do work around the house (for example, I struggled to move a frying pan around the cooktop), I figured I’d go back to work.&lt;/p&gt;

&lt;p&gt;I really couldn’t get excited about most of the opportunities that I was encountering. Hiring is so damn broken.&lt;/p&gt;

&lt;p&gt;Unfortunately, because we’d just moved, I didn’t have an office set up, or even a desk or monitor. So, I was doing interviews, sitting on a couch, propping the laptop on a pillow, in near-constant pain from an injured back, in a period of substantial emotional ill-health. I’m proud to stay I still got job offers, and turned down a very compelling full-time offer in favor of a high-risk, high-potential-reward role with &lt;a href="https://pashi.com/"&gt;Pashi&lt;/a&gt;, a manufacturing startup.&lt;sup id="fnref:writing"&gt;&lt;a class="footnote" href="#fn:writing" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Soham, the founder, splits his time between India and the US. A brilliant, hard-working, driven person. I very much enjoyed the work we did together, but the 12 hour timezone difference, coupled with most of the work being done in India/Europe in a synchronous way, made it difficult for me to keep context and quickly do good work, while still maintaining a wall between work and non-work time.&lt;/p&gt;

&lt;p&gt;We agreed to a month-long trial period, with the goal of me going full-time, with equity, working hard to grow Pashi in a few manufacturing verticals in the USA.&lt;/p&gt;

&lt;p&gt;I ended up pulling the plug half-way through the trial. External sources of stress, plus the removal of normal health-promoting activities, plus the time-zone difference and loose communication meant I was working until late in the evening, and then hopping on calls at 7a the next day with the team, and I wasn’t able to rest or recover. Soham was totally pleased with my work, and excited for what we were going to do, and sad when I told him I couldn’t continue.&lt;/p&gt;

&lt;p&gt;This was a tumultuous time for me, and a tumultuous experience. Kristi and I were experiencing profound relationship difficulties, I was extremely depressed, and I feel like I was operating at like 20% of my normal capacity.&lt;/p&gt;

&lt;h3 id="december-2020"&gt;December 2020&lt;/h3&gt;

&lt;p&gt;This was the first Christmas in the 13 years I’ve known Kristi that we &lt;em&gt;didn’t&lt;/em&gt; spend the week leading up to Christmas doing difficult and exhausting work to prepare for the busiest season in the bakery.&lt;/p&gt;

&lt;p&gt;It was something that had been long-promised in the family.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;By next year, the business will be sold and we can enjoy Christmas like normal people!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bakery &lt;em&gt;had&lt;/em&gt; sold. We &lt;em&gt;could&lt;/em&gt; enjoy Christmas like normal people. Except Wayne passed away, and we spent the Christmas without him. It sucked.&lt;/p&gt;

&lt;p&gt;Kristi and I had scheduled a week-long “counseling intensive”, which brought us a lot of relief&lt;sup id="fnref:restoring-the-soul"&gt;&lt;a class="footnote" href="#fn:restoring-the-soul" rel="footnote"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;There’s much more that could be said. I usually do end-of-year “review” posts, and I didn’t do one for 2019, because I got curb-stomped by 2020, and struggled to write anything public. Ditto 2020, and I’d like to get to much more important topics to write about than this sort of naval-gazing, but this feels like it helps unblock me, and here I am.&lt;/p&gt;

&lt;p&gt;The stories we tell ourselves are important.&lt;/p&gt;

&lt;p&gt;I heard someone say something recently:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Narrative is the only difference between pain and suffering.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was a true statement in the context in which it was said, and while it likely cannot be generalized to every situation, it’s a broadly helpful sentiment.&lt;/p&gt;

&lt;p&gt;I’m spending some time making sure I get my narrative right. Lots of the errors I make in this domain are to have errors that tend in the direction of despair and hopelessness.&lt;/p&gt;

&lt;h2 id="what-has-helped"&gt;What has helped&lt;/h2&gt;

&lt;p&gt;This topic will eventually get more discussion. I’m doing things that are helping. &lt;em&gt;I have friends that are enormously helpful&lt;/em&gt;, with and without awareness of what they’re helping with. In this section, asterisks and qualifications abound.&lt;/p&gt;

&lt;p&gt;A thread through what seems to have helped has been things like:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Understand that when depressed, my reasoning exhibits different tendencies than when not depressed. Externalize my reasoning and emotions, let trusted friends weigh in on it. Trust them, and generally do what they suggest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Being comfortable labelling myself as depressed, based on easy external metrics (DSM V, the many resources available to diagnose oneself)&lt;/li&gt;
  &lt;li&gt;Treating my depression as an illness instead of feeling shame (from myself or from others) for being depressed.&lt;/li&gt;
  &lt;li&gt;Talking about it with dear and trusted friends. (I’ll expand on this substantially soon.)&lt;/li&gt;
  &lt;li&gt;Making a concerted effort to trust my brain and emotions less when feeling sad, and let “feeling sad” trigger that intentional downgrading of trust in myself.&lt;/li&gt;
  &lt;li&gt;Imagine I had a friend who said:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Josh, I’m feeling rather depressed today. I’d like to spend an hour doing something that promotes wellness, because I know I should, but because I’m depressed, I’m struggling to identify or do that thing. What would you recommend?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How would that conversation unfold? What would I be trying to accomplish for that friend? What would I recommend? How would I follow up? OK, can I do that for myself?&lt;/p&gt;

&lt;p&gt;If not, I should go ask a friend that question.&lt;/p&gt;

&lt;h4 id="footnotes"&gt;Footnotes&lt;/h4&gt;

&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:comparing-trends"&gt;
      &lt;p&gt;It’s not a perfect strategy, but a way I grew to observe my own depression was to find ways that my behaviors had changed compared to times that I know I wasn’t depressed. Writing has always been a big part of how I operate in the world, and I observed a strong aversion to any kind of biographical writing, which has historically not been the case. &lt;a class="reversefootnote" href="#fnref:comparing-trends"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:hard-for-me-boulder-problems"&gt;
      &lt;p&gt;I did a few v8s in a single session, sometimes after just four or five attempts. Given that I’d never imagined myself a “strong climber”, I never thought V10 (or 5.13+) was attainable to me. Turns out it is. To most normal people, V10 is very, very hard. I also know some V13+ climbers that make V10 look easy and casual. ¯\_(ツ)_/¯ &lt;a class="reversefootnote" href="#fnref:hard-for-me-boulder-problems"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:writing"&gt;
      &lt;p&gt;I got the job, and the opportunity, after Soham just clicked around this website a bit, and &lt;a href="https://josh.works/coffee"&gt;set up a coffee&lt;/a&gt;. We moved quick, he paid me for the first two weeks of work, and I quickly put together sales collateral and explainers to help some early critical sales opportunities land. &lt;a class="reversefootnote" href="#fnref:writing"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:restoring-the-soul"&gt;
      &lt;p&gt;We went to &lt;a href="https://restoringthesoul.com/"&gt;Restoring The Soul&lt;/a&gt;, and indeed, our souls were restored. &lt;a class="reversefootnote" href="#fnref:restoring-the-soul"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Wed, 28 Jul 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/depression</guid></item><item><title>Hamilton is an Obnoxious Aggressive</title><link>http://evantravers.com/articles/2021/07/28/hamilton-is-an-obnoxious-aggressive/</link><description>&lt;p&gt;I'm in the middle of reading &lt;em&gt;Radical Candor&lt;/em&gt; by Kim Scott… so the quadrants of guidance labels are kind of floating at the top of my brain. As a &lt;em&gt;quick&lt;/em&gt; recap:&lt;/p&gt;
&lt;p&gt;Scott presents the following diagram:&lt;/p&gt;
&lt;p&gt;&lt;img alt="radical candor quadrants" src="https://images.evantravers.com/articles/2021/07/radical.png" /&gt;&lt;/p&gt;
&lt;p&gt;The ideal scenario is Radical Candor&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-definition" id="fnref-definition"&gt;1&lt;/a&gt;&lt;/sup&gt;, challenging directly while caring personally. The quickest way to understand the idea is to imagine a colleague entering the room with his fly down:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Radical Candor&lt;/strong&gt;: immediately walks up, privately whispers: &amp;quot;Hey, your fly is down. You are good… nobody saw but me. You ok?&amp;quot;&lt;br /&gt;
&lt;strong&gt;Obnoxious Aggressive&lt;/strong&gt;: Stretches and yells: &amp;quot;Hey, Davis! Your fly is down… fix it.&amp;quot;&lt;br /&gt;
&lt;strong&gt;Manipulative Insincerity&lt;/strong&gt;: Turns to their computer and direct messages ten colleagues: &amp;quot;Davis walked out with his fly down, lol.&amp;quot;&lt;br /&gt;
&lt;strong&gt;Ruinous Empath&lt;/strong&gt;: Freezes, and thinks &amp;quot;Oh no! I don't want to embarrass them, what if they hate me? Surely they'll realize it before walking into that board meeting… no one will notice!&amp;quot;&lt;/p&gt;
&lt;p&gt;It honestly troubles me that the second most effective guidance style is Obnoxious Aggressive. Scott muses that this may be the &amp;quot;jerk's advantage&amp;quot;: even without personal care a direct challenger is still effective as a leader… but may leave a trail of bodies in his wake.&lt;/p&gt;
&lt;p&gt;Which brings me to our favorite ten-dollar founding father without a father… what's his name? I think you know…&lt;/p&gt;
&lt;div style="padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
  
&lt;/div&gt;
&lt;p&gt;Thinking about the obnoxious aggressive personality in the light of the like-able Hamilton has helped me understand a bit more. Obnoxious aggressive is an effective, if destructive, communication style and contributes to Hamilton's success. His frenemy, Burr, sings in frustration:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hamilton doesn’t hesitate.&lt;br /&gt;
He exhibits no restraint.&lt;br /&gt;
He takes and he takes and he takes&lt;br /&gt;
and he keeps winning anyway.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In contrast, Burr displays more &amp;quot;slick empathy&amp;quot; than the bombastic Hamilton, (who manages to out-brag and out-boast a cast of &lt;em&gt;very&lt;/em&gt; forceful characters!) Burr's mantra struts into the forefront in his first interaction with Hamilton… providing some free advice:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;talk less, smile more. Don't let them know what you're against or what you're for…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the definition of what Scott calls Manipulative Insincerity. Someone who is able to get what they want by detecting emotional needs but playing them.&lt;/p&gt;
&lt;p&gt;The show is full of characters shouting direct guidance… but Washington stands tall: His lyrics seem to embody the idea of Radical Candor: caring deeply, but challenging directly. His paternal relationship with Hamilton both recognizes Hamilton's deep need for approval and family and provides some examples of good guidance:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WASHINGTON&lt;br /&gt;
It’s alright, you want to fight, you’ve got a hunger. I was just like you when I was younger.&lt;br /&gt;
Head full of fantasies of dyin’ like a martyr?&lt;br /&gt;
HAMILTON Yes.&lt;br /&gt;
WASHINGTON&lt;br /&gt;
Dying is easy, young man. Living is harder.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With three of Kim Scott's labels covered I started searching the show for an example of the last quadrant of guidance: the Ruinous Empath. This is unfortunately the one I fall into the most. It's a bit of a stretch, but I feel the closest contender is Angelica in the show-stopping toast &lt;em&gt;Satisfied&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I know my sister like I know my own mind&lt;br /&gt;
You will never find&lt;br /&gt;
Anyone as trusting or as kind&lt;br /&gt;
If I tell her that I love him she'd be silently resigned&lt;br /&gt;
He'd be mine&lt;br /&gt;
She would say, &amp;quot;I'm fine.&amp;quot;&lt;br /&gt;
[Angelica and Company:]&lt;br /&gt;
She'd be lying.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Angelica knows the truth of how she feels. She can see the danger in Hamilton's character and position. Despite this she doesn't speak because she doesn't want to hurt Eliza's heart… setting in motion a course of events that will end with at &lt;em&gt;least&lt;/em&gt; three broken hearts.&lt;/p&gt;
&lt;p&gt;Every person loves the strengths and weaknesses of his personality type… and despises other types' weaknesses. I am sadly no exception. Hamilton is an excellent character to examine the &amp;quot;obnoxious aggressive&amp;quot; communication style: I can empathize with Hamilton's motivations that drive his communication, because the show shows us his internal struggles and frailties. I will keep on looking for different personality and communication styles in fiction… it's a good way to learn to recognize patterns without being emotionally affected by the actions.&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-definition"&gt;
&lt;p&gt;Scott regrets saying &amp;quot;radical&amp;quot; now, she meant &amp;quot;new and novel&amp;quot; but people have taken that to mean &amp;quot;extreme without restraint.&amp;quot; &lt;a class="footnote-backref" href="#fnref-definition"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Wed, 28 Jul 2021 10:07:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/07/28/hamilton-is-an-obnoxious-aggressive/</guid></item><item><title>The Lucid Motors Configurator is A Video Game Streaming to Your Browser</title><link>https://blog.skz.dev/lucid-configurator-is-a-streaming-video-game</link><description>&lt;p class="description"&gt;
    It makes sense when you realize video game engines are the best simulators out there.
&lt;/p&gt;

&lt;h2 id="a-clever-solution"&gt;A Clever Solution&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.barrons.com/articles/lucid-churchill-merger-ipo-ev-51627308359"&gt;Lucid Motors went public on Monday&lt;/a&gt;, and although I am not going to be buying a Lucid anytime soon, I found myself looking at &lt;a href="https://www.lucidmotors.com/air/configure/"&gt;their vehicle configurator&lt;/a&gt; out of curiosity. It is a piece of art. It allows you to observe and interact with the vehicle from almost every direction, inside and out:&lt;/p&gt;

&lt;div style="width: 100%; height: 100%;"&gt;&lt;/div&gt;
&lt;p class="caption video-caption"&gt;Lucid's configurator for their flagship elite vehicle, the Lucid Air Dream.&lt;/p&gt;

&lt;p&gt;You can simulate various colours and trims, open and close the doors, and even retract the center console display. The backdrop of the Golden Gate bridge reflects off the glassy puddles. The configurator preserves all sorts of details of the vehicle, including lights under the door handles that activate when you are using them. I feel like I got an accurate enough feel for the vehicle, I don’t even have to see it in person anymore.&lt;/p&gt;

&lt;p&gt;So how did Lucid manage to pull this off? Can you expect the average user’s web browser to render a scene like this in real-time, reflections and all? The answer is probably not. It works because the scene you are seeing is rendered with Unity, a video game engine, running in the cloud - offloading the intense computational load to an Amazon server - and streamed back to your browser via WebRTC - a peer-to-peer video streaming protocol.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="the-model"&gt;The Model&lt;/h2&gt;

&lt;p&gt;Initially, I naively assumed that the model was being rendered in my browser via the latest &lt;a href="https://en.wikipedia.org/wiki/WebGL"&gt;WebGL library&lt;/a&gt; (an API for rendering 2D and 3D graphics in web browsers with hardware acceleration). And since the model must have been rendered locally, it had to exist in my RAM somewhere, so I could extract it and load it up in a 3D model editor. Specifically, I wanted to see if I could remove the sun visors interrupting the almost seamless glass canopy - an effect achieved by Tesla’s Model X, I’m not sure why Lucid didn’t follow.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Lucid Air sun visors interrupting the glass canopy" src="/assets/img/lucid-configurator-is-a-streaming-video-game/lucid-air-sun-visors.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Lucid Air sun visors interrupting the glass canopy.
&lt;/p&gt;

&lt;p&gt;So I dove into the code. After some time with the browser debug tools, skimming thousands of lines of minified Javascript, recording and examining the network requests between my laptop and Lucid’s servers, I realized the answer was quite literally just being printed in the console the whole time:&lt;/p&gt;

&lt;p&gt;&lt;img alt="The Configurator is a Video Stream" src="/assets/img/lucid-configurator-is-a-streaming-video-game/zlbolt-webrtc.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
The Configurator is a webRTC stream.
&lt;/p&gt;

&lt;p&gt;There’s no local model being rendered. What I’m seeing is a video.&lt;/p&gt;

&lt;p&gt;So what’s happening here? The configurator initializes with a 2D display - that is, a choppy panorama of images which provide the illusion of a 3D interaction. This is how most online interactive 3D displays are constructed. You can see a demo of that below:&lt;/p&gt;

&lt;div style="width: 100%; height: 100%;"&gt;&lt;/div&gt;
&lt;p class="caption video-caption"&gt;A choppy 3D view, created by a series of 2D images.&lt;/p&gt;

&lt;p&gt;Then, the client tests my bandwidth and latency, and if it’s fast enough to support a realtime video feed, it upgrades the session to 3D:&lt;/p&gt;

&lt;p&gt;&lt;img alt="Latency Test" src="/assets/img/lucid-configurator-is-a-streaming-video-game/latency-test.png" /&gt;
&lt;img alt="Bandwidth Test" src="/assets/img/lucid-configurator-is-a-streaming-video-game/bandwidth-test.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
You upgrade to a 3D session if you have the bandwidth.
&lt;/p&gt;

&lt;p&gt;This initiates a webRTC connection. &lt;a href="https://en.wikipedia.org/wiki/WebRTC"&gt;WebRTC&lt;/a&gt; is a peer-to-peer streaming protocol. The scene is rendered elsewhere, and streamed via webRTC to my browser.&lt;/p&gt;

&lt;p&gt;Inspecting the contents of their &lt;code class="language-plaintext highlighter-rouge"&gt;get_token&lt;/code&gt; request reveals some more details about their mechanics.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Contents of the get_token request" src="/assets/img/lucid-configurator-is-a-streaming-video-game/get-token-request.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Contents of the get_token request.
&lt;/p&gt;

&lt;p&gt;The first box reveals that, to test your bandwidth, they partially download from &lt;a href="https://libzl.zlthunder.net/bandwidth-test-img.png"&gt;a 25 MB test image&lt;/a&gt; to your device, and time how long that takes. This is the image, if you’re curious (don’t worry, I’ve compressed it down 10x).&lt;/p&gt;

&lt;p&gt;&lt;img alt="Bandwidth Test Image" src="/assets/img/lucid-configurator-is-a-streaming-video-game/bandwidth-test-image.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Doesn't have much to do with automobiles, but it will test your bandwidth just fine.
&lt;/p&gt;

&lt;p&gt;By the way, &lt;a href="https://www.gimp.org/tutorials/Floating_Logo/Plasma.jpg"&gt;this image seems to be an effect produced by the plasma plugin from GIMP&lt;/a&gt;. It loads quite slowly; as it resembles noise it must be largely incompressible.&lt;/p&gt;

&lt;p&gt;Opening up &lt;a href=""&gt;chrome://webrtc-internals/&lt;/a&gt; reveals some details about the webRTC connection streaming the 3D scene to my browser.&lt;/p&gt;

&lt;p&gt;&lt;img alt="WebRTC Info" src="/assets/img/lucid-configurator-is-a-streaming-video-game/lucid-webrtc.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Chrome's debug tools provide more information about the webRTC connection.
&lt;/p&gt;

&lt;p&gt;At this point, you may have noticed that none of the URLs here are on a lucid domain, but rather under zlthunder. Who are they?&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="zerolight"&gt;ZeroLight&lt;/h2&gt;

&lt;p&gt;Searching the web for zlthunder brings you to a company called &lt;a href="https://zerolight.com/"&gt;ZeroLight&lt;/a&gt;. They indicate that they provide cloud-based 3D visualization, specializing in automotive. It all makes sense now!&lt;/p&gt;

&lt;p&gt;&lt;img alt="Zero Light" src="/assets/img/lucid-configurator-is-a-streaming-video-game/zero-light.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Cloud based 3D visualization specialists.
&lt;/p&gt;

&lt;p&gt;It wasn’t much longer until I found &lt;a href="https://unity3d.com/showcase/case-stories/zerolight"&gt;this blog post from Unity&lt;/a&gt; (the prominent video game engine) explaining how ZeroLight leveraged the engine to create a realistic virtual showroom, with optimized physics, reflections, and all.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Zero Light" src="/assets/img/lucid-configurator-is-a-streaming-video-game/unity-zero-light.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Video games have brought us a long way.
&lt;/p&gt;

&lt;p&gt;Now I put all the pieces together. When I connect to the site, they load a 2D configurator. If my internet connection can handle it, they connect me to an instance of Unity - a 3D video game engine - running on an AWS server. The server receives my mouse inputs, rotating and zooming the field of view, renders the results in the cloud, and streams them back to me.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="sounds-expensive"&gt;Sounds Expensive&lt;/h2&gt;

&lt;p&gt;Imagine you have a 1,000 users connected to the site: you need a 1,000 hardware accelerated instances of a video game engine receiving inputs and streaming back large amounts of realtime video- just like the cloud video games products that are Stadia and GeForce Now, among others. But it seems they’re very aware of that: to save on costs, they disconnect you after 60s of inactavity:&lt;/p&gt;

&lt;p&gt;&lt;img alt="Inactivity downgrade" src="/assets/img/lucid-configurator-is-a-streaming-video-game/inactivity-downgrade.png" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Downgrading on idle must save a lot of money.
&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="what-is-a-video-game-engine-anyway"&gt;What is a Video Game Engine Anyway?&lt;/h2&gt;

&lt;p&gt;Although it may seem a surprising use of a video game engine at first, a video game engine is really just an optimized simulator. Established game engines like Unity incorporate decades of research into simulating light rays and other physical phenomona. So if you want to simulate reality, even if that reality is a showroom, a video game engine is a perfect, ready-to-use tool, that can be extended to many applications.&lt;/p&gt;

&lt;p&gt;In hindsight, I probably could have figured out how Lucid’s configurator worked by doing some searching and coming across the article from Unity. But reverse-engineering the configurator provided a closer look inside the challenges, solutions, and mechanics of their product, and I felt like a detective working from the ground up. What I found most amusing, though, was the incredible complexity we have achieved in technology today. A super realistic physics simulator, running on a remote server in an Amazon data center, receiving commands from my computer and streaming back the video feed at the speed of light, so that I can play with a car configurator. And all we needed to get to the moon was a calculator!&lt;/p&gt;</description><author>Sasha Kuznetsov's Blog</author><pubDate>Wed, 28 Jul 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.skz.dev/lucid-configurator-is-a-streaming-video-game</guid></item><item><title>Raya and the Last Dragon</title><link>https://olshansky.info/movie/raya_and_the_last_dragon/</link><description>Olshansky's review of Raya and the Last Dragon</description><author>🦉 olshansky 🦁</author><pubDate>Wed, 28 Jul 2021 08:32:09 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/raya_and_the_last_dragon/</guid></item><item><title>Whatever happened to the Memex?</title><link>https://www.marginalia.nu/log/08-whatever-happened-to-the-memex/</link><description>&lt;p&gt;I stumbled upon the Memex, which is a spiritual predecessor to hypertext technology. It was supposed to be a sort of personal data store that allows the user to link and annotate various documents in order to produce a sort of external memory, a private knowledge bank that associates ideas in a similar way a human brain does. The operator could also save and share associative trails through the information.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 28 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/08-whatever-happened-to-the-memex/</guid></item><item><title>No Trophy for Winning an Argument</title><link>https://june.kim/no-trophy-for-winning-an-argument/</link><author>june.kim</author><pubDate>Wed, 28 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/no-trophy-for-winning-an-argument/</guid></item><item><title>Configuring MDX for Next.js</title><link>https://www.tderflinger.com/en/configuring-mdx-for-nextjs</link><description>MDX allows you to write JSX in Markdown documents. You can embed these components in your document. In this article, I show how to configure your Next.js application to work with MDX.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Wed, 28 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/configuring-mdx-for-nextjs</guid></item><item><title>Book notes - OSTEP (part 3)</title><link>https://river.me/blog/book-notes-ostep-3/</link><description>My notes on &amp;ldquo;Operating Systems: Three Easy Pieces&amp;rdquo; - part 3, Persistence</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 27 Jul 2021 08:16:48 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-notes-ostep-3/</guid></item><item><title>Untitled • Art</title><link>https://muffinman.io/art/boxes-untitled/</link><description>&lt;div class="container"&gt;&lt;div class="art-page__content"&gt;&lt;p&gt;Shading was done by generating (a lot of) dots. Then I used &lt;a href="https://github.com/nornagon/saxi"&gt;saxi&amp;#x27;s&lt;/a&gt; option to connect nearby dots in order to create these squiggly lines.&lt;/p&gt;&lt;/div&gt;&lt;table class="text-sm art-page__info"&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Created: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;July 2021&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Size: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;30x30cm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Paper: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;Fabriano Black Black 300gsm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Pens: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;Uni-ball Signo UM-153&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="art-gallery photoswipe-gallery"&gt;&lt;div class="art-gallery__cover"&gt;&lt;a class="gallery__link outline" href="./0.jpg"&gt;&lt;img alt="" class="gallery__image" height="2236" src="./0.jpg" width="2256" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="art-gallery__items"&gt;&lt;a class="gallery__link outline" href="./1.jpg"&gt;&lt;img alt="" class="gallery__image" height="2400" src="./w_500-h_500--1.jpg" width="2400" /&gt;&lt;/a&gt;&lt;a class="gallery__link outline" href="./2.jpg"&gt;&lt;img alt="" class="gallery__image" height="2500" src="./w_500-h_500--2.jpg" width="2500" /&gt;&lt;/a&gt;&lt;a class="gallery__link outline" href="./plot_1920x1080.mp4"&gt;&lt;video class="gallery__video" src="./plot_1920x1080.mp4#t=0.001"&gt;&lt;/video&gt;&lt;svg class="gallery__play-icon" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="M405.2,232.9L126.8,67.2c-3.4-2-6.9-3.2-10.9-3.2c-10.9,0-19.8,9-19.8,20H96v344h0.1c0,11,8.9,20,19.8,20 c4.1,0,7.5-1.4,11.2-3.4l278.1-165.5c6.6-5.5,10.8-13.8,10.8-23.1C416,246.7,411.8,238.5,405.2,232.9z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;</description><author>Muffin Man</author><pubDate>Tue, 27 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://muffinman.io/art/boxes-untitled/</guid></item><item><title>Control-Flow Analysis</title><link>https://lambdaland.org/posts/2021-05-20_cfa/</link><description>&lt;p&gt;Control-Flow Analysis is a popular technique for performing static analysis of many different kinds of programming languages.
It’s most often needed in cases where you have some kind of dynamic dispatch: either where you have first-class functions or when you have objects and you call one of their methods.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Tue, 27 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-05-20_cfa/</guid></item><item><title>Issues related to the city of Golden</title><link>https://josh.works/issues-in-golden</link><description>&lt;p&gt;While I was biking around recently, I saw notes about an upcoming neighborhood meeting about some rezoning, a big lot in downtown Golden.&lt;/p&gt;

&lt;p&gt;I went to the meeting (Thursday, July 22) and learned a lot.&lt;/p&gt;

&lt;p&gt;Here’s the lot in question:&lt;/p&gt;

&lt;p&gt;&lt;img alt="picture of Coors property" src="coors-property.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;I have ridden my bike past this property hundreds of times. It’s directly between my house and downtown:&lt;/p&gt;

&lt;p&gt;&lt;img alt="strava paths" src="strava-map.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Blue lines are all the strava trips I’ve taken (by bike). See more here: &lt;a href="https://cryptic-sea-38287.herokuapp.com/"&gt;https://cryptic-sea-38287.herokuapp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two books that contain (broadly) everything an architect, land-user planner, or developer needs to maximize the value of the work they’re doing, and minimize the “externalities” of their project.&lt;/p&gt;

&lt;p&gt;If you’re serious about cities being places that promote the wellbeing of their populations, the environment, etc, these are the two books you need to have on your bookshelf:&lt;/p&gt;

&lt;p&gt;&lt;img alt="Shoup and Bertraud" src="/images/shoup-and-bertaud.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Fixing these problems in Golden takes three steps. Ideally, all three would be done, but any of them done in isolation at any level of implementation would be a step in the right direction:&lt;/p&gt;

&lt;h3 id="step-1-get-parking-right"&gt;Step 1: Get Parking Right&lt;/h3&gt;

&lt;p&gt;Golden city staff must read and implement &lt;a href="https://www.amazon.com/High-Cost-Parking-Updated-Edition/dp/193236496X/ref=as_li_ss_tl?ie=UTF8&amp;amp;qid=1332084228&amp;amp;sr=8-1&amp;amp;linkCode=ll1&amp;amp;tag=markeurban-20&amp;amp;linkId=65aeac5942c99b794876bb2d2dc32bb0"&gt;The High Cost of Free Parking&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No other “solution” will do anything but make the current situation worse.&lt;/p&gt;

&lt;p&gt;I’m writing more about parking here:&lt;/p&gt;

&lt;p&gt;👉  &lt;a href="/parking-in-golden"&gt;parking in Golden, CO&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="step-2-get-buildings-right-location-size-use-etc"&gt;Step 2: Get Buildings right (location, size, use, etc)&lt;/h3&gt;

&lt;p&gt;Read &lt;a href="https://www.goodreads.com/book/show/39644188-order-without-design"&gt;Order Without Design: How Markets Shape Cities&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;But Josh this is a big academic book I don’t want to read it…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s not that big, it’s quite approachable, but it’s fine if you don’t want to read it. Most modern planners have never read this book (I’ve asked about a dozen of them…) but they would be 100x better at their jobs (or they would at least stop making bad situations worse) if they’d read it.&lt;sup id="fnref:planners"&gt;&lt;a class="footnote" href="#fn:planners" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;This is THE book for anyone planning on spending a lot of money trying to solve a big problem in the city.&lt;/p&gt;

&lt;h3 id="step-3-give-full-evaluation-to-the-racist-intent-behind-modern-zoning-and-its-impact-of-how-cities-have-been-built-over-the-last-century"&gt;Step 3: Give full evaluation to the racist intent behind modern zoning and it’s impact of how cities have been built over the last century&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/32191706-the-color-of-law"&gt;The Color of Law: A Forgotten History of How Our Government Segregated America&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.fidelitypress.org/slaughter-of-cities"&gt;The Slaughter of Cities: Urban Renewal as Ethnic Cleansing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="hindrances-to-the-city-of-golden-trending-in-the-right-direction"&gt;Hindrances to the City of Golden trending in the right direction&lt;/h2&gt;

&lt;p&gt;As you might imagine, this system is not stable. Things are always changing, and either getting better, or getting worse.&lt;/p&gt;

&lt;p&gt;They’re currently getting &lt;em&gt;worse&lt;/em&gt;, and &lt;a href="https://www.guidinggolden.com/code-changes"&gt;the new zoning plan&lt;/a&gt; that the city zoning staff is working on generally makes things worse, in addition to probably eating up about $1.5 million in budget and payroll.&lt;/p&gt;

&lt;h3 id="hindrance-1-grossly-mismanaged-parking"&gt;Hindrance 1: Grossly Mismanaged parking&lt;/h3&gt;

&lt;p&gt;Parking is a scarce commodity, and like all commodities, &lt;em&gt;if you mismanage access to said commodity, there are downstream negative effects&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The solution is technical, not political. There are some complexities to it, but implementing this fix is well within the capacities of the full-time planning and engineering staff.&lt;/p&gt;

&lt;p&gt;This comes from The High Cost of Free Parking by Donald Shoup. Read more on this here:&lt;/p&gt;

&lt;p&gt;👉  &lt;a href="/parking-in-golden"&gt;parking in Golden, CO&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set appropriate price for curb-side parking&lt;/li&gt;
  &lt;li&gt;Spend all revenue where it’s collected&lt;/li&gt;
  &lt;li&gt;Abolish parking minimums&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="hindrance-2-exclusionary-zoning"&gt;Hindrance 2: Exclusionary zoning&lt;/h3&gt;

&lt;p&gt;Euclidean zoning, AKA &lt;a href="https://en.wikipedia.org/wiki/Exclusionary_zoning"&gt;exclusionary zoning&lt;/a&gt;, is killing our city. Serious people in the space don’t debate that exclusionary zoning is terrible.&lt;/p&gt;

&lt;p&gt;Persons who support exclusionary zoning are as sophisticated in urban planning as doctors &lt;em&gt;who believe in &lt;a href="https://www.britannica.com/science/leeching"&gt;leeching&lt;/a&gt;&lt;/em&gt; are sophisticated about the human body.&lt;/p&gt;

&lt;p&gt;It’s not up for debate. You don’t have to believe you, and I’m actually eager to convince you otherwise, and I am willing to do so, and I’ll do so gently, but &lt;em&gt;exclusionary zoning is horrible for cities and causes most of the harms facing cities today&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here’s more on this:&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;1/20 Thoughts on Denver's zoning and systemic racism. &lt;br /&gt;&lt;br /&gt;Here's a screenshot from &lt;a href="https://twitter.com/CityofDenver?ref_src=twsrc%5Etfw"&gt;@CityofDenver&lt;/a&gt;'s zoning map on &lt;a href="https://t.co/Tzx271JS8u"&gt;https://t.co/Tzx271JS8u&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This is textbook Euclidean Zoning, AKA &amp;quot;Single-Use Zoning&amp;quot;.&lt;br /&gt;&lt;br /&gt;The problems of this form of zoning are well-known:&lt;a href="https://t.co/WqiklxjB4h"&gt;https://t.co/WqiklxjB4h&lt;/a&gt; &lt;a href="https://t.co/7RDaFJlttS"&gt;pic.twitter.com/7RDaFJlttS&lt;/a&gt;&lt;/p&gt;&amp;mdash; Josh Thompson (@josh_works) &lt;a href="https://twitter.com/josh_works/status/1294726871574179840?ref_src=twsrc%5Etfw"&gt;August 15, 2020&lt;/a&gt;&lt;/blockquote&gt;


&lt;h3 id="hindrance-3-legally-imposed-minimums-lot-size-setbacks"&gt;Hindrance 3: Legally-imposed Minimums (lot size, setbacks)&lt;/h3&gt;

&lt;p&gt;Having “minimums” around lot sizes and setbacks wrecks cities. I’ll add more sources at some point, but again, &lt;em&gt;this is not a contentious statement&lt;/em&gt;. If you think it’s politically charged, it’s because you’re thinking about the issue from the wrong angle.&lt;/p&gt;

&lt;h3 id="hindrance-4-legally-imposed-maximums"&gt;Hindrance 4: Legally-imposed Maximums&lt;/h3&gt;

&lt;p&gt;Proscribing maximums (like floor area ratios, heights, densities) wrecks cities as much as the minimums do.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Most of the pleasant part of downtown that we all know and love is &lt;em&gt;wildly&lt;/em&gt; illegal according to modern zoning codes, because it violates minimums (setbacks) and maximums (floor area ratios).&lt;/p&gt;

&lt;p&gt;Together, these minimums/maximums leave little room for new development - all the new development we get is the ugly stuff. The charming, efficient buildings of the past are all illegal.&lt;/p&gt;

&lt;p&gt;More later.&lt;/p&gt;

&lt;h3 id="resources"&gt;Resources&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.guidinggolden.com/planning-projects/news_feed/600-9th-street-rezoning"&gt;PC21-12 Coorstek 600 9th Street Rezoning, from the City of Golden planning staff&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.trybaarchitects.com/people/john-mcintyre"&gt;Tryba Architcts (The company working w/Coors on the development)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:planners"&gt;
      &lt;p&gt;There’s a lot more I’ll say on “planners” soon - most of them mean well, and are highly constrained actors. Some of them are prevented from doing the right thing by broken political systems. Others are prevented from doing the right thing by ignorance. One of these is partically forgivable. &lt;a class="reversefootnote" href="#fnref:planners"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>Josh Thompson</author><pubDate>Mon, 26 Jul 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/issues-in-golden</guid></item><item><title>Nerdy Reading</title><link>https://www.masswerk.at/nowgobang/2021/nerdy-reading</link><description>Time for a summer romance.</description><author>mass:werk – Now Go Bang!</author><pubDate>Mon, 26 Jul 2021 05:50:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/nerdy-reading</guid></item><item><title>Local Backlinks</title><link>https://www.marginalia.nu/log/07-local-backlinks/</link><description>&lt;p&gt;Maintaining links is difficult. My gemini server doesn&amp;rsquo;t have a lot of pages, but already maintaining links between relevant pages is growing more tedious by the page. It&amp;rsquo;s going to become untenable soon.&lt;/p&gt;
&lt;p&gt;In part inspired by Antenna, I had the idea of extracting local backlinks, and automatically appending them to the pages that are linked. That way all local links are effectively bidirectional. If new a new post links to an old post, the old post automatically links to the new post. Old pages will thus over time accumulate more links to new pages without manual maintenance.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Mon, 26 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/07-local-backlinks/</guid></item><item><title>It's a great time to be a developer</title><link>https://jasoneckert.github.io/myblog/it-is-a-great-time-to-be-a-developer/</link><description>&lt;p&gt;&lt;img alt="Developer" src="developer.jpg#right" title="Developer" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been a developer for over 3 decades. I remember &lt;code&gt;prompt&lt;/code&gt; statements in dBASE III, M68k Assembly and those awful Borland tools. Documentation was provided in large printed binders and often left a lot to the imagination.&lt;/p&gt;
&lt;p&gt;Things got better in the late 1990s and early 2000s, but it wasn&amp;rsquo;t until the late 2000s when things really started to heat up. Sun Microsystem&amp;rsquo;s early 1990s slogan &amp;ldquo;The network is the computer&amp;rdquo; became a reality with the rise of cloud computing, object storage and slick frontend/backend stacks.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Mon, 26 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/it-is-a-great-time-to-be-a-developer/</guid></item><item><title>2021–07–26: The latest Pinephone keyboard prototype</title><link>https://xnux.eu/log/#043</link><author>megi's PinePhone Development Log</author><pubDate>Mon, 26 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#043</guid></item><item><title>Deleting Code to Double the Performance of my Trivial Objective-S Tasks Backend</title><link>https://blog.metaobject.com/2021/07/deleting-code-to-double-performance-of.html</link><description>About two months ago, I &lt;a href="https://blog.metaobject.com/2021/06/towards-todomvc-backend-in-objective-s.html"&gt;showed&lt;/a&gt;
a trivial tasks backend for a hypothetical ToDoMVC app.  At the time, I noted that the performance was pretty insane
for something written in a (slow) scripting language:  7K requests per second when fetching a single task.&lt;p&gt;

That was using an encoder method (that writes key/value pairs to the JSON encoder) written in Objective-S, and I
wondered how much faster it would go if that was  no longer the case.  Twice as fast, it turns out.&lt;p&gt;

Yesterday, I wrote about tuning the Objective-S's SQLite insert performance to around 130M rows/minute, coincidentally
also for a simple tasks schema.  One part of that performance story was the fact that the encoder method (writing key/value
pairs to the SQLite encoder) was generated by pasting together Objective-C blocks and installing the whole thing
as an Objective-C method.  No interpretation, except for calling a series of blocks stored in an &lt;code&gt;NSArray&lt;/code&gt;.

I had completely forgotten about the hand-written Objective-S encoder method in the back-end's &lt;code&gt;Task&lt;/code&gt; class!
Since generation is automatic, but won't override an already existing method, all I had to do in order to get the
better performance was delete the old method.&lt;p&gt;

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
&gt; wrk -c 1 -t 1 http://localhost:8082/tasks 
Running 10s test @ http://localhost:8082/tasks
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    66.60us    9.69us   1.08ms   96.72%
    Req/Sec    14.95k   405.55    15.18k    98.02%
  150275 requests in 10.10s, 30.67MB read
Requests/sec:  14879.22
Transfer/sec:      3.04MB
&gt; curl  http://localhost:8082/tasks         
[{"id":1,"done":0,"title":"Clean Room"},{"id":2,"done":1,"title":"Check Twitter"}]%
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

More than twice the performance, and that while fetching &lt;em&gt;two&lt;/em&gt; tasks instead of just one, so around 30K tasks/second!  (And yes, I checked that I wasn't hitting a 404...).&lt;p&gt;

So what's the performance if we actually fetch more than a minimal number of tasks?  For 128 tasks, 64x more than before, it's still around 9K requests/s, so most of the time so far was per-request overhead.  At this point we are serving a little over 1M tasks/s:&lt;p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
&gt; wrk -c 1 -t 1 'http://localhost:8082/tasks/' 
Running 10s test @ http://localhost:8082/tasks/
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   112.13us   76.17us   5.57ms   99.63%
    Req/Sec     9.05k   397.99     9.21k    97.03%
  90923 requests in 10.10s, 483.41MB read
Requests/sec:   9002.44
Transfer/sec:     47.86MB
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

If memory serves, that was around the rate we were seeing with the &lt;a href="https://www.infoq.com/news/2014/11/gotober-wunderlist-microservices/"&gt;Wunderlist backend&lt;/a&gt; when we had a couple
of million users, not that these are comparable in any meaningful way.

For 1024 tasks there's a significant drop to slightly above 1.8K requests/s, with the task-rate almost doubling to 1.8M/s:&lt;p&gt;

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
&gt; wrk -c 1 -t 1 'http://localhost:8082/tasks/' 
Running 10s test @ http://localhost:8082/tasks/
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   552.06us   62.77us   1.84ms   81.08%
    Req/Sec     1.82k    52.95     1.89k    90.10%
  18267 requests in 10.10s, 778.36MB read
Requests/sec:   1808.59
Transfer/sec:     77.06MB
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

&lt;p&gt;UPDATE:&lt;br /&gt;

Of course, those larger request sizes also see a much larger increase in performance than 2x.  With the old code, the 128 task case clocks in at 147 requests/s and the 1024 task case at 18 requests/s, at which point it's a 100x improvement.  So gives you an idea just how slow my Objective-S interpreter is.&lt;p&gt;</description><author>metablog</author><pubDate>Sun, 25 Jul 2021 16:34:00 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/07/deleting-code-to-double-performance-of.html</guid></item><item><title>What's bad about Julia?</title><link>https://viralinstruction.com/posts/badjulia/index.html</link><description>A mega-rant about all the things I find bad about Julia.</description><author>viralinstruction</author><pubDate>Sun, 25 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://viralinstruction.com/posts/badjulia/index.html</guid></item><item><title>Resolving AWS CloudFront Access Denied Errors</title><link>https://nelson.cloud/resolving-aws-cloudfront-access-denied-errors/?ref=rss</link><description>Resolving Access Denied Errors in a CloudFront Distribution that uses a Private S3 Bucket, Origin Access Identity, and Contains Multiple index.html Templates.</description><author>Nelson Figueroa</author><pubDate>Sun, 25 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nelson.cloud/resolving-aws-cloudfront-access-denied-errors/?ref=rss</guid></item><item><title>Minutes - A Quantified Self App for Myself</title><link>https://trigonaminima.github.io/2021/07/flutter_app_1/</link><description>I’ve long since 2014 engaged in some form of Quantified Self. It started with a simple Libre Office worksheet. I tracked my finances, sleep, time spent on entertainment, walking steps, and many other things. Maintaining a worksheet had a lot of flaws. I didn’t want to put my data in a Google worksheet, so it always remained on my laptop. Sometimes, I forgot to enter the data, and other times, I couldn’t access the device. There was also a limit on what I could track through it. I wanted to know where my time was going within a day. To manually enter everything takes a lot of time. I wanted something automated. I found many useful tools while trying to automate these things.</description><author>Playground</author><pubDate>Sun, 25 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://trigonaminima.github.io/2021/07/flutter_app_1/</guid></item><item><title>A/B testing and the historic lift paradox [redacted]</title><link>https://bytepawn.com/ab-testing-and-the-historic-lift-paradox.html</link><description>&lt;p&gt;I show how looking at historic "before" values in A/B testing can lead to an apparent paradox.&lt;br /&gt;&lt;br /&gt; &lt;img alt="Historic lift" src="/images/historic_lift3.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 25 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/ab-testing-and-the-historic-lift-paradox.html</guid></item><item><title>Inserting 130M SQLite Rows per Minute...from a Scripting Language</title><link>https://blog.metaobject.com/2021/07/inserting-130m-sqlite-rows-per.html</link><description>The other week, I stumbled on the post &lt;a href="https://avi.im/blag/2021/fast-sqlite-inserts/"&gt;Inserting One Billion Rows in SQLite Under A Minute&lt;/a&gt;, which was a funny coincidence, as I was just in the process of giving my own SQLite/Objective-S
adapter a bit of tune-up.  (The post's title later had "Towards" prepended, because the author wasn't close to hitting that goal).&lt;p&gt;

This SQLite adapater was a spin-off of my earlier &lt;a href="https://blog.metaobject.com/2020/06/beyond-faster-json-support-for-iosmacos.html"&gt;article&lt;/a&gt; &lt;a href="https://blog.metaobject.com/2020/04/faster-json-support-for-iosmacos-part-8.html"&gt;series&lt;/a&gt; on &lt;a href="https://blog.metaobject.com/2020/04/faster-json-support-for-iosmacos-part-7.html"&gt;optimizing&lt;/a&gt; JSON performance, itself triggered by
the ludicrously bad &lt;a href="https://blog.metaobject.com/2020/04/somewhat-less-lethargic-json-support.html"&gt;performance&lt;/a&gt; of Swift Coding at this rather simple and relevant task. To recap: Swift's JSON coder clocked in at about 10MB/s.  By using
a streaming approach and a bit of tuning, we got that to around 200MB/s.&lt;p&gt;

Since then, I have worked on making Objective-S much more useful for UI work, with the object-literal syntax making
defining UIs as convenient as the various "declarative" functional approaches such as React or SwiftUI.  Except it is
still using the same AppKit or UIKit objects we know and love, and doesn't force us to embrace the &lt;a href="https://blog.metaobject.com/2018/12/uis-are-not-pure-functions-of-model.html"&gt;silly&lt;/a&gt; notion that
the UI is a pure function of the model.  Oh, and you get live previews that actually work.  But more on that later.&lt;p&gt;

So I am slowly inching towards doing a &lt;a href="https://todomvc.com"&gt;ToDoMVC&lt;/a&gt;, a benchmark that feels
rather &lt;a href="https://en.wikipedia.org/wiki/Wunderlist"&gt;natural&lt;/a&gt; to me.  While I am still very partial to
just dumping JSON files, and the previous article series hopefully showed that this approach is plenty fast 
enough, I realize that a lot of people prefer a "real" database, especially on the &lt;a href="https://blog.metaobject.com/2021/06/towards-todomvc-backend-in-objective-s.html"&gt;back-end&lt;/a&gt;, and I wanted to build that as well.  One of the many benchmarks I have for Objective-S is that it should
be possible to build a nicer Rails with it.  (At this point in time I am pretty sure I will hit that benchmark).&lt;p&gt;

One of the ways to figure out if you have a good design is to stress-test it.  One very useful stress-test is seeing
how fast it can go, because that will tell you if the thing you built is lean, or if you put in unnecessary layers
and indirections.&lt;p&gt;

This is particularly interesting in a Scripted Components (&lt;a href="https://web.archive.org/web/20180702110347/https://www.inf.ed.ac.uk/teaching/courses/sapm/2011-2012/slides/scripting.pdf"&gt;pdf&lt;/a&gt;) system that combines a relatively slow
but flexible interactive scripting language with fast, optimized components.  The question is whether you can actually
combine the flexibility of the scripting language while reaping the benefits of the fast components, rather than 
having to dive into adapting and optimizing the components for each use case, or just getting slow performance despite
the fast components.  My hunch was that the streaming approach I have been using for a while now and that worked really
well for JSON and Objective-C would also do well in this more challenging setting.&lt;p&gt;

Spoiler alert: it did!&lt;p&gt;

&lt;h3&gt;The benchmark&lt;/h3&gt;

The benchmark was a slightly modified version of the script that serves as a tasks backend.  Like said sample
script it also creates a tasks database and inserts some example rows.  Instead of inserting two rows,
it inserts 10 million.  Or a hundred million.&lt;p&gt;

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
#-taskbench:dbref
#

class Task {
	var  id.
	var  done.
	var  title.
	-description { "". }
	+sqlForCreate {
		'( [id] INTEGER PRIMARY KEY, [title] VARCHAR(220) NOT NULL, [done] INTEGER );'.
	}
}.

scheme todo : MPWAbstractStore {
	var db.
	var tasksTable.
	-initWithRef:ref {
		this:db := (MPWStreamQLite alloc initWithPath:ref path).
		this:tasksTable :=  #MPWSQLTable{ #db: this:db , #tableClass: Task, #name: 'tasks'  }.
		this:db open.
		self.
	}
	-createTable {
		this:tasksTable create.
	    this:tasksTable := this:db tables at:'tasks'.
        this:tasksTable createEncoderMethodForClass: Task.
	}
	-createTaskListToInsert:log10ofSize {
		baseList ← #( #Task{  #title: 'Clean Room', #done: false }, #Task{  #title: 'Check Twitter', #done: true } ).
		...replicate ...
		taskList.
	}
	-insertTasks {
	    taskList := self createTaskListToInsert:6.
		1 to:10 do: {
			this:tasksTable insert:taskList.
		}.
	}
}.
todo := todo alloc initWithRef:dbref.
todo createTable.
todo insertTasks.
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

(I have removed the body of the method that replicates the 2 tasks into the list of millions of tasks we need to insert.
It was bulky and not relevant.)&lt;p&gt;

In this sample we define the Task class and use that to create the SQL Table.  We could also have simply created 
the table and generated a Tasks class from that.&lt;p&gt;

Anyway, running this script yields the following result.
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
&gt; time ./taskbench-sqlite.st /tmp/tasks1.db 
./taskbench-sqlite.st /tmp/tasks1.db  4.07s user 0.20s system 98% cpu 4.328 total
&gt; ls -al  /tmp/tasks1.db* 
-rw-r--r--  1 marcel  wheel   214M Jul 24 20:11 /tmp/tasks1.db
&gt; sqlite3 /tmp/tasks1.db 'select count(id) from tasks;' 
10000000
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
So we inserted 10M rows in 4.328 seconds, yielding several hundred megabytes of SQLite data.  This would be 138M rows
had we let it run for a minute.  Nice.  For comparison, the original article's numbers were 11M rows/minute for 
CPython, 40M rows/minute for PyPy and 181M rows/minute for Rust, though on a slower Intel MacBook
Pro whereas I was running this on an M1 Air.   I compiled and ran the Rust version on my M1 Air and it did 
100M rows in 21 seconds, so just a smidgen over twice as fast as my Objective-S script, though with
a simpler schema (CHAR(6) instead of VARCHAR(220)) and less data (1.5GB vs. 2.1GB for 100M rows).
&lt;p&gt;
&lt;h3&gt;Getting SQLite fast&lt;/h3&gt;

The initial version of the script was far, far slower, and at first it was, er, "sub-optimal" use of SQLite
that was the main culprit, mostly inserting every row by itself without batching.  When SQLite sees an
INSERT (or an UPDATE for that matter) that is not contained in a transaction, it will automatically wrap that 
INSERT inside a generated transaction and commit that transaction after the INSERT is processed.  Since 
SQLite is very fastidious about ensuring that transactions get to disk atomically, this is slow.  Very slow.&lt;p&gt;

The class handling SQLite inserts is a &lt;a href="https://conf.researchr.org/details/dls-2019/dls-2019/7/Standard-Object-Out-Streaming-Objects-with-Polymorphic-Write-Streams"&gt;Polymorphic Write Stream&lt;/a&gt;, so it knows what an array is.
When it encounters one, it sends itself the &lt;code&gt;beginArray&lt;/code&gt; message, writes the contents of the array 
and finishes by sending itself the &lt;code&gt;endArray&lt;/code&gt; message.  Since writing an array sort of implies that
you want to write all of it, this was a good place to insert the transactions:

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
-(void)beginArray {
    sqlite3_step(begin_transaction);
    sqlite3_reset(begin_transaction);
}

-(void)endArray {
    sqlite3_step(end_transaction);
    sqlite3_reset(end_transaction);
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

So now, if you want to write a bunch of objects as a single transaction, just write them as an array, as the
benchmark code does. There were some other minor issues, but after that less than 10% of the total time
were spent in SQLite, so it was time to optimize the caller, my code.&lt;p&gt;

&lt;h3&gt;Column keys and Cocoa Strings&lt;/h3&gt;

At this point, my guess was that the biggest remaining slowdown would be my, er, "majestic" Objective-S
interpreter.  I was wrong, it was Cocoa string handling.  Not only was I creating the SQLite parameter
placeholder keys dynamically, so allocating new NSString objects for each column of each row, it also
happens that getting character data from an NSString object nowadays involves some very complex and slow
internal machinery using encoding conversion streams.  &lt;code&gt;-UTF8String&lt;/code&gt; is not your friend, and other
methods appear to fairly consistently use the same slow mechanism. I guess making NSString horribly slow is
one way to make other string handling look good in comparison.&lt;p&gt;

After a few transformations, the code would just look up the incoming NSString key in a dictionary that
mapped it to the SQLite parameter index. String-processing and character accessing averted.&lt;p&gt;

&lt;h3&gt;Jitting the encoder method. Without a JIT&lt;/h3&gt;

One thing you might have noticed about the class definition in the benchmark code is that there is no
encoder method, it just defines its instance variables and some other utilities.  So how is the class
data encoded for the &lt;code&gt;SQLTable&lt;/code&gt;?  KVC?  No, that would be a bit slow, though it might make a good
fallback.&lt;p&gt;

The magic is the &lt;code&gt;createEncoderMethodForClass:&lt;/code&gt; method.  This method, as the name suggests,
creates an encoder method by pasting together a number of blocks, turns the top-level into
a method using &lt;code&gt;imp_implementationWithBlock()&lt;/code&gt;, and then finally adds that method
to the class in question using &lt;code&gt;class_addMethod()&lt;/code&gt;.&lt;p&gt;

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
-(void)createEncoderMethodForClass:(Class)theClass
{
    NSArray *ivars=[theClass allIvarNames];
    if ( [[ivars lastObject] hasPrefix:@"_"]) {
        ivars=(NSArray*)[[ivars collect] substringFromIndex:1];
    }
    
    NSMutableArray *copiers=[[NSMutableArray arrayWithCapacity:ivars.count] retain];
    for (NSString *ivar in ivars) {
        MPWPropertyBinding *accessor=[[MPWPropertyBinding valueForName:ivar] retain];
        [ivar retain];
        [accessor bindToClass:theClass];
        
        id objBlock=^(id object, MPWFlattenStream* stream){
            [stream writeObject:[accessor valueForTarget:object] forKey:ivar];
        };
        id intBlock=^(id object, MPWFlattenStream* stream){
            [stream writeInteger:[accessor integerValueForTarget:object] forKey:ivar];
        };
        int typeCode = [accessor typeCode];
        
        if ( typeCode == 'i' || typeCode == 'q' || typeCode == 'l' || typeCode == 'B' ) {
            [copiers addObject:Block_copy(intBlock)];
        } else {
            [copiers addObject:Block_copy(objBlock)];
        }
    }
    void (^encoder)( id object, MPWFlattenStream *writer) = Block_copy( ^void(id object, MPWFlattenStream *writer) {
        for  ( id block in copiers ) {
            void (^encodeIvar)(id object, MPWFlattenStream *writer)=block;
            encodeIvar(object, writer);
        }
    });
    void (^encoderMethod)( id blockself, MPWFlattenStream *writer) = ^void(id blockself, MPWFlattenStream *writer) {
        [writer writeDictionaryLikeObject:blockself withContentBlock:encoder];
    };
    IMP encoderMethodImp = imp_implementationWithBlock(encoderMethod);
    class_addMethod(theClass, [self streamWriterMessage], encoderMethodImp, "v@:@" );
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

What's kind of neat is that I didn't actually write that method for this particular use-case:  I had
already created it for JSON-coding.  Due to the fact that the JSON-encoder and the SQLite writer
are both Polymorphic Write Streams (as are the targets of the corresponding decoders/parsers), 
the same method worked out of the box for both.&lt;p&gt;

(It should be noted that this encoder-generator currently does not handle all variety of data types;
this is intentional).

&lt;h3&gt;Getting the data out of Objective-S objects&lt;/h3&gt;

The encoder method uses &lt;code&gt;MPWPropertyBinding&lt;/code&gt; objects to efficiently access the instance
variables via the object's accessors, caching IMPs and converting data as necessary, so they are
both efficient and flexible.  However, the actual accessors that Objective-S generated for its
instance variables were rather baroque, because they used the same basic mechanism used for 
Objective-S methods, which can only deal with objects, not with primitive data types.&lt;p&gt;

In order to interoperate seamlessly with Objective-C, which expected methods that can
take data types other than objects, all non-object method arguments are converted
to objects on the way in, and return values are converted from objects to primitive
values on the way out.&lt;p&gt;

So even the accessors for primitive types such as the integer "id" or the boolean "done"
would have their values converted to and from objects by the interface machinery.  As
I noted above, I was a bit surprised that this inefficiency was overshadowed by the 
NSString-based key handling.&lt;p&gt;

In fact, one of the reason for pursuing the SQLite insert benchmark was to have a 
reason for finally tackling this Rube-Goldberg mechanism.  In the end, actually
addressing it turned out to be far less complex than I had feared, with the technique
being very similar to that used for the encoder-generator above, just simpler.&lt;p&gt;

Depending on the type, we use a different block that gets parameterised with the
offset to the instance variable.  I show the setter-generator below, because
there the code for the object-case is actually different due to retain-count
handling:

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
#define pointerToVarInObject( type, anObject ,offset)  ((type*)(((char*)anObject) + offset))

#ifndef __clang_analyzer__
// This leaks because we are installing into the runtime, can't remove after

-(void)installInClass:(Class)aClass
{
    SEL aSelector=NSSelectorFromString([self objcMessageName]);
    const char *typeCode=NULL;
    int ivarOffset = (int)[ivarDef offset];
    IMP getterImp=NULL;
    switch ( ivarDef.objcTypeCode ) {
        case 'd':
        case '@':
            typeCode = "v@:@";
            void (^objectSetterBlock)(id object,id arg) = ^void(id object,id arg) {
                id *p=pointerToVarInObject(id,object,ivarOffset);
                if ( *p != arg ) {
                    [*p release];
                    [arg retain];
                    *p=arg;
                }
            };
            getterImp=imp_implementationWithBlock(objectSetterBlock);
            break;
        case 'i':
        case 'l':
        case 'B':
            typeCode = "v@:l";
            void (^intSetterBlock)(id object,long arg) = ^void(id object,long arg) {
                *pointerToVarInObject(long,object,ivarOffset)=arg;
            };
            getterImp=imp_implementationWithBlock(intSetterBlock);
            break;
        default:
            [NSException raise:@"invalidtype" format:@"Don't know how to generate set accessor for type '%c'",ivarDef.objcTypeCode];
            break;
    }
    if ( getterImp &amp;&amp; typeCode ) {
        class_addMethod(aClass, aSelector, getterImp, typeCode );
    }
    
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

At this point, profiles were starting to approach around two thirds of the time being spent in &lt;code&gt;sqlite_&lt;/code&gt; functions,
so the optimisation efforts were starting to get into a region of diminishing returns.&lt;p&gt;

&lt;h3&gt;Linear scan beats dictionary&lt;/h3&gt;

One final noticeable point of obvious overhead was the (string) key to parameter index mapping, which the
optimizations above had left at a &lt;code&gt;NSDictionary&lt;/code&gt; mapping from &lt;code&gt;NSString&lt;/code&gt; to &lt;code&gt;NSNumber&lt;/code&gt;.
As you probably know, &lt;code&gt;NSDictionary&lt;/code&gt; isn't exactly the fastest.  One idea was to replace that lookup
with a &lt;a href="https://blog.metaobject.com/2020/04/equally-lethargic-json-support-for.html"&gt;MPWFastrStringTable&lt;/a&gt;,
but that means either needing to solve the problem of fast access to &lt;code&gt;NSString&lt;/code&gt; character data or changing the 
protocol.&lt;p&gt;

So instead I decided to brute-force it:  I store the actual pointers to the NSString objects in a C-Array indexed by 
the SQLite parameter index.  Before I do the other lookup, which I keep to be safe, I do a linear scan in that table
using the incoming string pointer.  This little trick largely removed the parameter index lookup from my profiles.&lt;p&gt;


&lt;h3&gt;Conclusion&lt;/h3&gt;

With those final tweaks, the code is probably quite close to as fast as it is going to get.  Its slower performance
compared to the Rust code can be attributed to the fact that it is dealing with more data and a more complex
schema, as well as having to actually obtain data from materialized objects, whereas the Rust code just generates
the SQlite calls on-the-fly.&lt;p&gt;

All this is achieved from a slow, interpreted scripting language, with all the variable parts (data class, steering
code) defined in said slow scripting language.  So while I look forward to the native compiler for Objective-S,
it is good to know that it isn't absolutely necessary for excellent performance, and that the basic design of
these APIs is sound.&lt;p&gt;</description><author>metablog</author><pubDate>Sat, 24 Jul 2021 23:59:57 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/07/inserting-130m-sqlite-rows-per.html</guid></item><item><title>What I Want from This Website</title><link>https://zef.studio/journal/what-I-want-from-this-site</link><description>Thoughts on what I want from making this website.</description><author>Zef Houssney</author><pubDate>Sat, 24 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://zef.studio/journal/what-I-want-from-this-site</guid></item><item><title>Custom Per Page CSS With Pelican</title><link>https://duncanlock.net/blog/2021/07/24/custom-per-page-css-with-pelican/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;Sometimes I want to have a just &lt;em&gt;a tiny bit&lt;/em&gt; of &lt;span class="caps"&gt;CSS&lt;/span&gt; that&amp;#8217;s unique, just for one page or post.
I don&amp;#8217;t want a &lt;em&gt;whole&lt;/em&gt; stylesheet, or to have to add this to my site-wide theme, just for one post&amp;#8201;&amp;#8211;&amp;#8201;I want a simple way to add it in the post&amp;nbsp;itself.&lt;/p&gt;
&lt;p&gt;This is how I did&amp;nbsp;it:&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_in_your_theme_templates"&gt;In Your Theme&amp;nbsp;Templates&lt;/h2&gt;&lt;p&gt;Add an &lt;code&gt;extrahead&lt;/code&gt; block into your templates, inside the &lt;span class="caps"&gt;HTML&lt;/span&gt; &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section, so that you can add extra stuff into the head in some&amp;nbsp;templates:&lt;/p&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_in_base_html_j2"&gt;In&amp;nbsp;base.html.j2&lt;/h3&gt;&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;extrahead&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt; &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class="sidebar"&gt;&lt;p&gt;I already had the &lt;code&gt;extrahead&lt;/code&gt; block and use it for other things, like this in my archive&amp;nbsp;template:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;"base.html.j2"&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
...
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;extrahead&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;SITEURL&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;/theme/css/archive.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Then, in the page/post/article templates, add a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; into the &lt;code&gt;extrahead&lt;/code&gt; if the page metadata contains an &lt;code&gt;extra_css&lt;/code&gt; entry:&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_in_article_html_j2"&gt;In&amp;nbsp;article.html.j2&lt;/h3&gt;&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;"base.html.j2"&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
...
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;extrahead&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  ...
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;article.extra_css&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;article.extra_css&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-2"&gt;&lt;h3 id="_in_page_html_j2"&gt;In&amp;nbsp;page.html.j2&lt;/h3&gt;&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;"base.html.j2"&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
...
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;extrahead&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  ...
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;page.extra_css&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;page.extra_css&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_in_your_postpage"&gt;In Your&amp;nbsp;Post/Page&lt;/h2&gt;&lt;p&gt;Add something like this to the post/page&amp;nbsp;metadata:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;:extra_css: .wavy-underline { text-decoration: red wavy underline; }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and then in your content itself, apply the &lt;span class="caps"&gt;CSS&lt;/span&gt;, something like this for AsciiDoc&lt;a class="footnote-ref" href="#_footnote_1" id="_footnoteref_1" title="View footnote 1"&gt;[1]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;This will have a [.wavy-underline]#red wavy underline#, but this won't.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;something like this for Markdown, where you need to use inline &lt;span class="caps"&gt;HTML&lt;/span&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;This will have a &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wavy-underline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;red wavy underline&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;, but this won't.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;something like this for&amp;nbsp;reStructuredText:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;:extra_css: .wavy-underline { text-decoration: red wavy underline; }

.. role: wavy-underline

This will have a :wavy-underline:`red wavy underline`, but this won't.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and it looks like&amp;nbsp;this:&lt;/p&gt;
&lt;p&gt;This will have a &lt;span class="wavy-underline"&gt;red wavy underline&lt;/span&gt;, but this&amp;nbsp;won&amp;#8217;t.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references_footnotes"&gt;References &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;Footnotes&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;If you want to add whole custom stylesheets to pages or posts, you can use the &lt;a href="https://notabug.org/jorgesumle/pelican-css"&gt;pelican-css plugin&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="https://rest-sphinx-memo.readthedocs.io/en/latest/Sphinx.html#css-class"&gt;Rest &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Sphinx Memo: Defining a css class for some&amp;nbsp;part&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;&lt;section class="footnotes"&gt;&lt;hr /&gt;&lt;ol class="footnotes"&gt;&lt;li class="footnote" id="_footnote_1"&gt;Note that this didn&amp;#8217;t initially work with the pelican asciidoc-reader plugin - but will now that this &lt;span class="caps"&gt;PR&lt;/span&gt; is merged: &lt;a class="bare" href="https://github.com/getpelican/pelican-plugins/pull/1344"&gt;https://github.com/getpelican/pelican-plugins/pull/1344&lt;/a&gt; &lt;a class="footnote-backref" href="#_footnoteref_1" title="Jump to the first occurrence in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Sat, 24 Jul 2021 17:27:47 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/07/24/custom-per-page-css-with-pelican/</guid></item><item><title>On GitHub Copilot</title><link>https://nicolaiarocci.com/on-github-copilot/</link><description>&lt;p&gt;Like everyone else on the planet, I&amp;rsquo;ve been following &lt;a href="https://copilot.github.com/"&gt;GitHub Copilot&lt;/a&gt; since
its launch. It is an impressive achievement and a remarkable milestone for the
deep learning industry, that&amp;rsquo;s for sure. We are obviously at the early stages
in deep learning applied to software development, and it is somewhat unsettling
to ponder what the future might hold in this field.&lt;/p&gt;
&lt;p&gt;Like many others, however, I worry about code quality issues and the risk of
license infringements&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. I am also concerned that the advent of Copilot-like
tools might fundamentally change the software developer experience, if not the
software developer role as a whole, and for the worst.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 24 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/on-github-copilot/</guid></item><item><title>Difference in beliefs</title><link>https://june.kim/difference-in-beliefs/</link><author>june.kim</author><pubDate>Sat, 24 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/difference-in-beliefs/</guid></item><item><title>[STM32] - part 4 - CPU goes brrrrr</title><link>https://ciesie.com/post/stm32_part4/</link><description>&lt;p&gt;This is the final part of a series of articles. I&amp;rsquo;d suggest going through &lt;a href="https://ciesie.com/post/stm32_part1/"&gt;part 1&lt;/a&gt;,
&lt;a href="https://ciesie.com/post/stm32_part2/"&gt;part 2&lt;/a&gt; and &lt;a href="https://ciesie.com/post/stm32_part3/"&gt;part 3&lt;/a&gt; first.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been working on a project (which you can find &lt;a href="https://gitlab.com/MrOneTwo/paw-usb"&gt;here&lt;/a&gt;),
using &lt;a href="https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill.html"&gt;Bluepill&lt;/a&gt; board with a
&lt;em&gt;STM32F103C8T6&lt;/em&gt; microcontroller. It&amp;rsquo;s a USB keyboard project. It uses the &lt;em&gt;libopencm3&lt;/em&gt; project and
I&amp;rsquo;d consider this a pretty minimalistic, baremetal project. It&amp;rsquo;s great for analyzing what happens
when a &lt;em&gt;ARM Cortex-M&lt;/em&gt; processor boots.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s analyze the linker script and the final binary of that project to understand more about the
boot up of a &lt;em&gt;STM32&lt;/em&gt;.&lt;/p&gt;</description><author>ciesie.com</author><pubDate>Sat, 24 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ciesie.com/post/stm32_part4/</guid></item><item><title>TIL that Docker plugins need exec perms on the filesystem</title><link>https://www.zufallsheld.de/2021/07/23/til-that-docker-plugins-need-exec-perms-on-the-filesystem/</link><description>&lt;p&gt;For one of our customers we needed to install the Docker-plugin for loki. To install it, you&amp;nbsp;run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker plugin install loki
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However this failed with the following error&amp;nbsp;message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error response from daemon: dial unix /run/docker/plugins/d442cff8568254659f9aa7d2dd5a30526b69d1ac62593c628b65da8b9e933c6a/loki.sock: connect: no such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running strace …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Fri, 23 Jul 2021 14:28:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/07/23/til-that-docker-plugins-need-exec-perms-on-the-filesystem/</guid></item><item><title>Adventures in Candy Land</title><link>http://blog.untrod.com/2021/07/adventures-in-candy-land.html</link><description>&lt;p&gt;I play Candy Land occasionally with my two boys (the six year old gets
it much more than the three year old). It has the strange property of
involving both zero skill and zero agency. There are absolutely no
decisions for the player to make at any point; the outcome …&lt;/p&gt;</description><author>Untrod</author><pubDate>Fri, 23 Jul 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">http://blog.untrod.com/2021/07/adventures-in-candy-land.html</guid></item><item><title>Savvy estate planning</title><link>https://www.bajkowski.com/savvy-estate-planning/</link><description>A brief review of James L. Cunningham Jr.'s book Savvy Estate Planning: What You Need to Know Before You Talk to the Right Lawyer</description><author>Bajkowski.com</author><pubDate>Fri, 23 Jul 2021 08:06:16 GMT</pubDate><guid isPermaLink="true">https://www.bajkowski.com/savvy-estate-planning/</guid></item><item><title>Index Optimizations</title><link>https://www.marginalia.nu/log/06-optimization/</link><description>&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t chase small optimizations&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Said some smart person at some particular time, probably. If not, he ought to have; if worse comes to worst, I&amp;rsquo;m declaring it now. The cost of 2% here and 0.5% there is high, and the benefits are (by definition) low.&lt;/p&gt;
&lt;p&gt;I have been optimizing Astrolabe, my search engine. The different kind of Search Engine Optimization. I&amp;rsquo;ve spent a lot of time recently doing soft optimization, improving the quality and relevance of search results, to great results. I&amp;rsquo;ll write about that later.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Fri, 23 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/06-optimization/</guid></item><item><title>The best code is easy code</title><link>https://ricardoanderegg.com/posts/easy-code-is-best-code/</link><description>&lt;p&gt;The best code is the one that is easy to understand.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The problem&lt;/h2&gt;
&lt;p&gt;&lt;img alt="" src="/img/s/code_style_years.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://twitter.com/LeaVerou/status/1306001020636540934"&gt;Source&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;People don&amp;rsquo;t focus on code readability. Readability is like writing documentation. It takes extra time, more key presses, and you need to stick to it to make it work in the long term. However, sometimes people write code like this:&lt;/p&gt;
&lt;div class="highlight" style="background: #f8f8f8;"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span style="color: #000;"&gt;settings&lt;/span&gt; &lt;span style="color: #CE5C00; font-weight: bold;"&gt;=&lt;/span&gt; &lt;span style="color: #000; font-weight: bold;"&gt;{&lt;/span&gt;&lt;span style="color: #4E9A06;"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;:&lt;/span&gt; &lt;span style="color: #0000CF; font-weight: bold;"&gt;1&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;,&lt;/span&gt; &lt;span style="color: #4E9A06;"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;:&lt;/span&gt; &lt;span style="color: #0000CF; font-weight: bold;"&gt;2&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;,&lt;/span&gt; &lt;span style="color: #4E9A06;"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;:&lt;/span&gt; &lt;span style="color: #0000CF; font-weight: bold;"&gt;3&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;}&lt;/span&gt;

&lt;span style="color: #000;"&gt;results&lt;/span&gt; &lt;span style="color: #CE5C00; font-weight: bold;"&gt;=&lt;/span&gt; &lt;span style="color: #000; font-weight: bold;"&gt;[&lt;/span&gt;&lt;span style="color: #000;"&gt;parse_document&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;(&lt;/span&gt;&lt;span style="color: #000;"&gt;settings&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;,&lt;/span&gt; &lt;span style="color: #000;"&gt;r&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;)[&lt;/span&gt;&lt;span style="color: #4E9A06;"&gt;&amp;quot;somekey&amp;quot;&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;]&lt;/span&gt;&lt;span style="color: #CE5C00; font-weight: bold;"&gt;.&lt;/span&gt;&lt;span style="color: #000;"&gt;get&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;(&lt;/span&gt;&lt;span style="color: #4E9A06;"&gt;&amp;quot;foobar&amp;quot;&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;,&lt;/span&gt; &lt;span style="color: #4E9A06;"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;)&lt;/span&gt; &lt;span style="color: #204A87; font-weight: bold;"&gt;for&lt;/span&gt; &lt;span style="color: #000;"&gt;r&lt;/span&gt; &lt;span style="color: #204A87; font-weight: bold;"&gt;in&lt;/span&gt; &lt;span style="color: #000;"&gt;get_documents&lt;/span&gt;&lt;span style="color: #000; font-weight: bold;"&gt;()]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code is understandable, but I need to guess:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I suppose &lt;code&gt;parse_document()&lt;/code&gt; is returning some kind of dictionary&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s another indexing operation. The dictionary is probably nested.&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;r&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Is &lt;code&gt;parse_document()&lt;/code&gt; mutating &lt;code&gt;settings&lt;/code&gt;? Can &lt;code&gt;settings&lt;/code&gt; have other keys?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you find a situation like this, the solution is usually going inside the functions and reading the code, which may be tedious. Yes, maybe you are avoiding creating a couple of variables, but at what cost?&lt;/p&gt;</description><author>Posts on rand[om]</author><pubDate>Fri, 23 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ricardoanderegg.com/posts/easy-code-is-best-code/</guid></item><item><title>A deep dive on macOS universal binaries</title><link>https://www.jviotti.com/2021/07/23/a-deep-dive-on-macos-universal-binaries.html</link><description>This article describes in detail how Mach-O universal binaries work</description><author>Juan Cruz Viotti</author><pubDate>Fri, 23 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jviotti.com/2021/07/23/a-deep-dive-on-macos-universal-binaries.html</guid></item><item><title>The Full Stack Data Scientist</title><link>https://bytepawn.com/the-full-stack-data-scientist.html</link><description>&lt;p&gt;What are the core skills a data scientist needs to sustainably achieve bottom-line impact, without blocking on external help from other roles?&lt;br /&gt;&lt;br /&gt; &lt;img alt="Data Scientist" src="/images/modern_data_scientist.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Fri, 23 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/the-full-stack-data-scientist.html</guid></item><item><title>Goodbye Evernote, Hello Bear</title><link>https://smcleod.net/2021/07/goodbye-evernote-hello-bear/</link><description>&lt;p&gt;Until recently I used Evernote for my notes, web clippings and document drafts, I was a subscriber going on 14 years and had over 3000 notes.&lt;/p&gt;
&lt;p&gt;However in recent years Evernote has gone downhill to the point it could be considered hostile to it&amp;rsquo;s users.&lt;/p&gt;
&lt;p&gt;The native macOS app was replaced with an Electron Javascript webframe and like almost all Electron apps I&amp;rsquo;ve tried it was plagued by poor performance, memory leaks and aggressive idle CPU utilisation and above all else it now lacks the low latency feeling the native application had.&lt;/p&gt;</description><author>smcleod.net</author><pubDate>Thu, 22 Jul 2021 22:00:00 GMT</pubDate><guid isPermaLink="true">https://smcleod.net/2021/07/goodbye-evernote-hello-bear/</guid></item><item><title>Filename Hook</title><link>https://nevesnunes.github.io/blog/2021/07/22/Filename-Hook.html</link><description/><author>onKeyPress</author><pubDate>Thu, 22 Jul 2021 20:46:03 GMT</pubDate><guid isPermaLink="true">https://nevesnunes.github.io/blog/2021/07/22/Filename-Hook.html</guid></item><item><title>Summer Reading 2021</title><link>https://rohitjha.com/blog/summer-reading-2021/</link><description>&lt;p&gt;When the world forces you indoors, you need to find a way out. If you are faced with a summer with COVID lockdowns in your city, you may want to hit some of these books to explore a different world out there.&lt;/p&gt;&lt;p&gt;My other reading updates can be found on&lt;/p&gt;</description><author>THINK@RJ</author><pubDate>Thu, 22 Jul 2021 16:47:32 GMT</pubDate><guid isPermaLink="true">https://rohitjha.com/blog/summer-reading-2021/</guid></item><item><title>The Genius of Apple's Name</title><link>https://www.swyx.io/apples-name</link><description>An excerpt from Becoming Steve Jobs that made me stop in my tracks.</description><author>swyx's site RSS Feed</author><pubDate>Thu, 22 Jul 2021 05:11:17 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/apples-name</guid></item><item><title>How to Exfiltrate Code from Bitbucket</title><link>https://etodd.io/2021/07/21/how-to-exfiltrate-code-from-bitbucket/</link><description>&lt;p&gt;
Every year or so, something compels me to start over with a fresh OS image.
Most recently, that something was an unfortunate water bottle incident.
Once again, I need to pull down my digital life from the internet.
&lt;/p&gt;
&lt;p&gt;
The code and assets for this blog currently live in a 500 MB Git repository, which was hosted on Bitbucket until earlier today.
Let's try cloning it!
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-bash"&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;evantodd@HW-0063 ~ % git clone git@bitbucket.org:etodd/etodd.io.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;Cloning into &lt;span style="color: #e6db74;"&gt;'etodd.io'&lt;/span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;remote: Enumerating objects: 2237, &lt;span style="color: #66d9ef;"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;remote: Counting objects: 100% &lt;span style="color: #f92672;"&gt;(&lt;/span&gt;2237/2237&lt;span style="color: #f92672;"&gt;)&lt;/span&gt;, &lt;span style="color: #66d9ef;"&gt;done&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;remote: Compressing objects: 99% &lt;span style="color: #f92672;"&gt;(&lt;/span&gt;1885/1903&lt;span style="color: #f92672;"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display: flex;"&gt;&lt;span&gt;&lt;span style="color: #75715e;"&gt;# hang&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;No worries, we can clone over HTTPS!&lt;/p&gt;</description><author>Evan Todd</author><pubDate>Wed, 21 Jul 2021 11:00:00 GMT</pubDate><guid isPermaLink="true">https://etodd.io/2021/07/21/how-to-exfiltrate-code-from-bitbucket/</guid></item><item><title>Book notes - OSTEP (part 2)</title><link>https://river.me/blog/book-notes-ostep-2/</link><description>My notes on &amp;ldquo;Operating Systems: Three Easy Pieces&amp;rdquo; - part 2, Concurrency</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 20 Jul 2021 14:07:21 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-notes-ostep-2/</guid></item><item><title>Pico Ada Libraries Release 0.5.0</title><link>https://synack.me/ada/pico/2021/07/19/rp2040-hal-release-0-5-0.html</link><description>I’ve tagged release 0.5.0 of the rp2040_hal, pico_bsp, and pico_examples libraries for the Raspberry Pi Pico.</description><author>Jeremy Grosser</author><pubDate>Tue, 20 Jul 2021 06:58:00 GMT</pubDate><guid isPermaLink="true">https://synack.me/ada/pico/2021/07/19/rp2040-hal-release-0-5-0.html</guid></item><item><title>Comparing NeuralProphet and Prophet for timeseries forecasting</title><link>https://bytepawn.com/comparing-neuralprophet-and-prophet-for-timeseries-forecasting.html</link><description>&lt;p&gt;I compare Prophet and NeuralProphet performance using a toy forecasting benchmark.&lt;br /&gt;&lt;br /&gt;&lt;img alt="NeuralProphet" src="/images/neuralprophet5.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Tue, 20 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/comparing-neuralprophet-and-prophet-for-timeseries-forecasting.html</guid></item><item><title>The CSS :is selector is pretty neat</title><link>https://duncanlock.net/blog/2021/07/19/the-css-is-selector-is-pretty-neat/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;I found out about the &lt;span class="caps"&gt;CSS&lt;/span&gt; &lt;code&gt;:is&lt;/code&gt; selector today, via this &lt;a href="https://blog.jim-nielsen.com/2021/things-i-learned-reading-webkits-ua-stylesheet/"&gt;&amp;#8220;Things I Learned Reading Webkit&amp;#8217;s &lt;span class="caps"&gt;UA&lt;/span&gt; Stylesheet&amp;#8221;&lt;/a&gt;&amp;nbsp;article.&lt;/p&gt;
&lt;p&gt;It takes a selector list as its argument, and selects any element that can be selected by one of the selectors in that list. This is useful for writing large selectors in a more compact&amp;nbsp;form:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Instead of this: */&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* You can do this: */&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;you can also put it in the middle of selectors, like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Instead of this: */&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* You can do this: */&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a class="bare" href="https://developer.mozilla.org/en-US/docs/Web/CSS/:is"&gt;https://developer.mozilla.org/en-&lt;span class="caps"&gt;US&lt;/span&gt;/docs/Web/&lt;span class="caps"&gt;CSS&lt;/span&gt;/:is&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="bare" href="https://www.caniuse.com/?search=is"&gt;https://www.caniuse.com/?search=is&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="bare" href="https://blog.jim-nielsen.com/2021/things-i-learned-reading-webkits-ua-stylesheet/"&gt;https://blog.jim-nielsen.com/2021/things-i-learned-reading-webkits-ua-stylesheet/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Mon, 19 Jul 2021 22:15:25 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/07/19/the-css-is-selector-is-pretty-neat/</guid></item><item><title>Temporal - the iPhone of System Design</title><link>https://www.swyx.io/why-temporal</link><description>Temporal ties Orchestration, Event Sourcing, and Workflows-as-Code in one distributed system and it is eating the world.</description><author>swyx's site RSS Feed</author><pubDate>Mon, 19 Jul 2021 10:46:12 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/why-temporal</guid></item><item><title>Interesting Go Memory Issue</title><link>https://boyter.org/posts/interesting-go-memory-issue/</link><description>&lt;p&gt;So had an interesting experience with Go the other day.&lt;/p&gt;
&lt;p&gt;This is part of building a bloom filter index which is going to replace the index in searchcode.com&lt;/p&gt;
&lt;p&gt;So I was trying to verify how long a search would take when the index was full. When trying it out on a machine to see what the performance would be I built a version to create some very large slices over 64,000,000 items in length to simulate the expected workload. This should take about 53 GB in memory with all the overhead and such added in.&lt;/p&gt;</description><author>Ben E. C. Boyter</author><pubDate>Mon, 19 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/interesting-go-memory-issue/</guid></item><item><title>Root Beer Float Ice Cream</title><link>https://dustin.lammiman.ca/icecream/rootbeer/</link><description>&lt;h2 id="root-beer-float-ice-cream" tabindex="-1"&gt;Root Beer Float Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/rootbeer/#root-beer-float-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/rootbeer/#ingredients"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;6 Tbsp Granulated Sugar&lt;/li&gt;
&lt;li&gt;2 Tbsp Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;6 Tbsp Root Beer Syrup (I used Organic Fair Root Beer Syrup, you may need to adjust quantities for other brands. This was the suggested amount to make 3 Cups of Root Beer)&lt;/li&gt;
&lt;li&gt;1 1/3 Cups Milk (I used 2%)&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/rootbeer/#instructions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;In a large bowl whisk together sugar, milk powder, and xanthan gum.&lt;/li&gt;
&lt;li&gt;Add milk and root beer syrup. Mix very well.&lt;/li&gt;
&lt;li&gt;Add cream and mix well again.&lt;/li&gt;
&lt;li&gt;Place mixture in a sealed contained and refridgerate for 12+ hours.&lt;/li&gt;
&lt;li&gt;Churn according to your ice cream maker's instructions.&lt;/li&gt;
&lt;li&gt;Split the ice cream into two containers, each ~1L in size. You now have Root Beer ice cream. To make it root beer float ice cream, make your favourite vanilla ice cream, and layer on top of your root beer ice cream!&lt;/li&gt;
&lt;/ol&gt;</description><author>Dustin.Lammiman</author><pubDate>Mon, 19 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/icecream/rootbeer/</guid></item><item><title>JavaScript gotchas</title><link>https://jonlu.ca/posts/javascript-facts</link><description>A tour of surprising JavaScript behavior involving functions, coercion, arrays, and language edges.</description><author>JonLuca's Blog</author><pubDate>Sun, 18 Jul 2021 16:18:39 GMT</pubDate><guid isPermaLink="true">https://jonlu.ca/posts/javascript-facts</guid></item><item><title>Build Your First Solidity Dapp With Web3.js and MetaMask</title><link>https://blog.adnansiddiqi.me/build-your-first-solidity-dapp-with-web3-js-and-metamask/</link><description>&lt;p&gt;Actually, I had planned another topic in Solidity Series for web3.js but then the post could go off tangent hence I am discussing the basics of web3.js and how to interact with a smart contract. I am going to use the same contract I had created here. Basically, it is the web3.js version of the last post. Unlike other posts about web3.js based frontends that use NodeJs and React, I am not going to use any such thing because it is not required to create the front-end of a decentralized app. You do not even need NodeJS at all. You can use your existing skillsets like PHP/Laravel, Django, Rails, etc to create a dApp that talks with an Ethereum blockchain via web3.js. In this post, I am going to create a simple HTML-based interface that will be interacting with the smart contract I made earlier. We will learn how to interact with MetaMask and how to connect MetaMask with Ganache. In the end, the app will be working something like the below: &amp;#160; What is MetaMask According to MetaMask official website: A crypto wallet &amp;#38; gateway to blockchain apps &amp;#160; They started as a Chrome extension to let web applications interact with Ethereum based blockchains. Users interact with MainNets or TestNets by either creating a new wallet or an existing one by importing private keys. MetaMask and Ganache Integration In the previous post, I already told you how to set up Ganache. Now we have to connect our Ganache-based blockchain with MetaMask which is pretty easy. The RPC URL is the one provided by Ganache on its interface. 1337 is the ChainID of Ganache. Once the blockchain node is connected, you will have to add an account in the MetaMask wallet. All you have to do is to import the keys from one of your accounts and label it. I integrated my first account here for the development purpose. Now let&amp;#8217;s start coding. One of the first things we have to do is to include all required libraries. I am using Bootstrap and Jquery apart from web3.js library. This is the structure of the same page I demoed above. It contains the icon of MetaMask which is greyed out to inform the user that MetaMask is not installed yet. We will write the code to implement this functionality. function detectMetaMask() { if (typeof window.ethereum !== 'undefined') { return true } else { return false } } The function is pretty simple. When you add MetaMask, it exposes ethereum object globally. If this object is available it means Metamask is enabled. I am using jQuery to gray-out/colorized the logo. Before we call it, I will use jQuery&amp;#8217;s onready() function to call it. $( document ).ready(function() { m = detectMetaMask() if(m) { $('#metaicon').removeClass('meta-gray') $('#metaicon').addClass('meta-normal') $('#enableMetamask').attr('disabled',false) connect() } else { $('#enableMetamask').attr('disabled',true) $('#metaicon').removeClass('meta-normal') $('#metaicon').addClass('meta-gray') } }) &amp;#160; If you run this code it&amp;#8217;d produce the following result &amp;#160; As you can see above, it disables both the button and logo when I remove the extension. Now, the next step is to write the logic of Connect with MetaMask button. I am introducing a new function conenct() here for this purpose. I am also going to introduce handleAccountsChanged callback function that handles the response of  eth_requestAccounts method offered by MetaMask. function handleAccountsChanged(accounts) { console.log('Calling HandleChanged') if (accounts.length === 0) { console.log('Please connect to MetaMask.'); $('#enableMetamask').html('Connect with Metamask') } else if (accounts[0] !== currentAccount) { currentAccount = accounts[0]; $('#enableMetamask').html(currentAccount) $('#status').html('') if(currentAccount != null) { // Set the button label $('#enableMetamask').html(currentAccount) } } console.log('WalletAddress in HandleAccountChanged ='+walletAddress) } function connect() { console.log('Calling connect()') ethereum .request({ method: 'eth_requestAccounts' }) .then(handleAccountsChanged) .catch((err) =&amp;#62; { if (err.code === 4001) { // EIP-1193 userRejectedRequest error // If this happens, the user rejected the connection request. console.log('Please connect to MetaMask.'); $('#status').html('You refused to connect Metamask') } else { console.error(err); } }); } &amp;#160; ethereum.request is a generic function provided by MetaMask to carry out different operations. Here I am asking MetaMask to return the attached accounts. If they are not, it pops up a dialog box to connect it. You can learn more about it here. &amp;#160; Lots of things are happening above. Assuming you have imported wallet keys when I click on the button, it activates the MetaMask interface and asks you to connect the wallet you want to use on this website. You can connect multiple wallets but for now, let&amp;#8217;s rely on a single wallet. Once it successfully connects this website with the wallet, I am displaying the wallet address on the button. This process is somewhat akin to a typical login mechanism where you show a welcome note after logged in. On cancellation, I am showing a message that the visitor of the site refused to connect his/her MetaMask wallet. Now whatever the operation you perform, it will be using your connected wallet. OK, so we have connected our wallet, the only thing is left now to interact with the smart contract. So let&amp;#8217;s begin to write the code! I just noticed I have not written the code to interact with Ganache. For that, we have to use the web3.js library. I already have included it in my HTML file(You do not need node or npm for that purpose. After all it is a simply JS library, we can remain part of old school yet use new technology). &amp;#160; let web3; let abi; $.getJSON("build/contracts/FirstContract.json", function(result) { abi = result.abi }); try { web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545")); } catch (error) { alert(error) } A couple of things happening here: After declaring the required variables, I first got the ABI of the contract. We will be using it while interacting with the smart contract routines. Next, I am connecting the Ganache Ethereum blockchain. So far so good, now I am going to add a function, named, getValue() here. This function will be called on page loading. The document.ready() now looks like the below: $( document ).ready(function() { m = detectMetaMask() if(m) { $('#metaicon').removeClass('meta-gray') $('#metaicon').addClass('meta-normal') $('#enableMetamask').attr('disabled',false) // connect() } else { $('#enableMetamask').attr('disabled',true) $('#metaicon').removeClass('meta-normal') $('#metaicon').addClass('meta-gray') } $('#enableMetamask').click(function() { connect() }); try { web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545")); } catch (error) { alert(error) } //Fetch Value from Smart Contract getValue() }) The getValue() contains the following code to interact with the blockchain &amp;#160; async function getValue() { console.log('GetValue') const contractFirst = new web3.eth.Contract( abi, contactAddress ); contractFirst.methods.getValue().call().then(function (result) { $('#getValue').html(result) }); } I am creating the instance of eth.Contract and passing both abi and contactAddress in the constructor. Once it&amp;#8217;s established, I call the smart contract&amp;#8217;s getValue() function here and displaying the result in the div. Next, I am going to write the logic of setValue() function which will be called on click event of the button and pass the value from input to the smart contract. async function setValue() { console.log('Set Value') input_value = $('#value').val() if(input_value.trim() == '') { input_value = 0 } if(!isNaN(input_value)) { input_value = parseInt(input_value) } const contractFirst = new web3.eth.Contract( abi, contactAddress ); contractFirst.methods.setValue(input_value).send({from:currentAccount}).then(function (result) { console.log(result); $('#getValue').html(input_value) }); } It is very similar to getValue() one. The only differences here are that I am using send() instead of call() I am also setting the wallet address here. I am doing this because when you pass a value in the blockchain you are actually making a transaction hence it&amp;#8217;d deduct the gas amount from your wallet. Ok, so when I click the Set Value button it returns the following JSON: You can always confirm: As you can see the transaction hash is the same. You can see that the 25 was the input value. Conclusion So in this (another long) post, you learned how you can harness your existing HTML/Javascript skills to write frontends of dApps. You do not need to learn NodeJS or React, as you see here. Since it was a single HTML file, I have committed here on Github with the name contract_demo.html. &amp;#160; &amp;#160; If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/build-your-first-solidity-dapp-with-web3-js-and-metamask/"&gt;Build Your First Solidity Dapp With Web3.js and MetaMask&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Sun, 18 Jul 2021 15:51:23 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/build-your-first-solidity-dapp-with-web3-js-and-metamask/</guid></item><item><title>Naches Peak</title><link>https://sam.hooke.me/trip/2021/07/naches-peak/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Sun, 18 Jul 2021 13:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/07/naches-peak/</guid></item><item><title>Book Review. Eight Days in May: The Final Collapse of the Third Reich</title><link>https://nicolaiarocci.com/book-review.-eight-days-in-may-the-final-collapse-of-the-third-reich/</link><description>&lt;p&gt;Volker Ullrich&amp;rsquo;s Eight Days in May describes the period from April 30, 1945,
the day of Hitler&amp;rsquo;s suicide, to May 8, the day of signing the German
capitulation, with significant jumps backwards in time and some hops in the
future. We&amp;rsquo;re covering only eight days, and the dictator dies on day one. What
essential events might ever have happened in such a short period? Well, many
pivotal ones, as this well-researched work shows us.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 18 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review.-eight-days-in-may-the-final-collapse-of-the-third-reich/</guid></item><item><title>The Mind's A Field</title><link>https://www.marginalia.nu/log/05-minds-field/</link><description>&lt;p&gt;I find if I do not regularly plant interesting thoughts in my mind, it will rarely grow them spontaneously. I need to read interesting books, or have interesting conversations; if I do not, I&amp;rsquo;ll look back at my ideas and find I haven&amp;rsquo;t really had any in a very long time. Thoughts will grow whether I take care to plant them or not, but what grows if I have been careless is weeds.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Sun, 18 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/05-minds-field/</guid></item><item><title>Monitoring VPN / Direct Connect Connectivity</title><link>https://blog.cetinich.net/content/2021/2021-vpc-icmp/</link><description>In an on premise network it is typical to do some type of topology
discovery via the MIB-II SNMP tables of all your network gear and then
use ICMP to ping each of the discovered routers or switches respective
management interfaces to validate connectivity and contribute to
intelligence when parts of the network go dark and speed up root cause
analysis.</description><author>Brent's Blog</author><pubDate>Sun, 18 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.cetinich.net/content/2021/2021-vpc-icmp/</guid></item><item><title>Timeseries forecasting with Prophet</title><link>https://bytepawn.com/timeseries-forecasting-with-prophet.html</link><description>&lt;p&gt;Prophet is a simple to use timeseries forecasting library by Facebook.&lt;br /&gt;&lt;br /&gt;&lt;img alt="Prophet" src="/images/prophet1.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 18 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/timeseries-forecasting-with-prophet.html</guid></item><item><title>The Internet is Rotting</title><link>https://nicolaiarocci.com/the-internet-is-rotting/</link><description>&lt;p&gt;Terrific piece by Jonathan Zittrain, on The Atlantic, on &lt;a href="https://www.theatlantic.com/technology/archive/2021/06/the-internet-is-a-collective-hallucination/619320/"&gt;link rot and digital
preservation&lt;/a&gt;. I love how well documented and informative it is. Yet, it
remains perfectly approachable for both the non-knowledgeable reader and the
technically savvy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Too much has been lost already. The glue that holds humanity’s knowledge
together is coming undone.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;We need more content like this.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 17 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/the-internet-is-rotting/</guid></item><item><title>Duality of Expression</title><link>https://june.kim/duality-of-expression/</link><author>june.kim</author><pubDate>Sat, 17 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/duality-of-expression/</guid></item><item><title>Rhythm and Vibe</title><link>http://dimitarsimeonov.com/2021/07/16/rhythm-and-vibe</link><description>&lt;p&gt;&lt;img alt="marathon" src="http://mitko.github.io/assets/image/20210716_vibe/marathon.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The myth of one-man orchestra&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I think I finally understand how multiplayer creation works.
My college dorm had a music room in the basement, and while I couldn’t play any instruments I loved hanging out with friends when they organized jam-sessions. Friends would invite their musician friends over to jam, and I would have the best time seeing how they pick up a tune, and the room filled with sound until my bones were resonating with it, and I was singing along, and inhabited a parallel universe where p-sets, projects or papers didn’t exist. Only sound, rhythm and vibes existed. One of those musicians devoted himself to music and made a great impression on me. Let’s call him Dave the Metal, as was a fan of Dave Mustaine, the leader of Megadeth. Our Dave had to work long hours at a pizza place, but he dedicated nearly all his extra time to music - playing it, writing it, listening to it and geeking about gear.&lt;/p&gt;

&lt;p&gt;One day Dave showed us a song he wrote, where he recorded the vocals, the guitar, the bass and the drums separately and he put them together into a song. He was a one-man orchestra, and I felt amazed that he was able to pull it off, as I imagined it was arduous, tricky and rare to do all the parts alone. The song was good and I sometimes still remember it, but the jam sessions had an extra magical element, of multiple people adjusting to each other in real time. Naturally, there’s a limit to how good you can get by yourself. A genius goes far but it only goes so far alone. But an actual “orchestra” of geniuses which resonate and amplify each other and whose domains fit each other - that orchestra can produce music that moves the listeners stronger and deeper.&lt;/p&gt;

&lt;p&gt;Even if we look at domains where individuals can seemingly do it all, we realize nobody creates anything massively impactful by themselves. A famous author like J.K. Rowling or George R.R. Martin may be a master storyteller, who can weave characters and emotions in an intricate basket of a story. Yet, they have editors, and publishers which help them productize the stories, tucking the loose straws in. Artists paint beautiful illustrations for the book cover. When the story is later turned into a movie or a show the costume designers jam in a whole new dimension to the universe, and they contribute so much we can’t imagine Game of Thrones or Harry potter without their signature costumes look. Creating something large is a group effort, and though the authors appear to be the key players, it takes an orchestra of geniuses jamming and creating together to make this happen.&lt;/p&gt;

&lt;p&gt;Similarly, in ball related sports such as soccer, football, volleyball and basketball, for a team to score, they almost always have to pass the ball between themselves. Passing the proverbial “ball” is what musicians jamming together seem to be doing as well, as they give each other the creative space to express themselves. Leo Messi, Lebron James or Tom Brady may occasionally do a hero-run and score by themselves, but that’s the exception, not the rule. When Leo Messi scores, he needs to coordinate with his teammates so they pass him the ball at the right position where he has that sliver of advantage that he can convert to a goal. Even Eliud Kipchoge, the first person to run a marathon under 2 hours, had a team of pacemakers helping to reduce the wind resistance in front of him for his record marathon run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowlege work is multiplayer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And yet, if we look at software engineers, or any other knowledge based or creative work, we often assume that they work in isolation. Knowledge work such as programming may feel different, as a lot of it can happen in a “single-player” mode, and it may seem at a time that it is closer to a single player sport such as sprinting or high jumping, but the most successful knowledge workers engage in multiplayer knowledge soccer instead. John Carmack is famous for his heroic coding, but even he didn’t build Doom alone - he worked with other people, and even if he had the most important role to play, he had to “pass the ball” to others.&lt;/p&gt;

&lt;p&gt;To further understand what “passing the ball” means, imagine a modified version of soccer - called “No-pass soccer”. Whenever a team is on the attack, each of their players a ball, they’re not allowed to pass it to anyone, and as soon as they lose possession of “their” ball, their attacking turn is over. Since they cannot send or receive passes, they become useless. It might be counter-intuitive at first, but in no-pass soccer, the attacking team actually has much lower chance of scoring a goal per attack, than a normal team. And why is that? How can I be so certain about the outcome of a made up sport? Well, my simple reasoning goes to argue that once the defending team has started eliminating people, by splitting them from their balls, they have ever increasing advantage and easily pick off the remaining attackers. It is no surprise that some of the most dominant soccer clubs such as Barcelona use a lot of passing.&lt;/p&gt;

&lt;p&gt;The metaphor is applicable to other domains, whenever a team of people try to achieve some abstract goal together. If they have one common goal, they can pass the initiative around, like a ball depending on each other’s skills. This doesn’t guarantee success, but it definitely increases the chances, as different members of the group may have the ability to overcome different challenges.&lt;/p&gt;

&lt;p&gt;At the same time, everybody on the team has a knowledge of the basics - running, positioning, dribbling, passing, and shooting, but they also have some specialized skills - goalie, attacker, wing, playmaker, defender. They can single handedly, or leggedly take care of most situations, but when the situation gets trickier, they ask a specialist for help, by passing the initiative and getting them to help.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The vibe and rhythm of a team&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot of knowledge works teams do not operate as a “genius orchestra” - instead they often produce cacophony, or at best a “solo”. And why is that? I’d like to explore the question of how to convert such teams into a source of creative wonder. I’ll look through the prisms of two concepts - vibe and rhythm. I am not a musician, so bear with me as I transplant these concepts into a new domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vibe as agreement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the purpose of this essay, let’s say that a team having a shared Vibe means that the team is in high agreement of what they are doing, and what the end result it is. For a music orchestra to have a Vibe, they’ll be in agreement of whether they are creating Jazz, or are they creating Metal, or are they creating Hip Hop, or something else. They will also agree on what emotional effect they want to create in the listener. That shared Vibe becomes the Vibe of the song.&lt;/p&gt;

&lt;p&gt;For a knowledge work team, shared Vibe means having a crisp agreement of who their customers are, and how the product is helping them, and how is that affecting the world. In corporate-speak, people use word “vision” to mean something related. The difference between vision and Vibe is that vision is something that is stated from a leader, and the Vibe is something that is &lt;strong&gt;felt&lt;/strong&gt; by everybody. A Vibe is more primal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rhythm as teamwork&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Vibe is about the abstract and the future, Rhythm is all about the concrete and the present. Rhythm is about being in sync. When the orchestra is in the same rhythm, the drummer is hitting the drums at the right tempo, which matches the tempo the guitarist is pulling the strings, and the tempo of the vocals. For a knowledge work team, Rhythm is the shared processes, mechanisms and standard operating procedures which make the team effective.&lt;/p&gt;

&lt;p&gt;Rhythm affects Vibe, and Vibe affects Rhythm. If you are hoping that this essay will provide a reductionist distinction between the two - it won’t. Instead I will try to still shine enough light on Rhythm and Vibe so that the image of them becomes crisp and memorable, feeding back into our everyday knowledge work, and giving us a bit more grip.&lt;/p&gt;

&lt;p&gt;Both Rhythm and Vibe are examples of shared information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutual information&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mutual information helps to grease the gears of a team, and turn the cacophony into harmony, so how do we get more of it? Well… let’s understand a bit what is and what isn’t mutual information.&lt;/p&gt;

&lt;p&gt;&lt;img alt="trend" src="http://mitko.github.io/assets/image/20210716_vibe/trend.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;As a term it began its life when the computer science pioneer Claude Shannon coined it in 1948 for his famous Noisy Channel Theorem. The gist of that theorem is that the more “mutual information” two people share, the faster they can exchange other information. The more mutual information team members share, the easier it is for them to “pass the ball” and “jam” with each other. Shared Rhythm allows team members to know when and where to pass, how to receive, and how to keep on keeping on. Shared Vibe allows team members to feel where they’re headed and to trust the motivations of the rest of the team. Mutual information, Rhythm and Vibe become the base for building trust, as the team members would learn that passing the initiative works and is effective.&lt;/p&gt;

&lt;p&gt;&lt;img alt="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Entropy-mutual-information-relative-entropy-relation-diagram.svg/1200px-Entropy-mutual-information-relative-entropy-relation-diagram.svg.png" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Entropy-mutual-information-relative-entropy-relation-diagram.svg/1200px-Entropy-mutual-information-relative-entropy-relation-diagram.svg.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start small and grow mutual information&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mutual information builds upon shared beliefs. For example, if I read a book, and you read the same book, this doesn’t mean we have any mutual information. We might have had different beliefs to begin with and have gotten disjoint and even opposite conclusions. If we are members of two opposing political parties, we might have learned nothing in common from the same book. We might even end up more aggravated at each other. If we don’t have any mutual information, any mutual Vibe before we read the book, we would not end with more after we read it. If you don’t believe me about the book, just see how people sympathizing to different political party react to the same news. Any news!&lt;/p&gt;

&lt;p&gt;In another, more timid example, if we are both programmers on the same team and our documentation says “code should be indented consistently”, we might come up with two different reasons why that is. I might think it is because the code needs to be easier to review, whereas you might think that the goal is for the code to be more easily maintained in the future, or even processed with an automated tool. Even though we might share the same Rhythm of consistent indentation, we might have two different Vibes about the purpose of the code. Furthermore, I might even think that we should indent code with tabs, and you might think that we should indent code with spaces. Those are two different Rhythms. If we can’t agree on the Rhythm, we won’t get started building at all. In that case we would share no mutual information really, no Vibe and no Rhythm.&lt;/p&gt;

&lt;p&gt;&lt;img alt="start small" src="http://mitko.github.io/assets/image/20210716_vibe/startsmall.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;Mutual information can only grow out of existing mutual information. That’s why in the movies when aliens are trying to talk to us, they use mathematics. Because we know that mathematics and physics are the only shared beliefs we have with aliens. In order to reliably pass information both sides need to know what they both know and agree on. In the case of aliens, mathematics may be the common meeting point (a.k.a. Shelling point) out of where we start the conversation.&lt;/p&gt;

&lt;p&gt;One caveat about mutual information is that hidden mutual information doesn’t count. If a team member hasn’t proven to the other team members that they agree about seeing the world the same way, then they cannot lean on that as a way to add new mutual information.&lt;/p&gt;

&lt;p&gt;When team members have been together for a while, they will have shared documentations, processes, SOPs, OKRs, metrics and a shared vision. They build these processes one conversation at a time, and the mortar that holds the bricks together is honest listening. When two people are having a conversation, and one of them is listening with an open mind, they are absorbing the Vibe and Rhythm from the other. They are receiving information and incorporating that into their own view of shared information. Of mutual information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resonate to increase mutual inromation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A common pitfall of teamwork might be when a team operates with a lot of shared rhythm but loses the shared vibe. You know who else does that? Robots. But we are humans and a part of us will always revolt when treated like robots. There are simple things to do which can create and grow back the shared vibe - and the main one is to be listening, giving our full attention to the other person, and then respond in an authentic and engaged manner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Listen so you can absorb&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We are horrible at absorbing information other people are telling us. I once saw a video where a teacher was lambasting a student, because the student had their hands up while another student was still presenting. The teacher was telling the student - “when your hand is up, you’re thinking about what you’ll say next, not what the other person is saying now”. I think about that video often, as I am often prone of anticipating what would I say next.&lt;/p&gt;

&lt;p&gt;In the movie “Fight Club” when The Narrator talks to Marla about the groups they visit, they realize they both go there because people actually listen, instead of waiting for their own turn to speak.&lt;/p&gt;

&lt;p&gt;If we are in a conversation and you say stuff, and I say stuff, we are not engaged in communication unless we’re actively listening, processing and internalizing what each other is saying. Otherwise, we might as well be speaking different languages or shouting at each other. If I am waiting to say stuff I am not listening&lt;/p&gt;

&lt;p&gt;Mutual information requires listening. In Shannon’s theorem, the typical examples are sound over radio, or bits over a wire, where the receiving side is absorbing and processing the received information. In a sense, the other side is &lt;strong&gt;listening&lt;/strong&gt;. If we’re not listening, we’re not increasing our mutual information and we’re not getting better at “passing the ball”. We’re not more likely to score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case study: vibe-setters.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some people can be amazing at setting the vibe. This is similar to charisma, but charisma is a bit more about the individual than the collective. A master negotiator such as Chris Voss would try to get under the skin of their counterpart and build a rapport before making demands. When the two parties of negotiation start vibing together a win-win resolution is often much closer. For the negotiator to build rapport they have to be actively listening. In the book “Never split the difference”, active listening is one of the central concepts early on.&lt;/p&gt;

&lt;p&gt;Other people can be amazing at “re-framing” - they will influence the other people to see the world from a given waypoint, thus creating a shared vibe in the process. But to do that successfully, they first need to listen to others and absorb the other people’s frames. The new frame has to match the other person’s frame on the in-flexible borders, and be different on the flexible borders. The new frame can have a different Vibe or Rhythm from the old frame.&lt;/p&gt;

&lt;p&gt;&lt;img alt="reframe" src="http://mitko.github.io/assets/image/20210716_vibe/reframe.png" width="100%" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vibrate back.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve absorbed the message from the other person, you can respond back. Feeling the vibe of the message can help you respond in a way that is more present and engaged with the other person. Having responded authentically, that shows a “proof of work” to the other person, that you are engaging in the conversation. As a result, they’ll likely reciprocate and engage as well. It might take some time to converge on a shared vibe, but then the conversation might go faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The vibe of a “single” person&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another aspect of the one-man-orchestra myth is that even a single person is a cohesive, unified and consistent unit. A person changes from day to day, from year to year, and within each person there’s often internal conflict between the parts of the personality. And having parts of a personality is not a unique perk reserved only for those with schizophrenia or bi-polar disorder. Each us experiences this on a daily, even hourly basis. Every time we experience a dilemma, we are experiencing such internal conflict. For example, one part of us, lets roughly call it the mouth, wants to eat another cookie because it is tasty and excites the mouth neurons. Another part of us, the stomach, is feeling full and doesn’t want any more. “Three cookies is plenty already. Basta!!!” it says and thus the internal scandal rages inside our minds. Sometimes the stomach is silenced, other times, it’s heeded.&lt;/p&gt;

&lt;p&gt;If we start to look on a person not as a monolith, but more like a team (see “Society of Mind”, “Internal Family Systems”), then we can un-generalize the concepts of a shared Vibe and Rhythm back to the same person. When that internal team plays music together we have what Carl Jung calls individuation - a cohesive self. A lot of my challenges, regrets and problems in life happened because different parts of me were in conflict with each other which got stuck in an unresolved state. So now I’m working towards a different version of a “one-man orchestra” - where the orchestra is within me, and plays in harmony.&lt;/p&gt;</description><author>D13V</author><pubDate>Sat, 17 Jul 2021 01:08:34 GMT</pubDate><guid isPermaLink="true">http://dimitarsimeonov.com/2021/07/16/rhythm-and-vibe</guid></item><item><title>The journey to controlling external monitors on M1 Macs</title><link>https://alinpanaitiu.com/blog/journey-to-ddc-on-m1-macs/</link><description>&lt;p&gt;One lazy evening in November 2020, I watched how Tim Cook announced a &lt;strong&gt;fanless&lt;/strong&gt; MacBook Air with a CPU faster than the latest 16 inch MacBook, while my work-provided 15 inch 2019 MacBook Pro was slowly frying my lap and annoying my wife with its constant fan noise.&lt;/p&gt;
&lt;p&gt;I had to get my hands on that machine. I also had the excuse that users of my app couldn&amp;rsquo;t control their monitor brightness anymore, so I could justify the expense easily in my head.&lt;/p&gt;
&lt;p&gt;So I got it! With long delays and convoluted delivery schemes because living in a country like Romania means incredibly high prices on everything Apple.&lt;/p&gt;
&lt;p&gt;This already starts to sound like those happy stories about seeing how awesome M1 is, but it’s far from that.&lt;/p&gt;
&lt;p&gt;This is a story about how getting an M1 made me quit my job, bang my head against numerous walls to figure out monitor support for it and turn an open source app into something that I can really live off without needing a &amp;ldquo;real job&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="adjusting-monitor-brightness-on-intel-macs"&gt;Adjusting monitor brightness on Intel Macs&lt;/h2&gt;
&lt;p&gt;I develop an app called &lt;a href="https://lunar.fyi" rel="noopener" target="_blank"&gt;Lunar&lt;/a&gt; that can adjust the real brightness, contrast and volume of monitors by sending &lt;a href="https://en.wikipedia.org/wiki/Display_Data_Channel" rel="noopener" target="_blank"&gt;DDC&lt;/a&gt; commands through the Mac GPU.&lt;/p&gt;














&lt;a href="https://alinpanaitiu.comimages/lunar-screenshot.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;screenshot of the Lunar app Display Settings&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="screenshot of the Lunar app Display Settings" src="https://alinpanaitiu.comimages/lunar-screenshot.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;

&lt;h4 id="useful-links"&gt;Useful links&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ddcutil.com/bibliography/#i2c" rel="noopener" target="_blank"&gt;DDC and I²C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://boichat.ch/nicolas/ddcci/specs.html" rel="noopener" target="_blank"&gt;DDC/CI Specifications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Intel Macs this worked really well because macOS had some private APIs to find the framebuffer of a monitor, send data to it through I²C, and best of all, someone has already done the hard part in figuring this out in this &lt;a href="https://github.com/kfix/ddcctl" rel="noopener" target="_blank"&gt;ddcctl utility&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;M1 Macs came with a different kernel, very similar to the iOS one. The previous APIs weren’t working anymore on the M1 GPU, the &lt;code&gt;IOFramebuffer&lt;/code&gt; was now an &lt;code&gt;IOMobileFramebuffer&lt;/code&gt; and the &lt;code&gt;IOI2C*&lt;/code&gt; functions weren’t doing anything.&lt;/p&gt;
&lt;p&gt;All of a sudden, I was getting countless emails, Twitter DMs and GitHub issues about how Lunar doesn’t work anymore on macOS Big Sur &lt;em&gt;(most M1 users were thinking the OS upgrade was causing this, disregarding the fact that they’re now using hardware and firmware that was never before seen on the Mac)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This was also a reality check for me. Without analytics, I had no idea that Lunar had so many active users!&lt;/p&gt;
&lt;h5 id="constructing-the-ddc-request"&gt;Constructing the DDC request&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define BRIGHTNESS_CONTROL_ID 0x10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;span class="n"&gt;UInt8&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 75% brightness
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOI2CRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;bzero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commFlags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x6E&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendTransactionType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kIOI2CSimpleTransactionType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;UInt8&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vm_address_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x51&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x84&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BRIGHTNESS_CONTROL_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x6E&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replyTransactionType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kIOI2CNoTransactionType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replyBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h5 id="sending-the-data-through-ic"&gt;Sending the data through I²C&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;io_service_t&lt;/span&gt; &lt;span class="n"&gt;framebuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;CGSServiceForDisplayNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;framebuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;io_service_t&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOFBCopyI2CInterfaceForBus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;framebuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;KERN_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOI2CConnectRef&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOI2CInterfaceOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kNilOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;KERN_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;IOI2CSendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kNilOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;IOI2CInterfaceClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kNilOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOObjectRelease&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr /&gt;
&lt;h2 id="hands-on-with-the-m1"&gt;Hands-on with the M1&lt;/h2&gt;
&lt;p&gt;It was the last day of November. Winter was already coming. Days were cold and less than 10km away from my place you could take a walk through snowy forests.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;snowy forests in Răcădău (Braşov, Romania)&lt;/em&gt;&lt;br /&gt;














&lt;a href="https://alinpanaitiu.comimages/snowy-forests.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;snowy forests in Brasov, Romania&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="snowy forests in Brasov, Romania" src="https://alinpanaitiu.comimages/snowy-forests.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;But I was fortunate, as I had my trusty 2019 MacBook Pro to keep my hands warm while I was cranking code that will be obsolete in less than 6 months on my day job.&lt;/p&gt;
&lt;p&gt;Just as the day turned into evening, the delivery guy called me about a laptop: &lt;strong&gt;the custom configured M1 MacBook Pro that costed as much as 7 junior developer monthly salaries has arrived!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After charging the laptop to 100%, I started the installation of my enormous &lt;a href="https://thoughtbot.com/blog/brewfile-a-gemfile-but-for-homebrew" rel="noopener" target="_blank"&gt;Brewfile&lt;/a&gt; and left it on battery as an experiment. Meanwhile I kept working on the 2019 MacBook because my day job was also a night job when deadlines got tight.&lt;/p&gt;
&lt;p&gt;Before I went to sleep, I wanted to test Lunar just to get an idea of what happens on M1. I launched it through Rosetta and the app window showed up as expected, every UI interaction worked normally but DDC was unresponsive. The monitor wasn’t being controlled in any way. I just hoped this was an easy fix and headed to bed.&lt;/p&gt;
&lt;h2 id="workarounds"&gt;Workarounds&lt;/h2&gt;
&lt;p&gt;So it turns out the I/O structure is very different on M1 (more similar to iPhones and iPads than to previous Macs). There&amp;rsquo;s no &lt;code&gt;IOFramebuffer&lt;/code&gt; that we can call &lt;code&gt;IOFBCopyI2CInterfaceForBus&lt;/code&gt; on. There&amp;rsquo;s now an &lt;code&gt;IOMobileFramebuffer&lt;/code&gt; in its place that has no equivalent function for getting an I²C bus from it.&lt;/p&gt;
&lt;p&gt;After days of sifting through the I/O Registry trying to find a way to send I²C data to the monitor, I gave up and tried to find a workaround.&lt;/p&gt;
&lt;p&gt;I realized I couldn’t work without Lunar being functional. I went back to doing the ritual I had to do in the first days I got my monitor and had no idea about DDC:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every evening I notice eye fatigue and a mild headache because the monitor is blinding me with its incredibly bright LED backlight &lt;em&gt;(might also be that I&amp;rsquo;m reaching the 10th hour of working but who counts)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Go through nested menus with an annoying joystick to lower the brightness and contrast&lt;/li&gt;
&lt;li&gt;Repeat about 3-5 times until I get too tired to do any more work&lt;/li&gt;
&lt;li&gt;Wake up in the morning&lt;/li&gt;
&lt;li&gt;Make coffee&lt;/li&gt;
&lt;li&gt;Get anxious about how much work there is to be done to meet the deadline&lt;/li&gt;
&lt;li&gt;Try to get back to work but now I can&amp;rsquo;t see a thing on the monitor because it has the brightness and contrast set to almost 0 from the night before&lt;/li&gt;
&lt;li&gt;Go through the same annoying menus to increase the brightness and contrast&lt;/li&gt;
&lt;li&gt;Repeat throughout the day&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="gamma-tables"&gt;Gamma Tables&lt;/h3&gt;
&lt;p&gt;One specific comment was becoming prevalent among Lunar users:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;QuickShade works for me on M1. Why can’t Lunar work?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://apps.apple.com/ro/app/quickshade/id931571202?mt=12" rel="noopener" target="_blank"&gt;QuickShade&lt;/a&gt; uses a black overlay with adjustable opacity to lower the image brightness. It can work on any Mac because it doesn’t depend on some private APIs to change the brightness of the monitor.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;it also makes colors look more washed out in low brightness&lt;/em&gt;&lt;br /&gt;


&lt;video loop="loop" poster="https://alinpanaitiu.comimages/quickshade-comparison.webp" style="width: 100%; margin: 10px auto; border-radius: 8px;"&gt;
  &lt;source src="/video/quickshade-comparison-small.mp4" type="video/mp4; codecs=avc1" /&gt;
  &lt;source src="/video/quickshade-comparison-small.webm" type="video/webm; codecs=vp9" /&gt;
&lt;/video&gt;
&lt;/p&gt;
&lt;p&gt;Actually, unlike Lunar, QuickShade doesn’t change the monitor brightness at all.&lt;/p&gt;
&lt;p&gt;QuickShade simulates a lower brightness by darkening the image using a fullscreen click-through black window that changes its opacity based on the brightness slider. The &lt;strong&gt;LED backlight&lt;/strong&gt; of the monitor and the brightness value in its &lt;strong&gt;OSD&lt;/strong&gt; stay the same.&lt;/p&gt;
&lt;p&gt;This is by no means a bad critique of QuickShade. It is a simple utility that does its job very well. Some people don&amp;rsquo;t even notice the difference between an overlay and real brightness adjustments that much so QuickShade might be a better choice for them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;LED monitor basic structure&lt;/em&gt;&lt;br /&gt;














&lt;a href="https://alinpanaitiu.comimages/led-panel-structure.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;LED panel structure&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="LED panel structure" src="https://alinpanaitiu.comimages/led-panel-structure.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I thought, that isn’t what Lunar set out to do, simulating brightness that is. But at the same time, a lot of users depend on this app and if it could at least do that, people will be just a bit happier.&lt;/p&gt;
&lt;p&gt;So I started researching how the brightness of an image is perceived by the human eye, and read way too much content about the Gamma factor.&lt;br /&gt;
Here’s a very good article about the subject: &lt;a href="http://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/" rel="noopener" target="_blank"&gt;What every coder should know about Gamma&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I noticed that macOS has a very simple way to control the Gamma parameters so I said &lt;em&gt;why not?&lt;/em&gt;. Let&amp;rsquo;s try to implement brightness and contrast approximation using Gamma table:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;minGamma&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gamma&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;brightnessPercent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;fromLow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromHigh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;toLow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toHigh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;contrast&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;powf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contrastPercent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;fromLow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromHigh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;toLow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toHigh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;CGSetDisplayTransferByFormula&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;displayID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;minGamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// red gamma&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;minGamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// green gamma&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;    &lt;span class="n"&gt;minGamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gamma&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;contrast&lt;/span&gt;   &lt;span class="c1"&gt;// blue gamma&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Of course this needed weeks of refactoring because the app was not designed to support multiple ways of setting brightness &lt;em&gt;(as it usually happens in every single-person hacked up project)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And there were so many unexpected issues, like, why does it take more than 5 seconds to apply the gamma values?? ლ(╹◡╹ლ)&lt;/p&gt;
&lt;p&gt;It seems that the gamma changes become visible only on the next redraw of the screen. And since I was using the builtin display of the MacBook to write the code and the monitor was just for observing brightness changes, it only updated when I became too impatient and moved my cursor to the monitor in anger.&lt;/p&gt;
&lt;p&gt;Now how do I force a screen redraw to make the gamma change apply instantly? &lt;em&gt;(and maybe even transition smoothly between brightness values)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Just draw something on the screen ¯\_(ツ)_/¯&lt;/p&gt;
&lt;p&gt;I chose to draw a (mostly hidden) blinking yellow dot when a gamma transition happens, to force screen redraw.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id="the-raspberry-pi-idea"&gt;The Raspberry Pi idea&lt;/h3&gt;
&lt;p&gt;Now I was prepared to release a new version of Lunar with the Gamma approximation thing as a fallback for M1. But as it happens, one specific user sends me an email of how he managed to &lt;strong&gt;change the brightness&lt;/strong&gt; of his monitor &lt;strong&gt;from a Raspberry Pi connected to the HDMI input&lt;/strong&gt;, while the active input was still set to the MacBook’s USB-C.&lt;/p&gt;
&lt;p&gt;I have already explored this idea as I have numerous Pis laying around, but I couldn’t get it working at all. I started writing a condescending reply of how I already tried this and how it will never work and he probably just has a monitor that happens to support this and won’t apply for other users.&lt;/p&gt;
&lt;p&gt;But then… I realized what I was doing and started pressing &lt;em&gt;backspace backspace backspace…&lt;/em&gt; and all the while I was remembering how the best features of Lunar were in fact ideas sent by users and I should stop thinking that I know better.&lt;/p&gt;
&lt;p&gt;Instead, I started asking questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what OS was the Pi running?&lt;/li&gt;
&lt;li&gt;was there a specific boot configuration he was using?&lt;/li&gt;
&lt;li&gt;what software was he using to send DDC requests?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I probably asked the right questions because the reply was exactly what I needed to get this working right away.&lt;br /&gt;
After 30 minutes of downloading the latest Raspberry Pi OS with full desktop environment, flashing it, updating to a beta firmware version, and setting the right values in &lt;code&gt;/boot/config.txt&lt;/code&gt;, the Pi was able to send DDC requests using &lt;a href="https://github.com/rockowitz/ddcutil" rel="noopener" target="_blank"&gt;ddcutil&lt;/a&gt; while the monitor was rendering the MacBook desktop.&lt;/p&gt;
&lt;p&gt;I couldn’t let this slip away, so I started implementing a network based DDC control for the next version of Lunar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a server would run on the Pi and listen for &lt;code&gt;/:monitor/:control/:value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the server will advertise itself on the network using mDNS so that Lunar won’t have to scan the whole LAN every x seconds&lt;/li&gt;
&lt;li&gt;Lunar will send the brightness, contrast, volume and input values to the server using simple HTTP requests&lt;/li&gt;
&lt;li&gt;the server will call &lt;a href="https://github.com/rockowitz/ddcutil" rel="noopener" target="_blank"&gt;ddcutil&lt;/a&gt; with the parameters from the request&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I established from the start that the local network latency and HTTP overhead was negligible compared to the DDC delay so I didn’t have to look into more complex solutions like USB serial, websockets or MQTT.&lt;/p&gt;
&lt;h3 id="the-dreaded-day-job"&gt;The dreaded day job&lt;/h3&gt;
&lt;p&gt;Even though &lt;strong&gt;side project&lt;/strong&gt; is such a praised thing in the software development world, I can’t recommend doing such a thing.&lt;/p&gt;
&lt;p&gt;It was very hard doing all of the above in the little time I had after working 9+ hours fullstack at an US company &lt;em&gt;(that was also going through 2 different transitions: bought by a conglomerate, merging with another startup)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I owe a lot to my manager there, I wouldn’t have had the strength to do what followed without his encouraging advice and always present genuine smile.&lt;/p&gt;
&lt;p&gt;One day, he told me that he finally started working on a bugfix for a long-standing problem in our gRPC gateway. He confessed that it was the first time in two months he found the time to write some code &lt;em&gt;(the thing he actually enjoyed)&lt;/em&gt;, between all the meetings and video calls. 10 minutes later, another non-US based team needed his help and his coding time got filled with scheduled meetings yet again. That is the life of a technical manager.&lt;/p&gt;
&lt;p&gt;Now that Lunar was working on M1 and the &lt;a href="https://www.buymeacoffee.com/alin23" rel="noopener" target="_blank"&gt;Buy me a Coffee&lt;/a&gt; donations showed that people find value in this app, I thought it was time to stop doing what I don’t like &lt;em&gt;(working for companies on products that I never use)&lt;/em&gt; and start doing what I always seemed to like &lt;em&gt;(creating software which I also enjoy using, and share it with others)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So on April 1st I finished my contract at the US company, and started implementing a licensing system in Lunar.&lt;/p&gt;
&lt;p&gt;Sounds simple right? Well it’s far from that. Preparing a product for the purpose of selling it, took me two whole months. And more energy than I put in 4 months of experimenting with Gamma and DDC on M1 &lt;em&gt;(yeah, that was the fun part)&lt;/em&gt;. This part of the journey is the hardest, and not fun at all.&lt;/p&gt;
&lt;p&gt;My take from this is: &lt;strong&gt;if you’re at the start of selling your work, choose a payment or licensing solution that requires the least amount of work, no matter how expensive it may seem at first.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I went with &lt;a href="https://paddle.com/" rel="noopener" target="_blank"&gt;Paddle&lt;/a&gt; for Lunar because of the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;they have a macOS SDK, which meant
&lt;ul&gt;
&lt;li&gt;no need to implement checkout views, license activation dialogs, guarding access to the app etc.&lt;/li&gt;
&lt;li&gt;the users can buy the license directly from the app, no need to redirect them to a website&lt;/li&gt;
&lt;li&gt;anti-cracking techniques are already better implemented than what I could have done in a few months time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;they act as a reseller which means they handle all the taxes
&lt;ul&gt;
&lt;li&gt;I just get a pay-check on the 1st of each month&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even with that, I made the mistake to choose a licensing system that wasn’t natively supported by Paddle and that made me dig into a 2-month rabbit hole of licensing servers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I wanted the system that Sketch has: a one-time payment for an unlimited license, that also includes 1 year of free updates.&lt;/em&gt;&lt;/p&gt;














&lt;a href="https://alinpanaitiu.comimages/sketch-licensing.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;Sketch app licensing&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="Sketch app licensing" src="https://alinpanaitiu.comimages/sketch-licensing.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;

&lt;hr /&gt;
&lt;h2 id="ic-on-m1"&gt;I²C on M1&lt;/h2&gt;
&lt;p&gt;After a successful launch in June, most users were happy with the Gamma solution, and some even tried the Raspberry Pi method: &lt;a href="https://talk.macpowerusers.com/t/lunar-app-a-way-for-m1-macs-to-control-3rd-party-monitors-brightness-and-contrast/23677?u=alinp32" rel="noopener" target="_blank"&gt;Lunar.app - a way for M1 Macs to control 3rd Party Monitor&amp;rsquo;s Brightness and Contrast - Hardware - MPU Talk&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Although one user was still persistent in looking for I²C support. Twice he tried to bring to my attention a way to use I²C on M1 and the second time he finally succeeded.&lt;/p&gt;














&lt;a href="https://alinpanaitiu.comimages/ioavservice-github-comment.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;@zhuowei Github comment&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="@zhuowei Github comment" src="https://alinpanaitiu.comimages/ioavservice-github-comment.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/alin23/Lunar/issues/210#issuecomment-871047164" rel="noopener" target="_blank"&gt;GIthub comment with I²C functions on M1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/zhuowei/status/1374870618894647297?s=21" rel="noopener" target="_blank"&gt;Twitter reply from 3 months before, that I totally missed because I don’t use Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;His GitHub comment on the M1 issue for Lunar sparked a new hope among users and some of the more technical users started experimenting with the&lt;br /&gt;
&lt;code&gt;IOAVServiceReadI2C&lt;/code&gt; and &lt;code&gt;IOAVServiceWriteI2C&lt;/code&gt; functions.&lt;/p&gt;
&lt;p&gt;Because of my shallow understanding of the DDC specification at the time, I couldn&amp;rsquo;t get a working proof of concept in the first few tries.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I didn&amp;rsquo;t know exactly what chipAddress and dataAddress were for&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOAVServiceWriteI2C&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;  &lt;span class="n"&gt;IOAVServiceRef&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;chipAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;dataAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;  &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inputBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;inputBufferSize&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I knew from my experiments with ESP32 and Arduino boards that I²C is in fact a serial &lt;strong&gt;bus&lt;/strong&gt;, which means you can communicate with more than one device from the same 2 pins of the main device by chaining the secondary devices.&lt;/p&gt;
&lt;p&gt;That possibility brings the requirement of a &lt;strong&gt;chip address&lt;/strong&gt; which the main device should send over the wire to reach a specific device from that chain.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;chaining sensor boards through I²C&lt;/em&gt;&lt;br /&gt;














&lt;a href="https://alinpanaitiu.comimages/i2c-chain.png"&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;I²C chain of sensors&lt;/figcaption&gt;
    
      
        
          
          
          
            &lt;source type="image/avif" /&gt;
          
          
            &lt;source type="image/webp" /&gt;
          
          &lt;source /&gt;
        
      
      &lt;img alt="I²C chain of sensors" src="https://alinpanaitiu.comimages/i2c-chain.png" /&gt;
    
  &lt;/figure&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;In the DDC standard, the secondary device is the monitor and has the chip address &lt;code&gt;0x37&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The EDID chip is located at the address &lt;code&gt;0x50&lt;/code&gt; which is what we have in &lt;a href="https://gist.github.com/zhuowei/bf37b110152aeb61383a3885cb465cc5" rel="noopener" target="_blank"&gt;@zhuowei&amp;rsquo;s EDID reading example&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOAVServiceReadI2C&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i2cBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2cBytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;But then what is the &lt;code&gt;dataAddress&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;No idea, but thankfully someone &lt;a href="http://boichat.ch/nicolas/ddcci/method.html" rel="noopener" target="_blank"&gt;reverse engineered the communication protocol&lt;/a&gt; and found this to always be &lt;code&gt;0x51&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After some trial and error, user &lt;a href="https://github.com/alin23/Lunar/issues/210#issuecomment-872601088" rel="noopener" target="_blank"&gt;@tao-j&lt;/a&gt; discovered the above details and managed to finally change the brightness from his M1 MacBook.&lt;/p&gt;
&lt;h4 id="the-mac-mini-problem"&gt;The Mac Mini problem&lt;/h4&gt;
&lt;p&gt;Unfortunately, this was just the beginning as the Mac Mini supports more than one monitor and it&amp;rsquo;s not clear what monitor we&amp;rsquo;re controlling when calling &lt;code&gt;IOAVServiceCreate()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I found a way to get each monitor&amp;rsquo;s specific AVService by iterating the I/O Kit registry tree and looking for the &lt;code&gt;AppleCLCD2&lt;/code&gt; class. To know which &lt;code&gt;AppleCLCD2&lt;/code&gt; belonged to what monitor, I had to cross reference identification data returned by &lt;code&gt;CoreDisplay_DisplayCreateInfoDictionary&lt;/code&gt; with the attributes of the registry node.&lt;/p&gt;
&lt;p&gt;With that convoluted logic, I managed to get DDC working on Mac Mini as well, but only on the Thunderbolt 3 port. The HDMI port still doesn&amp;rsquo;t work for DDC, &lt;a href="https://github.com/alin23/Lunar/issues/125" rel="noopener" target="_blank"&gt;and no one knows why&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In the end, DDC on M1 was finally working in the same way it worked on Intel Macs!&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="sending-ic-data-on-m1"&gt;Sending I²C data on M1&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define BRIGHTNESS 0x10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;span class="n"&gt;IOAVServiceRef&lt;/span&gt; &lt;span class="n"&gt;avService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IOAVServiceCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kCFAllocatorDefault&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOReturn&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;UInt8&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;UInt8&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x84&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BRIGHTNESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x6E&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="mh"&gt;0x51&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IOAVServiceWriteI2C&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Some quirks are still bothering the users of Lunar though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;some monitors lose signal or flicker in and out of connecting when the M1 GPU sends any I²C data&lt;/li&gt;
&lt;li&gt;the HDMI port of the Mac Mini doesn’t send any kind of data over I²C&lt;/li&gt;
&lt;li&gt;reading the currently visible input through DDC never works on M1&lt;/li&gt;
&lt;li&gt;reading brightness, contrast or volume from the monitor fails about 30% of the time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the moment these seem to be hardware problems and I’ll just have to keep responding to the early morning support emails no matter how obvious I make it that these are unsolvable.&lt;/p&gt;
&lt;h3 id="technical-stuff"&gt;Technical stuff&lt;/h3&gt;
&lt;p&gt;I left these at the end because the details may bore most people but they might still be useful for a very small number of readers.&lt;/p&gt;
&lt;h4 id="how-is-an-app-able-to-change-the-hardware-brightness-of-a-monitor"&gt;How is an app able to change the hardware brightness of a monitor?&lt;/h4&gt;
&lt;p&gt;All monitors have a powerful &lt;strong&gt;microprocessor&lt;/strong&gt; inside that has the purpose of receiving video data over multiple types of connections, and creating images from that data through the incredibly tiny crystals of the panel.&lt;/p&gt;
&lt;p&gt;That same microprocessor &lt;strong&gt;dims or brightens&lt;/strong&gt; a panel of &lt;strong&gt;LEDs&lt;/strong&gt; behind that panel of crystals based on the &lt;strong&gt;Brightness value&lt;/strong&gt; that you can change in the &lt;strong&gt;monitor settings&lt;/strong&gt; using its &lt;strong&gt;physical buttons&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Because the devices that connect to the monitor need to know stuff about its capabilities (e.g. resolution, color profile etc), there needs to be a &lt;strong&gt;language known by both the computer and the monitor&lt;/strong&gt; so that they can communicate.&lt;/p&gt;
&lt;p&gt;That language is called a &lt;strong&gt;communication protocol&lt;/strong&gt;. The protocol implemented inside the processors of most monitors is called &lt;strong&gt;Display Data Channel&lt;/strong&gt; or &lt;strong&gt;DDC&lt;/strong&gt; for short.&lt;/p&gt;
&lt;p&gt;To allow for different monitor properties to be read or changed from the host device, VESA created the &lt;a href="https://en.wikipedia.org/wiki/Monitor_Control_Command_Set" rel="noopener" target="_blank"&gt;Monitor Control Command Set&lt;/a&gt; (or &lt;strong&gt;MCCS&lt;/strong&gt; for short) which works over DDC.&lt;/p&gt;
&lt;p&gt;MCCS is what allows Lunar and other apps to change the monitor brightness, contrast, volume, input etc.&lt;/p&gt;
&lt;h4 id="then-what-the-heck-is-ic"&gt;Then what the heck is I²C?&lt;/h4&gt;
&lt;p&gt;I²C is a Wire protocol, which basically specifies how to translate electrical pulses sent over two wires into bits of information.&lt;/p&gt;
&lt;p&gt;DDC specifies which sequences of bits are valid, while I²C specifies how a device like the monitor microprocessor can get those bits through wires inside the HDMI, DisplayPort, USB-C etc. cables.&lt;/p&gt;
&lt;h4 id="why-does-macos-block-me-from-changing-volume-on-the-monitor-while-windows-allows-that"&gt;Why does macOS block me from changing volume on the monitor, while Windows allows that?&lt;/h4&gt;
&lt;p&gt;


&lt;img alt="volume lock macOS OSD" src="https://alinpanaitiu.comimages/volume-lock.png" /&gt;&lt;/p&gt;
&lt;p&gt;macOS doesn’t block volume, it simply doesn&amp;rsquo;t implement any way for you to change the volume of a monitor.&lt;/p&gt;
&lt;p&gt;Windows actually only changes the software volume, so if your monitor real volume is at 50%, windows can only lower that in software so you&amp;rsquo;ll hear anything between 0% and 50%. If you check the monitor OSD, you&amp;rsquo;ll see that the volume value of the monitor always stays at 50%.&lt;/p&gt;
&lt;p&gt;Now macOS could probably do that as well, so that at least we&amp;rsquo;d have a way to lower the volume. But it doesn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;So if you want to change the real volume of the monitor on Mac, Lunar can do that.&lt;/p&gt;</description><author>Copper • A blog about conductive layers</author><pubDate>Fri, 16 Jul 2021 18:39:37 GMT</pubDate><guid isPermaLink="true">https://alinpanaitiu.com/blog/journey-to-ddc-on-m1-macs/</guid></item><item><title>Shane's pneumatic RAM chip published in &amp;lt;i&amp;gt;PLOS ONE&amp;lt;/i&amp;gt;</title><link>http://groverlab.org/news/2021/07/16/pneumatic-ram-chip.html</link><description>&lt;p&gt;&lt;img alt="" src="/assets/pneumatic-ram-chip.png" /&gt;&lt;/p&gt;

&lt;p&gt;Shane Hoang’s paper “A pneumatic random-access memory for controlling soft robots” was &lt;a href="https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0254524"&gt;published in &lt;em&gt;PLOS ONE&lt;/em&gt;&lt;/a&gt;.  Shane developed a “pneumatic RAM chip,” a pneumatic logic circuit capable of controlling pneumatic soft robots.  By dramatically reducing the amount of electronic hardware required to control soft robots, the pneumatic RAM can accelerate the spread of soft robotic technologies to a wide range of important application areas.&lt;/p&gt;

&lt;p&gt;Thanks to our coauthors Konstantinos Karydis and Philip Brisk for their help with this work!&lt;/p&gt;</description><author>Grover Lab</author><pubDate>Fri, 16 Jul 2021 12:00:00 GMT</pubDate><guid isPermaLink="true">http://groverlab.org/news/2021/07/16/pneumatic-ram-chip.html</guid></item><item><title>Yet Another Reason to Use DuckDuckGo</title><link>https://nicolaiarocci.com/yet-another-reason-to-use-duckduckgo/</link><description>&lt;p&gt;I couldn&amp;rsquo;t recall a &lt;a href="https://github.com/tmux/tmux"&gt;tmux&lt;/a&gt; command, so I quickly reached for my trusted default
search engine DuckDuckGo. I typed &amp;ldquo;tmux cheat sheet&amp;rdquo; because, well, once
I found an excellent one which I wanted to summon again. To my surprise, the
&lt;a href="https://duckduckgo.com/?t=ffab&amp;amp;q=tmux+cheat+sheet&amp;amp;atb=v224-1&amp;amp;ia=cheatsheet"&gt;search result&lt;/a&gt; included an in-page cheat sheet—a good one too.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DuckDuckGo provides an in-page tmux cheat sheet" src="https://nicolaiarocci.com/images/duckduckgo-tmux.png" /&gt;&lt;/p&gt;
&lt;p&gt;It isn&amp;rsquo;t the first time that DuckDuckGo surprises me like that. Need a new
GUID? &lt;a href="https://duckduckgo.com/?q=new+guid&amp;amp;t=ffab&amp;amp;atb=v224-1&amp;amp;ia=answer"&gt;Search for it&lt;/a&gt;.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 16 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/yet-another-reason-to-use-duckduckgo/</guid></item><item><title>Reverse engineering a pen</title><link>https://dorotac.eu/posts/REing a pen/</link><description>&lt;h1&gt;Reverse engineering a pen&lt;/h1&gt;
&lt;p&gt;Like a lot of people, I have a favorite pen. I ran across it randomly, but I fell in love with it instantly. It glides across paper with barely any pressure, leaves strong, confident, and uniform streaks, and it's built like a tank to boot. But, unlike most pens that are worthy of appreciation, this pen is really many pens – it's disposable.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A red ink Uni-Ball Eye pen" src="pen.png" /&gt;&lt;/p&gt;
&lt;p&gt;While the ink is bright an saturated, and there's a selection of colours, the Uni-Ball Eye is not meant to be refilled. To someone trying to reduce my waste production, using up one perfectly good pen every year is completely unacceptable. Knowing tricks back from the bad days of ink jet printers, I bought a vial of fountain pen ink, a syringe, and I set out to refill the carcass of my most recent pen.&lt;/p&gt;
&lt;p&gt;Knowing that this is a blog post about refilling a pen, you might already guess that it wasn't as easy as it sounds.&lt;/p&gt;
&lt;h2&gt;Sesame open&lt;/h2&gt;
&lt;p&gt;Before refilling, I destroyed a previous used up carcass to find best ways to inject liquid into the pen. It took me a while with the scissors – the outer shell is &lt;em&gt;really&lt;/em&gt; thick – but eventually I found out that the back end has a thinner wall, and a needle did the job.&lt;/p&gt;
&lt;p&gt;I also dissected the funny comb-like part near the tip that always looked like it filled with a little bit of ink. It's actually a second ink vessel, whose role I discover while refilling. Surprisingly, this vessel is not connected to the tip. There's a wick going from the tip straight to the big compartment, &lt;em&gt;inside&lt;/em&gt; the tubular, comb-like second vessel.&lt;/p&gt;
&lt;p&gt;Here's a diagram:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A diagram of a pen. From end to tip, a useless? hole leading to an empty container. A weak wall separating it from the ink container, which contains a thin wick leading all the way to the tip. The ink container ends in a permeable wall, which leads to a container defined by a &amp;quot;comb&amp;quot; structure. The &amp;quot;comb&amp;quot; is a pipe with the wick inside, and many flat rings on its outside. The rings are not reaching the container wall. Two guessed passages connect the end of this container with two &amp;quot;nostrils&amp;quot; near the tip." src="diagram%20annotated.svg" /&gt;&lt;/p&gt;
&lt;p&gt;(The walls are much thicker in reality.)&lt;/p&gt;
&lt;h2&gt;Shooting it up&lt;/h2&gt;
&lt;p&gt;For the first refill, I simply poked a hole in the back of the pen, placed the conjoined pen and syringe horizontally, and squirted in a few drops of the liquid. The wick became flooded with ink, and I could write again! But… all the ink slowly seeped into the comb container, leaving the wick dry after a minute. Even worse, two "nostrils" near the tip readily released ink as long as the comb was full. What's going on?&lt;/p&gt;
&lt;p&gt;&lt;img alt="Comb compartment" src="comb.png" /&gt; This comb container on a new pen is already partially filled. The red streaks are ink.&lt;/p&gt;
&lt;p&gt;An obvious culprit is gravity, but the ink wasn't flowing all at once, so there must be more to it.&lt;/p&gt;
&lt;p&gt;The comb shape drew my attention. Being composed mostly of walls, it gives the liquid plenty of tight spaces to which it can stick. Did the manufacturer intend to make use of the capillary effect? It would help liquid flow into the comb.&lt;/p&gt;
&lt;p&gt;There was the open question of whether the pen was originally airtight, but adding pressure into considerations would make the problem too complicated, so I followed the next obvious lead.&lt;/p&gt;
&lt;h2&gt;Capillary effect&lt;/h2&gt;
&lt;p&gt;If I could prevent the comb compartment from filling up, ink would stay in the main vessel, and I would write indefinitely. The only difference, apart from the opened end, between my refill and the original, was the ink used. Perhaps the original ink was tuned specifically for this pen design. Hoping to alter the liquid to be less eager to climb up the comb, I tried a couple mixes with glycerin and water.&lt;/p&gt;
&lt;h2&gt;Nosebleed&lt;/h2&gt;
&lt;p&gt;I tried those tests also with the syringe hole plugged. That's when something unusual happened: the nostrils produced bubbles of air, like a kid who dropped an ice cream. Clearly, that was not just gravity's fault: air doesn't flow out under its own weight.&lt;/p&gt;
&lt;p&gt;The story of fountain pens leaking inside airplanes came to my mind. Perhaps this pen had similar considerations?&lt;/p&gt;
&lt;h2&gt;Air pressure&lt;/h2&gt;
&lt;p&gt;I had reasons to believe that yes, they do. First off, why would a pen need "nostrils" connecting to the ink compartment, if not to equalize pressure? Coupled with the comb compartment, this made sense. When the pen is in an upright position, ink flows towards the tip, and there's a pocket of air near the butt. That air would expand when surrounding air pressure drops, and push out ink from the main vessel. The comb area would contain the excess, and the comb structure itself would prevent it from flowing out.&lt;/p&gt;
&lt;p&gt;Every time I tried to use the pen in the morning, it cried me tears of ink, flourishing my handwriting in a special way. Pressure is the hint. Mornings are cold. My hands are warm. Gases expand with temperature. Air pushes out ink. It all makes sense!&lt;/p&gt;
&lt;h2&gt;Flawed methodology&lt;/h2&gt;
&lt;p&gt;Sadly, my tests so far were all flawed: I never allowed the comb to dry up so far, and if the pressure theory was right, then the pen could work correctly only when the comb had plenty of empty space. I came up with a new procedure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use the pen until all the ink was released – via the tip or the nostrils&lt;/li&gt;
&lt;li&gt;carefully plug the injection point.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If my theory was right, then the following would happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the liquid would have enough surface tension to not flood the comb after a few hours&lt;/li&gt;
&lt;li&gt;the wick would stay submerged in ink, and never dry up&lt;/li&gt;
&lt;li&gt;writing on cold mornings would make the comb fill up a little&lt;/li&gt;
&lt;li&gt;but it would not be enough to fill up the comb and cause spills.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Experiments&lt;/h2&gt;
&lt;p&gt;&lt;img alt="A red ink blot" src="blot.png" /&gt; My daily companion when researching this article: the ink blot.&lt;/p&gt;
&lt;p&gt;The plan didn't work out flawlessly, and every attempt was about as fun as watching ink dry. That's because using up all ink took up ages, and the ink blots added a special flourish to my handwriting. I made several failures to plug the injection point: it turns out that just sticking a needle in there is not enough, and ink flows out the back. What's good enough is glueing it up using some plastic foil and liquid latex.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The end of a pen plugged with a piece of paper, all soaked with red." src="plug.png" /&gt;&lt;/p&gt;
&lt;p&gt;While that works to keep the ink out the back end, it turns out that the ink still flows out the front! That invalidates my theory. Or does it?&lt;/p&gt;
&lt;p&gt;I didn't want to seal the pen irreversibly in case that I needed to adjust something, so the end was probably letting air seep in a little. I left the pen overnight in different positions, and, after watching the ink in the comb change, concluded that gravity was responsible for the failure this time.&lt;/p&gt;
&lt;p&gt;Without any better ideas, I went ahead and mixed in some glycerin into the ink vessel, which seems to have stopped the outflow. Success!&lt;/p&gt;
&lt;h2&gt;Inkblots&lt;/h2&gt;
&lt;p&gt;That last experiment proved the minimal changes needed for the Uni-Ball Eye to be reuseable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use ink with about 10% glycerin&lt;/li&gt;
&lt;li&gt;refill from the back while the comb compartment is not full&lt;/li&gt;
&lt;li&gt;plug in the refill hole afterwards.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The comb compartment is probably a way to avoid leaks due to pressure.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Two pens next to similar squiggles of red ink. The bottom is broader and brighter than the top one." src="comparison.png" /&gt; The top pen in this picture is the refilled one. In reality, both inks look darker.&lt;/p&gt;
&lt;p&gt;The ink I used is not as intense as the original one, and it doesn't flow so easily, but on the other hand, it's a bit thinner, and still flows decently enough. It can work as a cheap alternative to a new pen, which normally costs about 3 EUR per piece.&lt;/p&gt;
&lt;h2&gt;Lessons learned&lt;/h2&gt;
&lt;p&gt;Turns out that there's a good deal of thought and complexity embedded in every day objects. Physical objects are take more effort to understand than as software. You need to control your environment, retrying the experiment can take a lot of time, and you may get yourself dirty in the middle. None of this will be a surprise to an experimental scientist, of course.&lt;/p&gt;
&lt;p&gt;It's still a joy to explore, and the results are going to be a fair bit easier to explain to a random human – the outcome is satisfyingly concrete!&lt;/p&gt;</description><author>dcz's posts</author><pubDate>Thu, 15 Jul 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://dorotac.eu/posts/REing a pen/</guid></item><item><title>Proust's Madeleine Was Originally a Slice of Toast</title><link>https://nicolaiarocci.com/prousts-madeleine-was-originally-a-slice-of-toast/</link><description>&lt;p&gt;A long-sought first draft of Marcel Proust&amp;rsquo;s &amp;lsquo;In Search of Lost Time&amp;rsquo; surfaced
a few years ago. Its fascinating story and intriguing news are revealed in
a Tablet article titled &lt;a href="https://www.tabletmag.com/sections/arts-letters/articles/proust-madeleine-antisemitism-jewishness"&gt;Proust&amp;rsquo;s Madeleine Was Originally a Slice Toast&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Being the Tablet &amp;ldquo;a daily online magazine of Jewish news, ideas, and culture&amp;rdquo;,
it makes sense that a good part of the article focuses on Proust&amp;rsquo;s ambivalence
about his Jewishness. Still, there are many other interesting tidbits to be
learned. On the novel itself and its development, on some relevant characters
and their real-world counterparts, and Proust himself.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Thu, 15 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/prousts-madeleine-was-originally-a-slice-of-toast/</guid></item><item><title>The Inevitable Consequence of a Numerical Id</title><link>https://backendhance.com/blog/2021/numerical-id-consequence/</link><description>&lt;p&gt;In many (JPA) applications numerical ids are chosen for the &lt;a href="https://en.wikipedia.org/wiki/Surrogate_key" rel="noopener noreferrer" target="_blank"&gt;surrogate key&lt;i class="bx bx-link-external"&gt;&lt;/i&gt;&lt;/a&gt;
of the entities. But how do we make sure that they are not used twice? In a scenario where our application needs to scale horizontally we need a solution for that. Most developers come to the conclusion that the database should take care of that. But this is a fragile solution and in this article I want to discuss it.&lt;/p&gt;</description><author>Backendhance</author><pubDate>Thu, 15 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://backendhance.com/blog/2021/numerical-id-consequence/</guid></item><item><title>About Me</title><link>https://justingarrison.com/about/</link><description>Some other information about me.</description><author>Justin Garrison's Homepage</author><pubDate>Thu, 15 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/about/</guid></item><item><title>Writing an efficient object previewer for JavaScript</title><link>http://notes.eatonphil.com/writing-an-efficient-javascript-object-previewer.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-07-15-writing-an-efficient-javascript-object-previewer.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Thu, 15 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/writing-an-efficient-javascript-object-previewer.html</guid></item><item><title>Will .NET 6 Minimal APIs turn heads?</title><link>https://nicolaiarocci.com/will-.net-6-minimal-apis-turn-heads/</link><description>&lt;p&gt;I am pretty excited about the [Minimal APIs][3] feature that is coming with
.NET 6. Three lines of code will be enough to build a fully functional REST
microservice&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var app = WebApplication.Create(args);
app.MapGet(&amp;quot;/&amp;quot;, () =&amp;gt; &amp;quot;Hello World!&amp;quot;);
await app.RunAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&amp;rsquo;re a seasoned ASP.NET MVC/WebApi developer, the snippet caught your
attention because, pre-.NET 6, achieving the same result will have you messing
with a lot of extra cruft&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;. I suspect, however, that this feature is not
primarily targeted at existing .NET developers. Developers new and old looking
at .NET for the first time, or those like me returning after a long break;
these are, I think, the designated audience.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 14 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/will-.net-6-minimal-apis-turn-heads/</guid></item><item><title>The one thing you need to make your marketing work</title><link>https://yasha.solutions/posts/the-1-thing-youneedtomakeyourmarketingwork/</link><description>Patience. Grit. Perseverance. Borderline Stubbornness.
The number one reason people failed, especially when it comes to content marketing and organic reach, is a lack of patience and will try one more time.
Statistics will be your first enemy The beauty of digital marketing is that it brings you statistics, and you know pretty much what&amp;rsquo;s happening with your content, which at the beginning, is not much.
It is very tempting to let it go, drop the pace slowly, miss a few milestones until your plans to post daily on your blog are gone, or you let your twitter engagement running at an all time low.</description><author>Yasha Solutions</author><pubDate>Wed, 14 Jul 2021 05:38:02 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/the-1-thing-youneedtomakeyourmarketingwork/</guid></item><item><title>Don’t Put Eggs Under Your Tomatoes, if you have Raccoons</title><link>https://duncanlock.net/blog/2021/07/13/dont-put-eggs-under-your-tomatoes-if-you-have-raccoons/</link><description>&lt;p&gt;They will dig up your garden to get&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve seen various recommendations for breaking an egg into the bottom of the hole when you plant out tomatoes. Apparently, this works and increases&amp;nbsp;yield:&lt;/p&gt;
&lt;div class="video-block"&gt;&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s just a form of fertilizer, so this isn&amp;#8217;t too surprising. I&amp;#8217;ve tried this in previous years and it seems to work. Sadly when I did it again this year, the raccoons decided they wanted the eggs and dug up the garden to get&amp;nbsp;them.&lt;/p&gt;
&lt;figure class="image-block"&gt;&lt;img alt="Picture of the garden bed the morning after. Shows some plants dug up, with soild scattered around, while others are untouched." src="https://duncanlock.net/images/posts/dont-put-eggs-under-your-tomatoes-if-you-have-raccoons/IMG_20210629_094011-smaller.webp" /&gt;
&lt;figcaption&gt;Figure 1. Part of the aftermath of the raccoon raid. They were actually &lt;em&gt;fairly&lt;/em&gt; targeted, digging up all the tomato plants that had eggs underneath, with limited collateral damage.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;So, if you have raccoons, don&amp;#8217;t bury eggs&amp;#8201;&amp;#8211;&amp;#8201;or anything else edible &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; smelly&amp;#8201;&amp;#8211;&amp;#8201;in the&amp;nbsp;garden.&lt;/p&gt;</description><author>duncan­lock­.net</author><pubDate>Wed, 14 Jul 2021 04:06:13 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/07/13/dont-put-eggs-under-your-tomatoes-if-you-have-raccoons/</guid></item><item><title>On Link Farms</title><link>https://www.marginalia.nu/log/04-link-farms/</link><description>&lt;p&gt;I&amp;rsquo;m in the midst of rebuilding the index of my search engine to allow for better search results, and I&amp;rsquo;ve yet again found need to revisit how I handle link farms. It&amp;rsquo;s an ongoing arms race between search engines and link farmers to adjust (and circumvent) the detection algorithms. Detection and mitigation of link farms is something I&amp;rsquo;ve found I need to modify very frequently, as they are constantly evolving to look more like real websites.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 14 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/04-link-farms/</guid></item><item><title>Scrum Board using Notion</title><link>https://june.kim/scrum-board/</link><author>june.kim</author><pubDate>Wed, 14 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/scrum-board/</guid></item><item><title>All That Is Gold</title><link>https://ho.dges.online/words/commonplace/all-that-is-gold-by-j.r.r-tolkien/</link><description>&lt;p&gt;All that is gold does not glitter,&lt;br /&gt;
Not all those who wander are lost;&lt;br /&gt;
The old that is strong does not wither,&lt;br /&gt;
Deep roots are not reached by the frost.&lt;br /&gt;
From the ashes a fire shall be woken,&lt;br /&gt;
A light from the shadows shall spring;&lt;br /&gt;
Renewed shall be blade that was broken,&lt;br /&gt;
The crownless again shall be king.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;J.R.R. Tolkien&lt;/em&gt;&lt;/p&gt;</description><author>ho.dges.online</author><pubDate>Wed, 14 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/all-that-is-gold-by-j.r.r-tolkien/</guid></item><item><title>And The Days Are Not Full Enough</title><link>https://ho.dges.online/words/commonplace/and-the-days-are-not-full-enough-by-ezra-pound/</link><description>&lt;p&gt;And the days are not full enough&lt;br /&gt;
And the nights are not full enough&lt;br /&gt;
And life slips by like a field mouse&lt;br /&gt;
Not shaking the grass.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ezra Pound&lt;/em&gt;&lt;/p&gt;</description><author>ho.dges.online</author><pubDate>Wed, 14 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/and-the-days-are-not-full-enough-by-ezra-pound/</guid></item><item><title>Book notes - OSTEP (part 1)</title><link>https://river.me/blog/book-notes-ostep-1/</link><description>My notes on &amp;ldquo;Operating Systems: Three Easy Pieces&amp;rdquo; - part 1, Virtualization</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 14 Jul 2021 01:50:55 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-notes-ostep-1/</guid></item><item><title>Three ways to check and fix ultrametric phylogenies</title><link>https://jonathanchang.org/blog/three-ways-to-check-and-fix-ultrametric-phylogenies/</link><description>&lt;p&gt;A recent user &lt;a href="https://github.com/jonchang/tact/issues/230"&gt;bug report&lt;/a&gt; in my software &lt;a href="https://github.com/jonchang/tact"&gt;TACT&lt;/a&gt; led me to look into how phylogenetic software varies in the way they determine whether a given phylogenetic tree is ultrametric (where the root-to-tip distance is equal among all tips). If you infer an ultrametric phylogeny using something like BEAST or treePL, your supposedly ultrametric tree can still cause problems for other tools by virtue of &lt;em&gt;not being ultrametric enough&lt;/em&gt;.&lt;/p&gt;
  &lt;p&gt;This ultrametric purity test was previously a problem during the great BAMM controversy of 2017 (“BAMMghazi”), when the whales phylogeny used as an example in BAMM &lt;a href="https://github.com/macroevolution/bammtools/issues/45"&gt;suddenly stopped being ultrametric&lt;/a&gt; as measured by the R function &lt;code&gt;ape::is.ultrametric&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;How then, do tools differ in the way that they check for ultrametricity?&lt;/p&gt;
  &lt;p&gt;&lt;img alt="Illustration of a construction worker wielding a ‘stop’ sign in front of a breaching humpback whale. The label  is shown, describing the situation by means of analogy." src="/uploads/2021/ultrametric/preview.jpg" /&gt;&lt;/p&gt;
  &lt;h2&gt;Method 1: variance&lt;/h2&gt;
  &lt;p&gt;This is &lt;a href="https://github.com/FePhyFoFum/phyx/blob/f6559150a4cf7f78f2ec6f4edab232ce34b99fda/src/tree_utils.cpp#L486-L492"&gt;used in phyx&lt;/a&gt;, and was &lt;a href="https://github.com/cran/ape/blob/650e3dfdde5de98680ea96e0e3bc6e30f52a51b2/R/is.ultrametric.R#L35"&gt;used in ape prior to version 4.0&lt;/a&gt;.&lt;/p&gt;
  &lt;p&gt;Load the whales tree into R and compute the root-to-tip distances for all tips:&lt;/p&gt;
  &lt;div class="language-r highlighter-rouge"&gt;
    &lt;div class="highlight"&gt;
      &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read.tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/macroevolution/bamm/ab1b69be13e9841d9e103170d0f61e4324f78676/examples/diversification/whales/whaletree.tre"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;root_node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dist.nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root_node&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Compute the variance using those distances:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] 6.519647e-13&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The default tolerance in ape is the square root of R’s &lt;a href="https://en.wikipedia.org/wiki/Machine_epsilon"&gt;machine epsilon&lt;/a&gt;, defined as the smallest positive floating-point number &lt;em&gt;x&lt;/em&gt; such that 1 + &lt;em&gt;x&lt;/em&gt; ≠ 1. This can vary from computer to computer, but on my laptop, this value is:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;.Machine&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;double.eps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] 1.490116e-08&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The whales tree is ultrametric using the variance method, since 1e-8 is much larger than 1e-13.&lt;/p&gt;
&lt;h2&gt;Method 2: relative difference&lt;/h2&gt;
&lt;p&gt;In ape 4.0, the &lt;code&gt;is.ultrametric&lt;/code&gt; method &lt;a href="https://github.com/cran/ape/blob/fa2a72e2814d26112792cf4676f6737abb7f7e0d/R/is.ultrametric.R#L35-L37"&gt;was changed&lt;/a&gt; to use the relative difference of the minimum and maximum root-to-tip distances. I’ll get into why this was changed, but first, let’s look at how to calculate this value:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;min_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;max_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max_tip&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] 1.115516e-07&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Immediately we can see why the whales tree stopped being ultrametric in ape 4.0, as this relative difference is larger than the default tolerance.&lt;/p&gt;
&lt;p&gt;Why was this change made? It wasn’t merely to cause a lot of problems for everyone; instead, the answer is &lt;a href="https://cran.r-project.org/web/packages/ape/ape.pdf#Rfn.is.ultrametric.1"&gt;in the documentation&lt;/a&gt;, which tersely states:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;The default criterion is invariant to linear changes of the branch lengths.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What does this look like in practice? Let’s first scale the root-to-tip distance by multiplying everything by 1000:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;scaled_root_to_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now compare the variance and relative difference:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scaled_root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] 6.519647e-07&lt;/span&gt;&lt;span class="w"&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="n"&gt;min_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scaled_root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;max_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scaled_root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_tip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max_tip&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] 1.115516e-07&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Uh oh! Our whales are no longer ultrametric per the variance statistic, even though &lt;em&gt;none of the branch lengths have changed&lt;/em&gt; relative to each other. You may not think that your phylogenies should be stretching like a taffy pull, but there are probably valid use cases for phylogenies like this.&lt;/p&gt;
&lt;p&gt;This is the method that TACT currently uses to determine ultrametricity.&lt;/p&gt;
&lt;h2&gt;Method 3: node ages&lt;/h2&gt;
&lt;p&gt;The previous two methods all relied on comparing some aspect of root-to-tip distances. Here, we’ll actually compare the distances to the tips of &lt;em&gt;all nodes&lt;/em&gt;. This is the method &lt;a href="https://github.com/jeetsukumaran/DendroPy/blob/29fd294bf05d890ebf6a8d576c501e471db27ca1/src/dendropy/datamodel/treemodel.py#L5649-L5655"&gt;used in DendroPy&lt;/a&gt; and the original impetus for this investigation. I’ll reimplement enough of this method in R to illustrate this technique.&lt;/p&gt;
&lt;p&gt;First, reorder the tree for postorder traversal, and set up some convenience variables.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postorder"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;[,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# parent node&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;[,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# child node&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;EL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge.length&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Also set up an &lt;code&gt;ages&lt;/code&gt; variable that will hold internal calculations for how old a node should be.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;ages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;Nnode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next, start iterating…&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;seq_along&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If we haven’t already stored an age for the parent node, go ahead and compute that now from the (left)&lt;sup class="footnote-ref"&gt;&lt;a href="#fn1" id="fnref1"&gt;1&lt;/a&gt;&lt;/sup&gt; child node  and the current edge length.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EL&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Otherwise, retrieve the stored age for the parent node, and re-compute what the age should be based on the (right)&lt;sup class="footnote-ref"&gt;&lt;a href="#fn1" id="fnref1"&gt;1&lt;/a&gt;&lt;/sup&gt; child node.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;recorded_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;new_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EL&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now test whether those ages differ. I could actually use either the variance or the relative difference method, but here I’ll just check for absolute difference.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recorded_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"node %i age %.6f != %.6f\n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;recorded_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_age&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="c1"&gt;## node 154 age 3.291163 != 3.291164&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 153 age 4.570892 != 4.570893&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 151 age 5.263495 != 5.263494&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 150 age 6.975185 != 6.975185&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 146 age 3.048675 != 3.048675&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 145 age 4.452720 != 4.452720&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 143 age 6.047030 != 6.047031&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 142 age 8.209050 != 8.209050&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 135 age 5.616381 != 5.616380&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 133 age 14.061554 != 14.061555&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 132 age 17.939426 != 17.939427&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 130 age 24.698214 != 24.698213&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 127 age 5.096384 != 5.096384&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 125 age 5.796587 != 5.796586&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 124 age 6.375631 != 6.375632&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 121 age 7.176680 != 7.176680&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 120 age 7.677424 != 7.677424&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 116 age 13.042869 != 13.042870&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 114 age 11.028304 != 11.028304&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 113 age 14.540283 != 14.540283&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 112 age 15.669702 != 15.669702&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 108 age 31.621529 != 31.621528&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 104 age 22.044391 != 22.044391&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 103 age 33.799003 != 33.799004&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 100 age 11.382958 != 11.382958&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 93 age 26.063016 != 26.063016&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## node 90 age 8.816019 != 8.816019&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Many internal nodes have ages that differ depending on whether you use the left or right child node to compute the age. This metric allows you to pinpoint exactly where in your phylogeny precision issues could be causing ultrametricity issues. I can imagine this being quite useful when you are grafting subtrees onto a backbone phylogeny and trying to figure out if you did the math on your branch lengths correctly.&lt;/p&gt;
&lt;h2&gt;Fix 1: extending the tips&lt;/h2&gt;
&lt;p&gt;Now that we’re aware of the differences between different ultrametricity checks, let’s look at ways to correct for phylogenies that aren’t quite there.&lt;/p&gt;
&lt;p&gt;One possibility is to simply extend the tips of the tree until the root-to-tip distances are completely equal. This is implemented in R as &lt;code&gt;BioGeoBEARS::extend_tips_to_ultrametricize&lt;/code&gt;&lt;sup class="footnote-ref"&gt;&lt;a href="#fn2" id="fnref2"&gt;2&lt;/a&gt;&lt;/sup&gt; and &lt;code&gt;phytools::force.ultrametric(method = &amp;quot;extend&amp;quot;)&lt;/code&gt;.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn3" id="fnref3"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;First, set up a copy of the whales tree and compute some important values, including the difference from each root-to-tip distance to their maximum:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;age_difference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root_to_tip&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next, grab the edges from the edge matrix that correspond to the tips. Note that the edges in &lt;code&gt;$edge.label&lt;/code&gt; correspond to the &lt;em&gt;row numbers&lt;/em&gt; in &lt;code&gt;$edge&lt;/code&gt;, not the values in those rows! This is tricky and confusing. We can also assume that the tip edges appear in ascending order, from 1 to &lt;em&gt;N&lt;/em&gt;.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tip_edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;[,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ntip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, extend the tips outwards and confirm that the new tree is ultrametric:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge.length&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tip_edges&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge.length&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tip_edges&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age_difference&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;is.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] TRUE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We can write a simple function in R that compares two phylogenies with identical topologies but differing branch lengths.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;diff_edge_lengths&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phy2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;diffs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phy2&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge.length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phy&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge.length&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Use the &lt;code&gt;sign&lt;/code&gt; function, which returns -1, 0, or 1 when the input is negative, zero, or positive, respectively. Then assign each of those values to a color (or lack thereof). This is &lt;a href="https://colorbrewer2.org/#type=diverging&amp;amp;scheme=PiYG&amp;amp;n=11"&gt;ColorBrewer palette PiYG&lt;/a&gt;.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diffs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#7fbc41"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#de77ae"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;NA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Plot the tree and report the results. The plot needs a bit of adjustment since the defaults of &lt;code&gt;ape::plot.phylo&lt;/code&gt; are a bit aggravating.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;show.tip.label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;no.margin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;edgelabels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%i longer branches, %i shorter branches"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diffs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diffs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This function places squares on branches that have changed in length, with red meaning a shorter branch and green meaning a longer branch. As expected, nearly all of the terminal branches have been extended to enforce ultrametricity.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;diff_edge_lengths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] "85 longer branches, 0 shorter branches"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Phylogeny of whales, forced to be ultrametric via the “extend tips” method. 85 terminal branches have increased in length." src="/uploads/2021/ultrametric/extend.png" /&gt;&lt;/p&gt;
&lt;h2&gt;Fix 2: non-negative least squares&lt;/h2&gt;
&lt;p&gt;This is the default approach used in the R function &lt;code&gt;phytools::force.ultrametric&lt;/code&gt; and the topic of a &lt;a href="http://blog.phytools.org/2016/08/fixing-ultrametric-tree-whose-edges-are.html"&gt;phytools blog post&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;This will give you the edge lengths that result in the distances between taxa with minimum sum of squared differences from the distances implied by your input tree, under the criterion that the resulting tree is ultrametric.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tre_nnls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phangorn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;nnls.tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cophenetic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rooted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;is.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre_nnls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] TRUE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Since it’s trying to minimize differences among the pairwise tip distance matrix, you’d expect many branches to be adjusted. Plotting the differences show that this is indeed the case:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;diff_edge_lengths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_nnls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] "86 longer branches, 85 shorter branches"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Phylogeny of whales, forced to be ultrametric via the “non-negative least squares” method. 171 branches have changed length, with about half becoming longer and half becoming shorter, in different parts of the tree." src="/uploads/2021/ultrametric/nnls.png" /&gt;&lt;/p&gt;
&lt;h2&gt;Fix 3: node adjustment&lt;/h2&gt;
&lt;p&gt;This is the approach optionally used in DendroPy,&lt;sup class="footnote-ref"&gt;&lt;a href="#fn4" id="fnref4"&gt;4&lt;/a&gt;&lt;/sup&gt; and is how TACT fixes ultrametricity issues if asked. Whenever a node’s age differs between its left and right children, correct one of the branch lengths so the node’s age will be calculated the same regardless of whether you’re using the left descendants or the right descendants.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn1" id="fnref1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Change the prior loop that checks node ages to additionally adjust the branch length:&lt;/p&gt;
&lt;div class="language-diff highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt; for (ii in seq_along(EL)) {
     if (ages[e1[ii]] == 0) {
         ages[e1[ii]] &amp;lt;- ages[e2[ii]] + EL[ii]
     } else {
         recorded_age &amp;lt;- ages[e1[ii]]
         new_age &amp;lt;- ages[e2[ii]] + EL[ii]
         if (recorded_age != new_age) {
             cat(sprintf("node %i age %.6f != %.6f\n", e1[ii], recorded_age, new_age))
&lt;span class="gi"&gt;+            EL[ii] &amp;lt;- recorded_age - ages[e2[ii]]
&lt;/span&gt;         }
     }
 }
&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then, update the branch lengths in the phylogeny itself and confirm that it’s ultrametric.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;edge.length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;is.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] TRUE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Plotting the differences shows that this method actually changes the fewest number of branches.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;diff_edge_lengths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tre_node_adjust&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] "13 longer branches, 14 shorter branches"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Phylogeny of whales, forced to be ultrametric via the “node adjustment” method. 27 branches have changed length, with about half becoming longer and half becoming shorter, in different parts of the tree." src="/uploads/2021/ultrametric/adjust_nodes.png" /&gt;&lt;/p&gt;
&lt;h2&gt;Issues with large phylogenies&lt;/h2&gt;
&lt;p&gt;Another issue identified in the &lt;a href="https://github.com/jonchang/tact/issues/230#issuecomment-871628907"&gt;bug report&lt;/a&gt; was the inability to use &lt;code&gt;phytools::force.ultrametric&lt;/code&gt; on large phylogenies. This is indeed the case when testing a random tree with 50,000 tips.&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rcoal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## Phylogenetic tree with 50000 tips and 49999 internal nodes.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## Tip labels:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;##   t11339, t29898, t18919, t6336, t34524, t1665, ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## Rooted; includes branch lengths.&lt;/span&gt;&lt;span class="w"&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="n"&gt;is.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] TRUE&lt;/span&gt;&lt;span class="w"&gt;&lt;/p&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="n"&gt;phytools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;force.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;“nnls”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## Error in double(nm * nm) : vector size cannot be NA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## In addition: Warning message:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## In nm * nm : NAs produced by integer overflow&lt;/span&gt;&lt;span class="w"&gt;&lt;/p&gt;
&lt;p&gt;&lt;/span&gt;&lt;span class="n"&gt;phytools&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;force.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;“extend”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## Error: vector memory exhausted (limit reached?)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;method = &amp;quot;extend&amp;quot;&lt;/code&gt;, its current implementation calls &lt;code&gt;diag(vcv(tree))&lt;/code&gt;, which requires creating an &lt;em&gt;N&lt;/em&gt; by &lt;em&gt;N&lt;/em&gt; matrix as a temporary value, where N is the number of tips. With 50,000 tips this implies a vector of length 2.5 billion, which exceeds R’s vector limit of 2.1 billion. This function could be optimized to avoid this storage requirement. With &lt;code&gt;method = &amp;quot;nnls&amp;quot;&lt;/code&gt; creating this &lt;em&gt;N&lt;/em&gt; by &lt;em&gt;N&lt;/em&gt; matrix may be unavoidable, so for large phylogenies consider using the tip extension or the node adjustment methods instead.&lt;/p&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;None of this really matters anyway, except in the special case of really big phylogenies, and even then not so much. If you’re certain your phylogeny is ultrametric, use any of these methods and you’ll be able to get your tree to be so precisely ultrametric that even the strictest tools can’t complain.&lt;/p&gt;
&lt;p&gt;I’m serious! Look at how well the “extend tips” method works:&lt;/p&gt;
&lt;div class="language-r highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;is.ultrametric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tre_extend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tolerance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;## [1] TRUE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This means there are literally no differences in root-to-tip distances even to a precision of 16 digits. &lt;a href="https://www.jpl.nasa.gov/edu/news/2016/3/16/how-many-decimals-of-pi-do-we-really-need/"&gt;This is enough to leave the solar system&lt;/a&gt;, so it’s probably good enough for your phylogeny, which likely has more interesting sources of error beyond numerical precision.&lt;/p&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="/uploads/2021/ultrametric/ultrametric.R"&gt;ultrametric.R&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="/uploads/2021/ultrametric/ultrametric.md"&gt;ultrametric.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn1"&gt;
      &lt;p&gt;Left and right are arbitrary here and named only to help the reader distinguish between the two. &lt;a class="footnote-backref" href="#fnref1"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn2"&gt;
      &lt;p&gt;BioGeoBEARS also has a function to average the heights of tip nodes. &lt;a class="footnote-backref" href="#fnref2"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn3"&gt;
      &lt;p&gt;You can also read my &lt;a href="https://github.com/macroevolution/bammtools/issues/45#issuecomment-289850728"&gt;embarrassing, non-working original attempt&lt;/a&gt;. I’ve gotten better at R, I promise. &lt;a class="footnote-backref" href="#fnref3"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn4"&gt;
      &lt;p&gt;Note that DendroPy lets you pick either the maximum or the minimum implied ages; my implementation here just picks the first one it sees, so is sensitive to rearrangements such as from a ladderized tree. &lt;a class="footnote-backref" href="#fnref4"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/section&gt;</description><author>Jonathan Chang</author><pubDate>Tue, 13 Jul 2021 04:55:00 GMT</pubDate><guid isPermaLink="true">https://jonathanchang.org/blog/three-ways-to-check-and-fix-ultrametric-phylogenies/</guid></item><item><title>Przeprowadzka do Polski</title><link>https://b.agilob.net/random/on-life/polska-przygoda/transport/</link><description>Zaczelo sie od znalezienia firmy, ktore przewiezie nasze rzeczy do Polski. Przeswietlilismy kilka firm, sprawdzilismy ich opinie o nich, status prawny w UK i Polsce, by ominac &amp;ldquo;firmy-krzak&amp;rdquo;, ktora wywiezie nasze rzeczy na Bialorus.
Znalezlismy firme &amp;ldquo;Przeprowadzki Raffa&amp;rdquo;. Wszystko spoko, zarejestrowana w HMCR, ma jakis (nie pamietam jaki) status prawny w Polsce. Firma zarejestrowana kilka lat temu wraz z domena. Kilka pozytywnych opinii. Umowilismy sie na wyjadz. Kierowcy przyjechali z 5h opoznieniem z Londynu.</description><author>Random Notes by agilob</author><pubDate>Tue, 13 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/random/on-life/polska-przygoda/transport/</guid></item><item><title>HASS saving my meat</title><link>https://b.agilob.net/random/hass-saving-my-meat/</link><description>That one day my fridge decided to give up but blasting me with hot air right when I wanted to put food in there. Thermostat failed in a really funny way. It refused to turn the cooler on, but decided to get hot itself. I spent half a day trying to find a replacement part, for such a simple thing as GE thermostat I found no help. I couldn&amp;rsquo;t ID it, repair shops or ebay were to no help.</description><author>Random Notes by agilob</author><pubDate>Tue, 13 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/random/hass-saving-my-meat/</guid></item><item><title>To Hell With Facebook</title><link>https://www.damninteresting.com/to-hell-with-facebook/</link><description>&lt;p&gt;&lt;img alt="" height="227" src="//www.damninteresting.com/wp-content/uploads/2021/07/001-340x227.jpg" style="width: 340px;" title="" width="340" /&gt;The earliest known version of the idiom &amp;#8220;the straw that broke the camel&amp;#8217;s back&amp;#8221; was written by the English philosopher Thomas Hobbes of Malmesbury in 1677, though it was concerned with horses and feathers:&lt;/p&gt;
			&lt;blockquote&gt;
				&lt;p&gt;&amp;#8220;The last Dictate of the Judgement, concerning the Good or Bad, that may follow on any Action, is not properly the &lt;i&gt;whole Cause&lt;/i&gt;, but the last Part of it, and yet may be said to produce the Effect &lt;i&gt;necessarily&lt;/i&gt;, in such Manner as the last Feather may be said to break a Horses Back, when there were so many laid on before as there want but that one to do it.&amp;#8221;&lt;/p&gt;
			&lt;/blockquote&gt;
					
&lt;p&gt;For the past few years, we at Damn Interesting have been hearing from scores of long-time fans who were under the mistaken impression that we had ceased all operations years ago. These fans are typically delighted to hear that a) we are still writing and podcasting; and b) there is a wealth of new content since they last visited. When we ask them what caused the assumption of our demise, they invariably cite the fact that our posts disappeared from their Facebook news feeds.&lt;/p&gt;
&lt;p&gt;This trend roughly coincides with Facebook&amp;#8217;s introduction of &amp;#8220;boosting&amp;#8221; for pages; in this new model, according to the stats we can see, Facebook stopped showing our posts to approximately 94% of our followers, demanding a fee to &amp;#8220;boost&amp;#8221; each post into an ad, which would make it visible to more of our audience. We lost contact with tens of thousands of fans practically overnight. We don&amp;#8217;t mind paying for a service if it is valuable, but we absolutely don&amp;#8217;t want to reach our audience by buying ad space on Facebook. Yuck. But no other option is given to reach the many people who previously followed our posts, and who presumably want to continue to do so.&lt;/p&gt;
&lt;p&gt;We established our Facebook page in 2008 as the fledgling social media site was gaining in currency, and we continued to maintain our page on the side, mostly as an afterthought. In the intervening years Facebook evolved from a dubious curiosity into a megacorporation that is firmly in the service of bad ideas. In a move that feels long overdue, we at Damn Interesting are abandoning all interactions and connections with Facebook.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.damninteresting.com/to-hell-with-facebook/"&gt;Continue reading &amp;#x25b6;&lt;/a&gt;&lt;/p&gt;</description><author>Damn Interesting</author><pubDate>Mon, 12 Jul 2021 23:17:58 GMT</pubDate><guid isPermaLink="true">https://www.damninteresting.com/to-hell-with-facebook/</guid></item><item><title>How I Became a Super Hero</title><link>https://olshansky.info/movie/how_i_became_a_super_hero/</link><description>Olshansky's review of How I Became a Super Hero</description><author>🦉 olshansky 🦁</author><pubDate>Mon, 12 Jul 2021 13:36:04 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/how_i_became_a_super_hero/</guid></item><item><title>Creating debug reports with Hugo inline shortcodes</title><link>https://utf9k.net/blog/hugo-debug-reports/</link><description>I teach you some tricks on introspecting your Hugo builds</description><author>utf9k</author><pubDate>Mon, 12 Jul 2021 08:50:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/hugo-debug-reports/</guid></item><item><title>The "3-5 traits of a great manager" question</title><link>http://raynicholus.com/posts/posts/great-managers/</link><description>&lt;p&gt;Back when I had a LinkedIn account, I’d see a familiar question populate my inbox from recruiters looking to fill engineering manager positions. In fact, I saw this question so often that I finally decided to answer it…sort of.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What would you say are 3-5 traits every great manager must possess?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My cop-out answer is: I don’t know yet. I’m still learning and am still a relatively young manager, making mistakes, adjusting, repeat.&lt;/p&gt;

&lt;p&gt;But maybe that’s a path towards articulating important traits of a manager. Perhaps a good manager must not be rigid. A good manager should be able to benefit from the continuous feedback loop that naturally exists, and make adjustments in response.&lt;/p&gt;

&lt;p&gt;Though getting candid feedback from your direct reports is…tricky. So perhaps a good manager must be able to make his or her direct reports feel comfortable sharing this feedback. This may not always be possible, so a good manager must be able to “read between the lines”, “take the temperature of the room”, “look for patterns” and various other detective tasks.&lt;/p&gt;

&lt;p&gt;But being a good manager probably isn’t as simple as feeding inputs into an algorithm to produce an output of a progressively better manager. So, there must be something else. I suppose a good manager must be confident, have solid intuition. She will have to trust her gut from time to time. Having the confidence and experience to do that also contributes to the success of a manager.&lt;/p&gt;

&lt;p&gt;Additionally, a good manager should be able to earn the respect of her direct reports. She should understand the work her direct reports must complete on a daily basis, and &lt;em&gt;must&lt;/em&gt; be able to complete this work herself. She must be able to serve as a model for her direct reports. She leads by example in every possible way, all the time. Surely, this is not overly common, but then neither are great managers, are they?&lt;/p&gt;

&lt;p&gt;And finally, as I think I have demonstrated throughout this message: a good (maybe even a great) manager is self-aware. He might not always be correct. His perspective is not necessarily “gospel”. He realizes that there is more than one answer. When he guides his direct reports, he does so by “planting seeds” instead of transforming them into clones of himself. He is suggestive and thought-provoking instead of demanding.&lt;/p&gt;

&lt;p&gt;And I do hope this was thought provoking for you.&lt;/p&gt;</description><author>Train of Thought</author><pubDate>Mon, 12 Jul 2021 05:00:00 GMT</pubDate><guid isPermaLink="true">http://raynicholus.com/posts/posts/great-managers/</guid></item><item><title>Writing for Reading</title><link>https://www.marginalia.nu/log/03-writing-for-reading/</link><description>&lt;p&gt;I&amp;rsquo;m struck by how easy it is to read things on Gemini. Not just skimming, but actual reading, like you would a book. Not everything written is great, but it&amp;rsquo;s usually worth reading all the same. Venturing out into the land of contemporary HTML is different, and it&amp;rsquo;s not a subtle difference.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve written before about the plague of inline links on Wikipedia, and this is largely a continuation of that discourse looking at other design elements.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Mon, 12 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/03-writing-for-reading/</guid></item><item><title>Why do I have high blood pressure?</title><link>https://vjeran.crnjak.xyz/posts/why-do-i-have-high-blood-pressure/index.html</link><description>&lt;div class="info"&gt;
    &lt;span class="icon-calendar"&gt; 2021-07-12
    &lt;a class="license" href="http://creativecommons.org/licenses/by/4.0/" title="Text license"&gt;
      &lt;span class="icon-cc"&gt;
      CC-BY
    &lt;/a&gt;
    
&lt;/div&gt;

&lt;p&gt;I was recently measuring my blood pressure after years of knowing it was high.
A few years ago I started a more active lifestyle but, other than my resting
heart rate lowering, nothing changed. Blood markers are still fine and blood
pressure is around 140/80 mmHg.&lt;/p&gt;
&lt;p&gt;It should be 120/80 mmHg and there’s no physical anomaly causing it to be high.
My blood vessels are nice. My heart is working well.&lt;/p&gt;
&lt;p&gt;Blood pressure might be accompanied by a headache or brainfog but the symptoms
would always be strongest when blood pressure drops. So it might be completely
unrelated. It would also be high during long periods of sitting. The only time
when I would feel completely fine is when I’d be walking 20+ kilometers per
day.&lt;/p&gt;
&lt;p&gt;I think it’s just the autonomous system trying to keep me in the state of
constant arousal. While working I experience some amount of stress. I can
compensate by humming or listening to music and then continue working without
interruptions, pauses or fatigue. At the same time I feel as if my body is very
relaxed while sitting and I experience no discomfort. When I lay down my body
decides to fall asleep. It is not I that decides that. I feel like the same
thing can happen while sitting. For some reason, during work, I never feel
sleepy. Either through conditioning, or by the nature of things, I am very
aroused and concentrated.&lt;/p&gt;
&lt;p&gt;It is like my body is forcing itself to overcome the relaxed state by arousal
and stress. It increases the blood pressure and heartbeat. Because of prolonged
periods of sitting it never decides to reduce it back to 120/80 mmHg unless I
lay down (laying down also reduced or eliminated headaches and brainfogs).&lt;/p&gt;
&lt;p&gt;It looks like sitting combined with the need to think for work sends mixed
signals that result in long-term high blood pressure.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How can this be tested?&lt;/em&gt; I guess I could try not to think hard while sitting
and just be relaxed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How could this be countered?&lt;/em&gt; I could increase my blood pressure through
highly intense physical effort. This could send new signals that might force
the autonomous nervous system to stabilize to a blood pressure that is close to
the perfect baseline.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer&lt;/em&gt;: This is fiction. I have no idea what I’m talking about. This is
just me imagining things and the writings presented have no basis outside of my
head.&lt;/p&gt;</description><author>Post feed - Chào bảo</author><pubDate>Mon, 12 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://vjeran.crnjak.xyz/posts/why-do-i-have-high-blood-pressure/index.html</guid></item><item><title>How to Replace All Instances of a Substring in JavaScript</title><link>https://www.dannyguo.com/blog/how-to-replace-all-instances-of-a-substring-in-javascript</link><author>Danny Guo</author><pubDate>Mon, 12 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.dannyguo.com/blog/how-to-replace-all-instances-of-a-substring-in-javascript</guid></item><item><title>Mob Programming with 65 Million of Your Peers</title><link>https://www.danstroot.com/posts/2021-07-12-github-copilot</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/copilot.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;You are contributing value, whether you like it not, when you make your code public. Training machine learning models on publicly available data is considered fair use across the machine learning community. The models gain insight and accuracy from the public collective intelligence. The only way to extract value back out is to use Copilot yourself.&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-07-12-github-copilot"&gt;Mob Programming with 65 Million of Your Peers&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Mon, 12 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-07-12-github-copilot</guid></item><item><title>Rancilio Silvia Upgrade</title><link>https://smcleod.net/2021/07/rancilio-silvia-upgrade/</link><description>&lt;p&gt;Weekend upgrades to my Ranchilio Silvia v4 espresso machine.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Added a digital &lt;a href="https://www.seattlecoffeegear.com/blog/2018/10/01/whats-a-pid/"&gt;PID&lt;/a&gt; &lt;a href="https://www.jetblackespresso.com.au/shop/p/rancilio-silvia-pid-preinfusion/"&gt;kit&lt;/a&gt; for improved temperature control&lt;/li&gt;
&lt;li&gt;Replaced the boiler element&lt;/li&gt;
&lt;li&gt;Thermal insulation&lt;/li&gt;
&lt;li&gt;Basic &lt;a href="https://www.cygnett.com/products/smart-wi-fi-plug-with-power-monitoring"&gt;&amp;ldquo;smart&amp;rdquo; power control&lt;/a&gt; with Siri integration&lt;/li&gt;
&lt;li&gt;Upgrade the standard Ranchilio basket to a &lt;a href="https://www.dukescoffee.com.au/shop/vst-precision-basket-ridged/"&gt;VST 18g Ridged&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/assembled.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/siri.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/vst_18g_r.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id="rebuild-photos"&gt;Rebuild Photos&lt;/h2&gt;
&lt;p&gt;Note the damage to the boiler elements terminals, this was shorting and causing the houses RCD to trip&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/boiler_element_damage.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/boiler_out.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/element.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;p&gt;Wiring the PID controller&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/sammcj/smcleod_files/blob/master/images/silvia_upgrades_2021/pid_interface_wiring.jpeg?raw=true" /&gt;&lt;/p&gt;
&lt;p&gt;Replacing the boiler thermostat with one connected to the PID controller&lt;/p&gt;</description><author>smcleod.net</author><pubDate>Sun, 11 Jul 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://smcleod.net/2021/07/rancilio-silvia-upgrade/</guid></item><item><title>Start in the middle</title><link>https://dorotac.eu/posts/in_the_middle/</link><description>&lt;h1&gt;Start in the middle&lt;/h1&gt;
&lt;p&gt;It's a piece of advice I read years ago in the depths of the 'Net, which I can't find despite best efforts any more. It's surprising, because it's such an obvious guidance! Especially for those easily distracted, or prone to losing motivation.&lt;/p&gt;
&lt;p&gt;TL;DR: When you work on something new, jump into the thick of it. Don't let yourself be sidetracked, and attack directly what interests you.&lt;/p&gt;
&lt;h2&gt;Storybuilding&lt;/h2&gt;
&lt;p&gt;Homer put Odysseus &lt;em&gt;in media res&lt;/em&gt; as Odyssey begins: he's trying to return home after being shipwrecked. There's little backstory, or discussion about motivations. You focus on the current state, and wonder: how did he end up so? What challenges will he encounter? Can he succeed? You're hooked, and keep reading to see the questions answered.&lt;/p&gt;
&lt;p&gt;It's good news for the distracted creator, because those are the same questions you may ask yourself when you have an awesome idea for a new project. What are the challenges? Can the idea succeed? You can get hooked on your own project by cutting out the fluff, and jumping straight into it.&lt;/p&gt;
&lt;h2&gt;Focus&lt;/h2&gt;
&lt;p&gt;No one has built Skynet yet. If you are overly ambitious, chances are that you will have to build a lot of supporting infrastructure. Instead, choose the core element and stick to it. If you want to take pictures of all the graffitti in your town, don't bother with setting up a web site, just take photos. If your idea is to program a fancy reverb effect, don't bother turning it into a library.&lt;/p&gt;
&lt;p&gt;In fact, don't bother with all the surroundings. Before you write all the READMEs, set up a documentation site, and publish the library on your favourite web site, you're risking that you run out of energy to actually do the thing you set out to do.&lt;/p&gt;
&lt;h2&gt;Side tracks&lt;/h2&gt;
&lt;p&gt;Sometimes you might want to build something that is so original that no one even built the tools you could use. A spaceship out of water bottles? Perhaps needs heavy duty glue, and all the glues you found are suboptimal. Suddenly, you're spending days researching chemistry, and the spaceship project is not moving anywhere.&lt;/p&gt;
&lt;p&gt;Everything is cool if you enjoy your glue side project, but if your only motivation is to build a rocket, then perhaps you need to cut your losses and use one of the store glues. Get back to the middle: the spaceship, before you start hating the project! Once you nail the central part, either you're going to have so much motivation you'll deal with the side projects, or you will blissfully not care any more. Win-win!&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;p&gt;The article is written about general projects, but software is especially prone to it, because there's so much complexity and potential challenges. I wouldn't have finished &lt;a href="/posts/fluwid"&gt;fluw&lt;/a&gt; if I didn't take this approach. Thankfully, knowledge stays, and the more you build, the easier it will be to choose tools and minimize the time spent not in the middle of things.&lt;/p&gt;
&lt;h2&gt;Protagonist&lt;/h2&gt;
&lt;p&gt;You're the protagonist in your life, and challenge you create for yourself are stories you live. So take advice from the art of writing, start in the middle, and chances are that you're going to enjoy more what you do.&lt;/p&gt;</description><author>dcz's posts</author><pubDate>Sun, 11 Jul 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://dorotac.eu/posts/in_the_middle/</guid></item><item><title>Running python webservers on port 80 without root</title><link>https://duncanlock.net/blog/2021/07/11/running-python-webservers-on-port-80-without-root/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;If you want to have a python webserver running locally and listening on port 80&amp;#8201;&amp;#8211;&amp;#8201;or any other port &amp;lt; 1024, you can do it like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;setcap &lt;span class="s1"&gt;'cap_net_bind_service=+ep'&lt;/span&gt; /usr/bin/python3.8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This gives the &lt;code&gt;/usr/bin/python3.8&lt;/code&gt; binary the ability to bind to privileged ports without being root. Note that this only works for real files, not symlinks, so this will probably not work, as &lt;code&gt;python&lt;/code&gt; is generally a&amp;nbsp;symlink:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;setcap &lt;span class="s1"&gt;'cap_net_bind_service=+ep'&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;which python&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I wanted this so that the &lt;code&gt;invoke livereload&lt;/code&gt; thing for this blog, could take over from Nginx for local development, giving me incremental live rebuilding &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; reloading while editing. This happens to use tornado underneath, which is a Python&amp;nbsp;webserver.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_removing_capabilities"&gt;Removing&amp;nbsp;Capabilities&lt;/h2&gt;&lt;p&gt;If you want to undo this, you do&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to remove just that one capability, use &lt;span class="nt"&gt;-ep&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;setcap &lt;span class="s1"&gt;'cap_net_bind_service=-ep'&lt;/span&gt; /usr/bin/python3.8
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to see that capabilities on a file
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;getcap  /usr/bin/python3.8
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to remove all of them
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;setcap &lt;span class="nt"&gt;-r&lt;/span&gt; /usr/bin/python3.8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/413807/is-there-a-way-for-non-root-processes-to-bind-to-privileged-ports-on-linux"&gt;Is there a way for non-root processes to bind to &amp;#8220;privileged&amp;#8221; ports on&amp;nbsp;Linux?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://gist.github.com/firstdoit/6389682"&gt;Allow Node to bind to port 80 without&amp;nbsp;sudo&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Sun, 11 Jul 2021 10:24:37 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/07/11/running-python-webservers-on-port-80-without-root/</guid></item><item><title>Flat Habits 1.1 released</title><link>https://xenodium.com/flat-habits-11-released</link><description>&lt;p&gt;&lt;a href="https://flathabits.com/"&gt;Flat Habits&lt;/a&gt; 1.1 is now available on the &lt;a href="https://apps.apple.com/app/id1558358855"&gt;App Store&lt;/a&gt;. Flat Habits is a habit tracker that’s mindful of your time, data, and privacy. It's powered by &lt;a href="https://orgmode.org"&gt;org&lt;/a&gt; plain text markup, enabling you to use your &lt;a href="https://xenodium.com/frictionless-org-habits-on-ios/"&gt;favorite editor&lt;/a&gt; (Emacs, Vim, VSCode, etc.) to poke at your habit data.&lt;/p&gt;
&lt;h2&gt;What's new?&lt;/h2&gt;
&lt;p&gt;This release implements a few of features requested by users.&lt;/p&gt;
&lt;h2&gt;Multiday weekly habits&lt;/h2&gt;
&lt;p&gt;This is the chunkiest addition and most requested feature. You can now select multiple days when scheduling weekly habits.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/flat-habits-11-released/multi_day_creation.gif" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/flat-habits-11-released/multi_day_calendar.png" /&gt;&lt;/p&gt;
&lt;h2&gt;Historical management&lt;/h2&gt;
&lt;p&gt;Sometimes you forget to mark a habit done or make a mistake toggling one. Either way, you can now toggle any habit day from the calendar/streak view.&lt;/p&gt;
&lt;h3&gt;Long tap&lt;/h3&gt;
&lt;p&gt;Long tap shows you the editing option available for that day.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/flat-habits-11-released/long_tap.gif" /&gt;&lt;/p&gt;
&lt;h3&gt;Short tap&lt;/h3&gt;
&lt;p&gt;Short tap typically toggles between &amp;quot;Done&amp;quot; and &amp;quot;Not done&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/flat-habits-11-released/short_tap.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;Where's today?&lt;/h2&gt;
&lt;p&gt;A few folks rightfully asked for today's date to be highlighted in the calendar view, and so we now have a red circle.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/flat-habits-11-released/today.png" /&gt;&lt;/p&gt;
&lt;h2&gt;Improved error messages&lt;/h2&gt;
&lt;p&gt;Hopefully you don't run into issues, but if you do, I hope the app helps ya sort them out.&lt;/p&gt;
&lt;h2&gt;Bugfixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tapping on blur now dismisses habit edit dialog.&lt;/li&gt;
&lt;li&gt;Future habits now longer editable.&lt;/li&gt;
&lt;li&gt;Skipped habits no longer have a default tap action.&lt;/li&gt;
&lt;li&gt;Undoing from streak/calendar view now refreshes correctly.&lt;/li&gt;
&lt;li&gt;Undoing habit addition on iPad removes streak/calendar view.&lt;/li&gt;
&lt;/ul&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 11 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/flat-habits-11-released</guid></item><item><title>macOS: Show in Finder / Show in Emacs</title><link>https://xenodium.com/show-in-finder--show-in-emacs</link><description>&lt;p&gt;From Christian Tietze's &lt;a href="https://christiantietze.de/posts/2021/07/open-finder-window-in-dired/"&gt;Open macOS Finder Window in Emacs Dired&lt;/a&gt;, I learned about &lt;a href="https://github.com/kaz-yos/reveal-in-osx-finder"&gt;reveal-in-osx-finder&lt;/a&gt;. This is handy for the few times I want to transition from Emacs to Finder for file management. I say few times since Emacs's directory editor, &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html"&gt;dired&lt;/a&gt;, is just awesome. I've written about dired customizations &lt;a href="https://xenodium.com/showhide-emacs-dired-details-in-style/"&gt;here&lt;/a&gt; and &lt;a href="https://xenodium.com/showhide-emacs-dired-details-in-style/"&gt;here&lt;/a&gt;, but since dired is &lt;em&gt;just another buffer,&lt;/em&gt; you can apply your Emacs magic like multiple cursors to &lt;a href="https://xenodium.com/batch-renaming-with-counsel-find-dired-and-wdired/"&gt;batch rename files in an editable dired buffer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To transition from macOS Finder to Emacs, Christian offers an Emacs interactive command that fetches Finder's location and opens a dired buffer via AppleScript. On a similar note, I learned from redditor &lt;a href="https://www.reddit.com/user/pndc/"&gt;u/pndc&lt;/a&gt; that &lt;a href="https://www.reddit.com/r/emacs/comments/ohgz0s/open_macos_finder_windows_path_in_dired/h4p8a8f?utm_source=share&amp;amp;utm_medium=web2x&amp;amp;context=3"&gt;Finder's proxy icons can be dragged over to Emacs&lt;/a&gt;, which handily drops ya into a dired buffer.&lt;/p&gt;
&lt;p&gt;With these two solutions in mind, I looked into a third one to offer a context menu option in Finder to show the file in Emacs. This turned out to be fairly easy using Automator, which I've rarely used.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/show-in-finder--show-in-emacs/show_in_emacs.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I created a flow that runs a shell script to &amp;quot;Show in Emacs&amp;quot;, revealing the selected file or folder in an dired buffer. This is similar to Christian's solution, but invoked from Finder itself. The flow also uses &lt;em&gt;dired-goto-file&lt;/em&gt; which moves the point (cursor) to the file listed under dired.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/show-in-finder--show-in-emacs/show_in_emacs.png" /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;current_dir=$(dirname &amp;quot;$1&amp;quot;)
osascript -e 'tell application &amp;quot;Emacs&amp;quot; to activate'
path/to/emacsclient --eval &amp;quot;(progn (dired \&amp;quot;$current_dir\&amp;quot;) (dired-goto-file \&amp;quot;$1\&amp;quot;))&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a bonus, I added an &amp;quot;Open in Emacs&amp;quot; option, which does as it says on the tin. Rather than show the file listed in a dired buffer, it gets Emacs to open it in your favorite major mode. This option is not technically needed since Finder already provides an &amp;quot;Open With&amp;quot; context menu, but it does remove a few click here and there.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/show-in-finder--show-in-emacs/open_in_emacs.png" /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;osascript -e 'tell application &amp;quot;Emacs&amp;quot; to activate'
/Users/alvaro/homebrew/bin/emacsclient --eval &amp;quot;(find-file \&amp;quot;$1\&amp;quot;)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a side note, Emacs defaults to creating new frames when opening files via &amp;quot;Open With&amp;quot; menu (or &amp;quot;open -a Emacs foo.txt&amp;quot;). I prefer to use my existing Emacs frame, which can be accomplished by setting ns-pop-up-frames to nil.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(setq ns-pop-up-frames nil)
&lt;/code&gt;&lt;/pre&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 11 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/show-in-finder--show-in-emacs</guid></item><item><title>GitHub URL Hacks</title><link>https://justingarrison.com/blog/2021-07-11-github-url-hacks/</link><description>Handy tricks and shortcuts while using GitHub</description><author>Justin Garrison's Homepage</author><pubDate>Sun, 11 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/blog/2021-07-11-github-url-hacks/</guid></item><item><title>3 Kinds of Tech Debt</title><link>https://bytesizetheories.com/posts/3-kinds-of-tech-debt/</link><description>In software engineering, we’re often faced with trade-offs that sacrifice what is best for the long term against something that increases velocity in the short term. A commonly used financial analogy is describing this as taking on technical debt.
I will not cover accidental and deliberate tech debt, neither will I give a decision making framework in this article. That is its own subject, and context specific.
However, there is a mental framework that has worked well for me across different teams, codebases and systems.</description><author>Byte Size Theories</author><pubDate>Sun, 11 Jul 2021 00:54:37 GMT</pubDate><guid isPermaLink="true">https://bytesizetheories.com/posts/3-kinds-of-tech-debt/</guid></item><item><title>If self-help books are a scam, what else is?</title><link>https://stop.zona-m.net/2021/07/if-self-help-books-are-a-scam-what-else-is/</link><description>&lt;p&gt;That&amp;rsquo;s easy to see. REALLY easy, actually.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 10 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/if-self-help-books-are-a-scam-what-else-is/</guid></item><item><title>Eve Fan Fiction</title><link>https://hth.is/2021/07/10/eve-fan-fiction/</link><description>Growing up in Iceland in the nineties, it was impossible not to hear about Eve Online, the game that was going to do what at the time sounded impossible. A massively multiplayer online role playing game set in a persistent world where enterprising captains would roam space in their giant spaceships, striking out in any which way they wanted. Truly the vision that had been behind the classic game Elite.</description><author>Hrafn Thorvaldsson</author><pubDate>Sat, 10 Jul 2021 19:44:00 GMT</pubDate><guid isPermaLink="true">https://hth.is/2021/07/10/eve-fan-fiction/</guid></item><item><title>How to Build a gRPC Server over tstorage to create tstorage-server</title><link>https://bartlomiejmika.com/posts/2021/how-to-build-a-grpc-server-over-tstorage-to-create-tstorage-server/</link><description>&lt;p&gt;Did you just finish reading the &lt;a href="https://grpc.io/docs/languages/go/basics/"&gt;&lt;strong&gt;gRPC Basics tutorial&lt;/strong&gt;&lt;/a&gt; and you don&amp;rsquo;t know where to begin with using it? In this post I&amp;rsquo;ll explain how to take a fast time-series database called &lt;a href="https://github.com/nakabonne/tstorage"&gt;&lt;code&gt;tstorage&lt;/code&gt;&lt;/a&gt; and write a gRPC server and client with it.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Sat, 10 Jul 2021 06:45:08 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/how-to-build-a-grpc-server-over-tstorage-to-create-tstorage-server/</guid></item><item><title>Surgical Refactors</title><link>https://camhashemi.com/essays/surgical-refactors/</link><description>&lt;p&gt;Refactoring is like surgery.&lt;/p&gt;&lt;p&gt;That&amp;rsquo;s not to say that clean refactors save lives. But messy refactors definitely hurt.&lt;/p&gt;&lt;p&gt;A surgeon is economical with their cuts, and refactoring requires the same mindset.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;A naive surgeon takes a quick look at their patient and starts slicing away. They start with a big gash through the patient. They seek to cover as much ground as possible, as quickly as possible. They&amp;rsquo;re confident that they can close the gap once their work is done. And whenever they see something surprising, they start trying to fix that too, in the same session.&lt;/p&gt;&lt;p&gt;But once all the necessary cuts are made and the patient is ready to get stitched up, that&amp;rsquo;s when the panic of entropy sets in.&lt;/p&gt;&lt;p&gt;Your patient is now losing blood from every one of those cuts.&lt;/p&gt;&lt;p&gt;The clock is ticking. You don&amp;rsquo;t know which cut to close up first. Your partner texts you to come home early tonight. You close up one cut, and another one opens. Your coworker asks you whether you have any plans for the weekend. You rush to wrap things up before gravity pulls you away. Your coworkers start to light candles on a birthday cake. It&amp;rsquo;s your birthday cake. And your patient is bleeding out in front of you.&lt;/p&gt;&lt;p&gt;That all might sound extreme, but maybe only if you&amp;rsquo;ve never refactored code.&lt;/p&gt;&lt;p&gt;If you step away from all those open cuts for too long, you won&amp;rsquo;t remember how to stitch them back up again. And with all those cuts still open, you can&amp;rsquo;t even check whether the ones you did close up worked in the end, because your patient is still laying on the table! At some point, in the chaos of cleaning up after your own mess, you wish you could just start all over again. And thankfully, with code, you can.&lt;/p&gt;&lt;p&gt;But that&amp;rsquo;s why you&amp;rsquo;re not a surgeon.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Now imagine how a tenured surgeon works. They don&amp;rsquo;t just start slicing away! First, they examine. They have a long talk with the patient, followed by an uncomfortably long peek-around, followed by a few technical exams. An X-ray here, a colonoscopy there. Before they even think about picking up a scalpel, they know exactly where they&amp;rsquo;re going to cut and why.&lt;/p&gt;&lt;p&gt;And on the day of the operation, of course they&amp;rsquo;ll find surprises. Maybe there&amp;rsquo;s a huge bad thing that&amp;rsquo;ll kill the patient in two days if it&amp;rsquo;s not removed. And I don&amp;rsquo;t know that much about surgery, but I feel like THEY STILL DON&amp;rsquo;T REMOVE IT RIGHT AWAY. They use the opportunity to poke around and take notes, then they finish what they went in to do, seal things up, and plan their next move. The patient has to wake up and hear the bad news, but at least they get to wake up. Maybe they&amp;rsquo;ll even get to have some birthday cake.&lt;/p&gt;&lt;p&gt;The risks of surgery are such that bad surgeons either quickly become good surgeons, or change careers and become programmers.&lt;/p&gt;&lt;p&gt;But programming doesn&amp;rsquo;t have such immediate risks, so it&amp;rsquo;s easy for us to keep hacking away. The difference between surgery and software is that in software, we&amp;rsquo;re the ones who suffer for our mistakes, just not always enough to change our ways.&lt;/p&gt;&lt;p&gt;Hopefully this metaphor makes things a bit more vivid :)&lt;/p&gt;</description><author>Cam Hashemi</author><pubDate>Sat, 10 Jul 2021 04:38:07 GMT</pubDate><guid isPermaLink="true">https://camhashemi.com/essays/surgical-refactors/</guid></item><item><title>Vax'd Travel: Vegas, Boston, North Bay</title><link>https://faingezicht.com/photos/2021/07/10/vaxd-travel/</link><description>Fully vaccinated, and with most of the US reopening, it was time to get back on the road.

First, our friend group planned a trip to Vegas, then we went to visit my family in Boston, and lastly we went on a short road trip in the North Bay. I took photos everywhere.</description><author>Avy Faingezicht</author><pubDate>Sat, 10 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://faingezicht.com/photos/2021/07/10/vaxd-travel/</guid></item><item><title>2021-07-10</title><link>https://ho.dges.online/pictures/2021-07-10/</link><description/><author>ho.dges.online</author><pubDate>Sat, 10 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/pictures/2021-07-10/</guid></item><item><title>Are we Paying the Ransom or Not?</title><link>https://www.danstroot.com/posts/2021-07-10-are-we-paying-the-ransom</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/ransomware.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;The answer will depend primarily on how quickly the technology team contained the infection, how much effort it will be to eradicate the malware and restore the systems and data, and whether backups are immutable. Many victims still pay the ransom even when they have the means to restore everything from backups on their own. Why?&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-07-10-are-we-paying-the-ransom"&gt;Are we Paying the Ransom or Not?&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Sat, 10 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-07-10-are-we-paying-the-ransom</guid></item><item><title>Chinese corporation surrogates parents</title><link>https://stop.zona-m.net/2021/07/chinese-corporation-surrogates-parents/</link><description>&lt;p&gt;No, not like you are thinking. But it&amp;rsquo;s bad all the same.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 10 Jul 2021 02:30:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/chinese-corporation-surrogates-parents/</guid></item><item><title>How to Build a gRPC Server for a SparkFun Weather Shield</title><link>https://bartlomiejmika.com/posts/2021/how-to-build-a-grpc-server-for-a-sparkfun-weather-shield/</link><description>&lt;p&gt;We will take what we learned in the &lt;a href="https://bartlomiejmika.com/posts/2021/how-to-read-data-from-a-sparkfun-weather-shield-dev-13956-in-golang/"&gt;&lt;em&gt;previous post&lt;/em&gt;&lt;/a&gt; and rewrite it to support interprocess communication of our SparkFun Weather Shield from other applications using the &lt;a href="https://grpc.io"&gt;&lt;code&gt;gRPC&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Sat, 10 Jul 2021 01:35:46 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/how-to-build-a-grpc-server-for-a-sparkfun-weather-shield/</guid></item><item><title>YOLO object detection architecture</title><link>https://bytepawn.com/yolo-object-detection-architecture.html</link><description>&lt;p&gt;I discuss the YOLO neural network architecture for object detection. &lt;br /&gt;&lt;br /&gt;&lt;img alt="YOLO architecture" src="/images/yolov1_regions_notext.JPG" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 10 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/yolo-object-detection-architecture.html</guid></item><item><title>Row polymorphism crash course</title><link>https://ahnfelt.medium.com/row-polymorphism-crash-course-587f1e7b7c47?source=rss-3be9e7bc720f------2</link><description>&lt;p&gt;With type inference, structural records types can make a statically typed language feel almost like a scripting language, without giving up type safety:&lt;/p&gt;&lt;pre&gt;// Works for any record with the fields width, height : Float&lt;br /&gt;area2d v = v.width * v.height&lt;/pre&gt;&lt;p&gt;Row polymorphism can be used to implement this, and can also be used for polymorphic variants, effect systems and stack types in concatenative languages. This post gives a few quick examples of how it works.&lt;/p&gt;&lt;p&gt;Suppose we have two functions that work on row polymorphic records:&lt;/p&gt;&lt;pre&gt;f : forall row1. {a: Int, …row1} -&amp;gt; Int&lt;br /&gt;g : forall row2. {b: String, …row2} -&amp;gt; Int&lt;/pre&gt;&lt;p&gt;And we want to figure out the type of the following function:&lt;/p&gt;&lt;pre&gt;h x = f x + g x&lt;/pre&gt;&lt;p&gt;As usual, we &lt;em&gt;instantiate&lt;/em&gt; the &lt;strong&gt;forall&lt;/strong&gt;-bound variables — in this case &lt;em&gt;row variables&lt;/em&gt; by replacing them with fresh row variables.&lt;br /&gt;The instantiated types for the occurrences of &lt;strong&gt;f&lt;/strong&gt; and &lt;strong&gt;g&lt;/strong&gt; here are thus:&lt;/p&gt;&lt;pre&gt;f : {a: Int, …row3} -&amp;gt; Int&lt;br /&gt;g : {b: String, …row4} -&amp;gt; Int&lt;/pre&gt;&lt;p&gt;Since &lt;strong&gt;x&lt;/strong&gt; occurs as the argument of both &lt;strong&gt;f&lt;/strong&gt; and &lt;strong&gt;g&lt;/strong&gt;, we know two things about it:&lt;/p&gt;&lt;pre&gt;x : {a: Int, …row3}&lt;br /&gt;x : {b: String, …row4}&lt;/pre&gt;&lt;p&gt;Thus we must perform unification on those two types:&lt;/p&gt;&lt;pre&gt;{a: Int, …row3} = {b: String, …row4}&lt;/pre&gt;&lt;p&gt;To solve this, we create a substitution where each of the row variables contain the opposite records fields, as well as a fresh row variable &lt;strong&gt;row5&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;row3 = {b: String, …row5}&lt;br /&gt;row4 = {a: Int, …row5}&lt;/pre&gt;&lt;p&gt;Applying this substitution to the equation above gives us:&lt;/p&gt;&lt;pre&gt;{a: Int, b: String, …row5} = {b: String, a: Int, …row5}&lt;/pre&gt;&lt;p&gt;Sorting the fields lexicographically by field name gives us syntactically equality:&lt;/p&gt;&lt;pre&gt;{a: Int, b: String, …row5} = {a: Int, b: String, …row5}&lt;/pre&gt;&lt;p&gt;The final type for &lt;strong&gt;h&lt;/strong&gt; is thus (after generalization):&lt;/p&gt;&lt;pre&gt;h : forall row5. {a: Int, b: String, …row5} -&amp;gt; Int&lt;/pre&gt;&lt;p&gt;And thus we have inferred a row polymorphic record type by hand.&lt;/p&gt;&lt;p&gt;&lt;em&gt;One thing I like about using row polymorphism for inferring structural types is that it only involves equalities — no inequalities as you get with subtyping, which are much harder to solve for.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Now, what if &lt;strong&gt;f&lt;/strong&gt; and &lt;strong&gt;g &lt;/strong&gt;instead had the same field name, but with different types?&lt;/p&gt;&lt;pre&gt;f : {a: Int, …row3} -&amp;gt; Int&lt;br /&gt;g : {a: String, …row4} -&amp;gt; Int&lt;/pre&gt;&lt;p&gt;Then we’d need to unify these:&lt;/p&gt;&lt;pre&gt;{a: Int, …row3} = {a: String, …row4}&lt;/pre&gt;&lt;p&gt;When unifying two records, the types of the common fields are unified, and &lt;em&gt;not included&lt;/em&gt; in the substitution of &lt;strong&gt;row3&lt;/strong&gt; and &lt;strong&gt;row4&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;In this case, there is exactly one common field &lt;strong&gt;a&lt;/strong&gt;, with a different type on each side. That leads to a unification failure, and thus a type error:&lt;/p&gt;&lt;pre&gt;Int = String&lt;/pre&gt;&lt;p&gt;We’ve discovered an error in our program! The field &lt;strong&gt;a &lt;/strong&gt;can’t be both an Int and a String.&lt;/p&gt;&lt;img alt="" height="1" src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=587f1e7b7c47" width="1" /&gt;</description><author>Stories by Joakim Ahnfelt-Rønne on Medium</author><pubDate>Sat, 10 Jul 2021 00:10:33 GMT</pubDate><guid isPermaLink="true">https://ahnfelt.medium.com/row-polymorphism-crash-course-587f1e7b7c47?source=rss-3be9e7bc720f------2</guid></item><item><title>More RSS Tweaks</title><link>https://explog.in/rss.html#orgdee8c8d</link><description>&lt;p&gt;
    Seeing RSS entries published in Recurse Center's aggregator made me
    realize that the links/entries being published in the feed were
    broken. This is an attempt at fixing them.
    &lt;/p&gt;</description><author>RSS</author><pubDate>Fri, 09 Jul 2021 23:06:00 GMT</pubDate><guid isPermaLink="true">https://explog.in/rss.html#orgdee8c8d</guid></item><item><title>How to Read Data From a SparkFun Weather Shield (DEV-13956) in Golang</title><link>https://bartlomiejmika.com/posts/2021/how-to-read-data-from-a-sparkfun-weather-shield-dev-13956-in-golang/</link><description>&lt;p&gt;SparkFun Weather Shield monitoring dutifully a red mulberry seedling, the code is powered by what you will learn in this blog post.&lt;/p&gt;
&lt;p&gt;The purpose of blog post is to explain how to write a Golang application which can poll time-series data from a &lt;a href="https://www.sparkfun.com/products/13956"&gt;SparkFun Weather Shield (DEV-13956)&lt;/a&gt;.&lt;/p&gt;</description><author>Bartlomiej Mika</author><pubDate>Fri, 09 Jul 2021 20:27:03 GMT</pubDate><guid isPermaLink="true">https://bartlomiejmika.com/posts/2021/how-to-read-data-from-a-sparkfun-weather-shield-dev-13956-in-golang/</guid></item><item><title>Bad practice: busy waiting</title><link>https://evilcookie.de/bad-practice-busy-waiting.html</link><description/><author>blog</author><pubDate>Fri, 09 Jul 2021 14:13:03 GMT</pubDate><guid isPermaLink="true">https://evilcookie.de/bad-practice-busy-waiting.html</guid></item><item><title>Book Review: Power to the Words</title><link>https://nicolaiarocci.com/book-review-power-to-the-words/</link><description>&lt;p&gt;Vera Gheno&amp;rsquo;s &amp;ldquo;Potere alle Parole&amp;rdquo; (Power to the Words) is an essay on the
importance of appropriate use of the (Italian) language, not just in written
works but also and predominately in everyday life.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What would we think of a person who, having a vast wardrobe of beautiful
clothes, always wore the same suit out of laziness? These situations appear
unlikely; yet, they are examples of the attitude that many have towards their
language: they have access to an immense, incalculable patrimony, which out
of indolence, or fear, or inexperience, they use partially. Even if the
Italian does not need to be saved or preserved, we should love it more
because it is a magnificent instrument. It is a shame to limit ourselves to
only a superficial frequentation.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 09 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-power-to-the-words/</guid></item><item><title>Emotional Valence</title><link>https://june.kim/emotional-valence/</link><author>june.kim</author><pubDate>Fri, 09 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/emotional-valence/</guid></item><item><title>Hypercorrection</title><link>http://alanbernstein.github.io/posts/hypercorrection/</link><description>&lt;p&gt;I don&amp;rsquo;t like to be &amp;ldquo;that guy&amp;rdquo;, correcting meaningless mistakes of grammar/usage/pronunciation in conversation, because for whatever reason it&amp;rsquo;s considered rude or thoughtless. But if the mistake you&amp;rsquo;re making is &lt;a href="wikipedia"&gt;hypercorrection&lt;/a&gt;, pointing that out must be&amp;hellip; hypocorrection? Which, by extrapolation, must then be polite and thoughtful (/s).&lt;/p&gt;
&lt;p&gt;Despite my belief that prescriptivism in language is useless, it still feels grating to my ears when I hear incorrect usage. I don&amp;rsquo;t care about being right, or winning an argument about it, I just care about the truth being known. So, I don&amp;rsquo;t bring it up in person. But that doesn&amp;rsquo;t mean I won&amp;rsquo;t write about it.&lt;/p&gt;
&lt;p&gt;Around 2012 I started noticing people pronouncing the word &lt;em&gt;processes&lt;/em&gt; as &amp;ldquo;processEEZ&amp;rdquo; instead of &amp;ldquo;processIZ&amp;rdquo;. As far as I am aware, there is no rule of English pronunciation that says to do this. I think what happened is that someone misremembered the rule for pluralization of -x words, and started applying it all over the place like a crazy person.&lt;/p&gt;
&lt;p&gt;To be specific:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Most words are pluralized by appending the letter &amp;ldquo;s&amp;rdquo;, which is pronounced as &amp;ldquo;z&amp;rdquo;, like in &amp;ldquo;birds&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Singular words ending in one &amp;ldquo;s&amp;rdquo; are pluralized by adding &amp;ldquo;es&amp;rdquo;, which is pronounced as &amp;ldquo;iz&amp;rdquo;, like in &amp;ldquo;buses&amp;rdquo;. Words ending in &amp;ldquo;sh&amp;rdquo;, &amp;ldquo;ch&amp;rdquo;, &amp;ldquo;se&amp;rdquo;, &amp;ldquo;z&amp;rdquo; are treated similarly.&lt;/li&gt;
&lt;li&gt;The fairly unusual case: words ending in &amp;ldquo;x&amp;rdquo; are &lt;em&gt;sometimes&lt;/em&gt; pluralized by adding &amp;ldquo;es&amp;rdquo;, which is pronounced as &amp;ldquo;ih-sees&amp;rdquo;, like in &amp;ldquo;matrices&amp;rdquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This third case only applies to certain words ending in &amp;ldquo;x&amp;rdquo;. For example, &amp;ldquo;thorax&amp;rdquo;, &amp;ldquo;vortex&amp;rdquo;, and &amp;ldquo;matrix&amp;rdquo; are pluralized with the third rule, but &amp;ldquo;climax&amp;rdquo;, &amp;ldquo;complex&amp;rdquo;, and &amp;ldquo;fix&amp;rdquo; are pluralized with the second rule. I can imagine that this is determined by etymology, but I don&amp;rsquo;t see an obvious pattern.&lt;/p&gt;
&lt;p&gt;Clearly, not even all &amp;ldquo;-x&amp;rdquo; words use the &amp;ldquo;ces&amp;rdquo; spelling and &amp;ldquo;ih-sees&amp;rdquo; pronunciation. So, I found it pretty confusing when I started hearing words that don&amp;rsquo;t even end in &amp;ldquo;x&amp;rdquo; being pluralized this way as well. This appears to be a perfect example of hypercorrection to me.&lt;/p&gt;
&lt;p&gt;If that&amp;rsquo;s not convincing enough, consider these other examples, none of which are usually pluralized with the third rule.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Words ending in &amp;ldquo;ss&amp;rdquo;: cross, mass, truss&lt;/li&gt;
&lt;li&gt;Words ending in &amp;ldquo;ess&amp;rdquo;: congress, dress, press, goddess, mattress&lt;/li&gt;
&lt;li&gt;Words ending in &amp;ldquo;cess&amp;rdquo;: success, excess, access, recess, princess&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that as this list progresses, the suffix match with &amp;ldquo;process&amp;rdquo; increases in length.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Processes&amp;rdquo; should be pronounced &amp;ldquo;processIZ&amp;rdquo;.&lt;/p&gt;</description><author>Micro-optimist</author><pubDate>Fri, 09 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://alanbernstein.github.io/posts/hypercorrection/</guid></item><item><title>Sargable Queries &amp;amp; MUL Indexes; or Why My Query is Slow</title><link>https://thomascountz.com/2021/07/09/sargable-queries-and-mysql-text-indexing</link><description>My discovery of "sargability" in SQL queries, told through the lens of debugging a slow ActiveRecord query in a MySQL database.</description><author>Thomas Countz</author><pubDate>Fri, 09 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://thomascountz.com/2021/07/09/sargable-queries-and-mysql-text-indexing</guid></item><item><title>Nah, we are not postmodern. We are just hypermodern</title><link>https://stop.zona-m.net/2021/07/nah-we-are-not-postmodern.-we-are-just-hypermodern/</link><description>&lt;p&gt;Is that good or bad? You judge.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 08 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/nah-we-are-not-postmodern.-we-are-just-hypermodern/</guid></item><item><title>I Care a Lot</title><link>https://olshansky.info/movie/i_care_a_lot/</link><description>Olshansky's review of I Care a Lot</description><author>🦉 olshansky 🦁</author><pubDate>Thu, 08 Jul 2021 11:05:31 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/i_care_a_lot/</guid></item><item><title>PhDs Aren't Starting Companies Like They Used To</title><link>https://whoisnnamdi.com/phd-founders/</link><description>The burden of scientific knowledge and managerial complexity is crushing our best and brightest.</description><author>Who is Nnamdi?</author><pubDate>Thu, 08 Jul 2021 10:01:00 GMT</pubDate><guid isPermaLink="true">https://whoisnnamdi.com/phd-founders/</guid></item><item><title>Re: To unit test or not to unit test, that is the question</title><link>https://www.marginalia.nu/log/02-re-tests/</link><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="#ZgotmplZ"&gt;gemini://gemini.conman.org/boston/2021/07/07.1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I felt the need to add some thoughts tangentially related to this post by Sean Conner.&lt;/p&gt;
&lt;h2 id="why-do-we-hold-unit-tests-in-such-high-regard"&gt;Why do we hold unit tests in such high regard?&lt;/h2&gt;
&lt;p&gt;Enterprise software development (Agile with a TM at the end), and to an increasing degree open source software development has really accepted the Unit Test as personal lord and savior deep within their souls. If it doesn&amp;rsquo;t have coverage, it&amp;rsquo;s bad. If it has coverage, it&amp;rsquo;s good.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Thu, 08 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/02-re-tests/</guid></item><item><title>React without webpack: fast path to a working app from scratch</title><link>http://notes.eatonphil.com/react-without-webpack.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-07-08-react-without-webpack.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Thu, 08 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/react-without-webpack.html</guid></item><item><title>Ergo update: co-working experiment</title><link>https://mgsloan.com/posts/ergo-update-coworking/</link><description>A semi-accidental experiment in sitting more</description><author>mgsloan's blog</author><pubDate>Thu, 08 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://mgsloan.com/posts/ergo-update-coworking/</guid></item><item><title>Using a Digital Ocean Droplet to backup Google Takeout to Backblaze B2</title><link>https://nikhilism.com/post/2021/google-takeout-backblaze-remote/</link><description>Notes on using a Digital Ocean Droplet to copy Google Takeout data to Backblaze B2, for my own reference and in case they are useful to others. This uses rclone to perform the sync.
Google Takeout allows exporting all your Google data, but requires a modern browser, with GUI and JS. The total data can be huge (~36GB for my last export), so downloading it to my local machine and uploading it can be slow (even on a Gigabit connection).</description><author>Nikhil's blog</author><pubDate>Wed, 07 Jul 2021 22:41:20 GMT</pubDate><guid isPermaLink="true">https://nikhilism.com/post/2021/google-takeout-backblaze-remote/</guid></item><item><title>What is Marginalization?</title><link>https://blog.lukesalamone.com/posts/what-is-marginalization/</link><description>&lt;p&gt;In machine learning and statistics, marginalization simply means summing over a set of independent variables. For example, suppose an avid tennis player kept track of the number of days he played tennis over a period of time as well as the weather on that day:&lt;/p&gt;

&lt;table style="width: 100%;"&gt;
  &lt;tr style="font-weight: bold;"&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th colspan="3" style="text-align: center;"&gt;weather&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr style="font-weight: bold; text-align: center; background-color: inherit;"&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;td&gt;sunny&lt;/td&gt;
    &lt;td&gt;cloudy&lt;/td&gt;
    &lt;td&gt;rainy&lt;/td&gt;
    &lt;th&gt;totals&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
  	&lt;td rowspan="2" style="font-weight: bold; text-align: right;"&gt;play?&lt;/td&gt;
    &lt;td style="text-align: right;"&gt;yes&lt;/td&gt;
    &lt;td class="blue"&gt;70&lt;/td&gt;
    &lt;td class="blue"&gt;25&lt;/td&gt;
    &lt;td class="blue"&gt;1&lt;/td&gt;
    &lt;td class="gray" style="font-weight: bold;"&gt;96&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr style="background-color: inherit;"&gt;
  	&lt;td style="text-align: right;"&gt;no&lt;/td&gt;
  	&lt;td class="blue"&gt;70&lt;/td&gt;
    &lt;td class="blue"&gt;5&lt;/td&gt;
    &lt;td class="blue"&gt;9&lt;/td&gt;
    &lt;td class="gray" style="font-weight: bold;"&gt;84&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr style="font-weight: bold;"&gt;
  	&lt;td colspan="2" style="text-align: right;"&gt;totals&lt;/td&gt;
  	&lt;td class="gray"&gt;140&lt;/td&gt;
    &lt;td class="gray"&gt;30&lt;/td&gt;
    &lt;td class="gray"&gt;10&lt;/td&gt;
    &lt;td class="gray"&gt;180&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;(&lt;em&gt;In this table we&amp;rsquo;re keeping track of the number of days. If you want probabilities, divide each value in the table by 180. But I think whole numbers are easier to think about so I&amp;rsquo;m keeping them.&lt;/em&gt;)&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Wed, 07 Jul 2021 22:23:12 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/what-is-marginalization/</guid></item><item><title>Foodinho and Glovo get a slap on the wrist</title><link>https://stop.zona-m.net/2021/07/foodinho-and-glovo-get-a-slap-on-the-wrist/</link><description>&lt;p&gt;Hopefully, it will matter. Eventually.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 07 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/foodinho-and-glovo-get-a-slap-on-the-wrist/</guid></item><item><title>Optimising a Vuetify app</title><link>https://gaganpreet.in/posts/vue-vuetify-performance/</link><description>&lt;h2 class="relative group" id="background"&gt;Background &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#background"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve been building &lt;a href="https://nocookieanalytics.com/" rel="noreferrer" target="_blank"&gt;No Cookie Analytics&lt;/a&gt; in Vue using the Vuetify framework. Most of my time was spent hacking away without any due regard to performance. I looked at the PageSpeed Insights score, which handed out an abysmal score of 29 (out of 100). It was not shocking, I hadn&amp;rsquo;t spent any time on this part. The app &lt;em&gt;was&lt;/em&gt; sluggish and I knew I had to fix it. Here&amp;rsquo;s my before and after scores with the steps I took to optimise the app:&lt;/p&gt;</description><author>Gaganpreet Arora</author><pubDate>Wed, 07 Jul 2021 15:53:00 GMT</pubDate><guid isPermaLink="true">https://gaganpreet.in/posts/vue-vuetify-performance/</guid></item><item><title>Interstellar</title><link>https://one.mikro2nd.net/pages/interstellar/</link><description>Let&amp;rsquo;s pretend. Let&amp;rsquo;s pretend that some sort of interstellar civilisation comes about. This is SF. The question that good SF wants to ask is, &amp;ldquo;What does this mean for people?&amp;rdquo; Or, put another way, what sort of society seems likely given a bunch of star-systems infested inhabited by humans.
Ground Rules My starting assumptions are:
No sapient aliens. This get complicated enough with just Homo sapiens and our eternal squabbling and irrationalities.</description><author>one mikro2nd</author><pubDate>Wed, 07 Jul 2021 13:14:12 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/interstellar/</guid></item><item><title>The Astrolabe Part I: Lenscraft</title><link>https://www.marginalia.nu/log/01-astrolabe/</link><description>&lt;p&gt;Something you probably know, but may not have thought about a lot is that the Internet is large. It is unbelievably vast beyond any human comprehension. What you think of as &amp;ldquo;The Internet&amp;rdquo; is a tiny fraction of that vast space with its billions upon billions of websites.&lt;/p&gt;
&lt;p&gt;We use various technologies, such as link aggregators and search engines to find our way and make sense of it all. Our choices in navigational aides also shapes the experience we have of the Internet. These convey a warped sense of what the Internet truly is. There is no way of not doing that. Since nothing can communicate the raw reality of the internet to a human mind, concessions need to be made. Some content needs to be promoted, other needs to be de-emphasized. An objective rendering is a pipe dream, even a fair random sample is a noisy incomprehensible mess.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 07 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/01-astrolabe/</guid></item><item><title>Remote work through a portal</title><link>https://june.kim/remote-work-through-a-portal/</link><author>june.kim</author><pubDate>Wed, 07 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/remote-work-through-a-portal/</guid></item><item><title>Speeding up Windows Electron.js builds with SCCACHE</title><link>https://www.jviotti.com/2021/07/07/speeding-up-windows-electron-builds-with-sccache.html</link><description>This article describes the back story behind a Chromium CL to restore SCCACHE support on Windows</description><author>Juan Cruz Viotti</author><pubDate>Wed, 07 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jviotti.com/2021/07/07/speeding-up-windows-electron-builds-with-sccache.html</guid></item><item><title>Type-Directed Permutation of Function Parameters</title><link>https://mgsloan.com/posts/reorder-args/</link><description>Variadic functions + unordered application.</description><author>mgsloan's blog</author><pubDate>Wed, 07 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://mgsloan.com/posts/reorder-args/</guid></item><item><title>A quick portrait of techno-feudalism</title><link>https://stop.zona-m.net/2021/07/a-quick-portrait-of-techno-feudalism/</link><description>&lt;p&gt;&amp;ldquo;After 2008, everything changed.&amp;rdquo;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 06 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/a-quick-portrait-of-techno-feudalism/</guid></item><item><title>When being a nerd paid off</title><link>https://dorotac.eu/posts/nerd/</link><description>&lt;h1&gt;When being a nerd paid off&lt;/h1&gt;
&lt;p&gt;A few days ago, my knowledge of obscure computing facts surprisingly saved the day! Surprisingly, because sticking to garden variety best principles would have left me worrying. What was the deal? It's about a deleted photo on my camera.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;It was a sunny day, so I copied photos off my camera onto the computer, and set out with a sense of journey and a clean storage. Or so I thought: when I stopped to take a photo, my camera complained about full storage, so I selected "delete all photos". While the screen showed "erasing", I realized in horror: I took a cute picture of a dilapidated wagon already! And I'm not going back there again to take another one.&lt;/p&gt;
&lt;p&gt;Common advice in those situations says: don't use the storage medium under no circumstances, and attempt recovery on a computer. Then you might be lucky and keep your deleted data from being overwritten.&lt;/p&gt;
&lt;p&gt;This advice is right, but the trip was just beginning, and I was not ready to stop taking photos. Thankfully, my nerdy sense told me that I have a very good chance to get the picture back even if I take more photos – as long as I don't go crazy with them. That's due to some secrets that I learned about technology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;computers (and therefore digital cameras) are made by humans, and humans like simplicity,&lt;/li&gt;
&lt;li&gt;modern storage is logically structured a bit like a cassette tape (or a photo film),&lt;/li&gt;
&lt;li&gt;"deleted" data does not actually get cleared until it's overwritten by new data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might be surprised to learn about the similarity of storage to tape, but it's there on hard drives, actual tape drives, SD cards, and SSDs. The structure exposed to the programmer is of a long string of small pieces of data (sectors), each of which has a number, starting from 0, almost like houses on the street.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A drawing of numbered houses on Sector Street, numbers 1 to 5" src="sectors.svg" /&gt;&lt;/p&gt;
&lt;p&gt;Accessing sectors (by writing or reading) is simplest when it's done in order. We're only humans, and why use an elaborate scheme if we can just move from one to the next? And so, the reading head slides steadily on the tape, the film in a camera moves by one frame after each photo, the postman visits houses in order. And it's not just out of convenience: it's clearly the fastest when there's a moving physical object involved. Surprisingly, writing in order is even the fastest way to &lt;a href="https://www.anandtech.com/show/2738/25"&gt;write&lt;/a&gt; data on SSDs, which have no moving parts!&lt;/p&gt;
&lt;h2&gt;Append only&lt;/h2&gt;
&lt;p&gt;This must be coupled with an observation: cameras – or at least my camera – only add new pictures onto the SD card (our "tape"), and when deletions happen, the entire contents of the card are marked as deleted. Just like in a film camera: once a photo is taken, it's taken. We can replace the entire tape, but we don't replace a single picture with a blank.&lt;/p&gt;
&lt;p&gt;This makes life easier, because imagine if we had a tape where we could erase already taken pictures. At first it's okay: we take 30 pics, and we hit the end. Then we erase pictures number 7, 2, 17, and 22. To take another picture, we would have to fiddle with the tape to find an empty spot, then rewind it to the right position (make sure it's not overlapping!), and do it again until the tape is full again. That's no fun. It's easier if pictures come one after another.&lt;/p&gt;
&lt;p&gt;And while I don't have any evidence, I assume that's how my digital camera works, too. It's not a general purpose computer, so it doesn't have to deal with a lot of deletion. I can safely assume that the picture of the wagon (which was the last picture on my SD card before it filled up) will not occupy the same sectors as the first 10 pictures I take after the card is erased, and that it will not get overwritten.&lt;/p&gt;
&lt;p&gt;With a peace of mind (and ready to be proven wrong), I continued to take photos of the trip. When back home, I fired up &lt;em&gt;testdisk&lt;/em&gt;. Lo and behold, I recovered the picture!&lt;/p&gt;
&lt;p&gt;&lt;img alt="A dilapidated wooden wagon with one wheel modern and one missing" src="wagon.jpg" /&gt;&lt;/p&gt;
&lt;h2&gt;The real world&lt;/h2&gt;
&lt;p&gt;Okay, maybe I just got lucky. In reality, there's one thing that changes size. For SD cards, it's usually the File Allocation Table. It contains the numbers of sectors which contain pieces of each file, along with the names, and a bunch of extra info. As enough files and directories get added, it probably changes in size.&lt;/p&gt;
&lt;p&gt;Another complication is the mysterious "PRIVATE" directory. I presume this contains only hibernation data to make the camera start faster, and I'm not seeing any reason for it to change size at all (what kind of generated data does a camera need anyway?).&lt;/p&gt;
&lt;p&gt;In the end, what matters is not exactly that sectors get written from 0 up. What matters is that there is a defined order of writing which doesn't change between full card erasures. That's much more difficult to verify.&lt;/p&gt;
&lt;h2&gt;FAT32&lt;/h2&gt;
&lt;p&gt;FAT32 doesn't actually mark sectors as "deleted". It marks the entire file as deleted, without permanently erasing any of its information until it's needed. Testdisk knows this, and it makes recovery a breeze. The entire operation took 5 minutes.&lt;/p&gt;
&lt;h2&gt;CAUTION&lt;/h2&gt;
&lt;p&gt;This is all based on high level guessing, so don't rely on this. I made peace with losing the deleted picture when I decided to keep shooting new ones. Basic advice is still best: if you accidentally delete something important, immediately stop using the storage, &lt;strong&gt;do not mount the file system&lt;/strong&gt;, and make a &lt;em&gt;block level&lt;/em&gt; copy. And why don't you restore it from a backup anyway?&lt;/p&gt;</description><author>dcz's posts</author><pubDate>Tue, 06 Jul 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://dorotac.eu/posts/nerd/</guid></item><item><title>什么是事件建模Event Modeling?</title><link>https://www.4async.com/2021/07/introducing-event-modeling/</link><description>&lt;img alt="Featured image of post 什么是事件建模Event Modeling?" src="https://www.4async.com/2021/07/introducing-event-modeling/event-modeling-tutorial.jpg" /&gt;&lt;h2 id="基本概念"&gt;基本概念
&lt;/h2&gt;&lt;p&gt;事件建模（Event Modeling）是一种描述系统的方法，展示信息如何随时间变化的例子。具体来说，这种方式省略了瞬息万变的细节，而着眼于在任何特定的时间点上的持久化存储和用户所见数据的变化。这些时间轴上的事件，构成了对系统的描述。&lt;/p&gt;
&lt;p&gt;近年来，很多系统使用事件通过事件存储数据库或者使用特定方式使用常规数据库构建了状态和信息传播的模块。然而，大多数方法仍然依赖于通过SQL数据库、文档数据库或者其他技术实现严格意义上的当前时间点信息的视图。&lt;/p&gt;
&lt;p&gt;对很多系统而言，特别是对于非小型系统而言，随着系统复杂性的增加，变更成本将会随着时间的推移难度指数级上升。与现有的设计和建模方式对比，事件建模可以在短时间内创建一个基础蓝图，将返工工作量降到最低。&lt;/p&gt;
&lt;h3 id="从过去谈起"&gt;从过去谈起
&lt;/h3&gt;&lt;p&gt;讲故事自古以来就是人类能够将知识传递给后代的方法，它在很大程度上依赖于我们如何存储记忆-无论是逻辑的、视觉的、听觉的还是其他的。这一点很重要，因为这与信息系统的构建方式有相似之处。
用具体的例子说明某物应该如何工作是一种常见的方式。这种方式可以在软件开发的成功实践中看到，如行为驱动开发。这种方式很有效，因为我们通过故事来沟通更有效。将它和讲故事联系在一起，是一种保持社会信息的方式。我们的大脑是为它而建的，而不是为流程图和其他格式而建的。
而事件建模模型就是遵循这种讲故事模式而建立的产品建模方式。&lt;/p&gt;
&lt;h2 id="事件建模模式"&gt;事件建模模式
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://eventmodeling.org/posts/what-is-event-modeling/blueprint_large.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;时间线是最好描述故事主线的方式，对我们的系统而言，时间线也是描述我们系统核心部分概念的重要组成部分。我们可以通过在一条时间线上，系统从开始到结束，在没有分支情况下应该做什么方式展示我们系统的一部分功能。这就是一个典型的事件模型的组成。我们可以用这种方式跟踪所有UI界面中字段值如何存储和如何展示的。比如在上面的示例图中，我们使用了3种不同模块的内容和传统的线框模型就展示了整个系统的模型。但是简单性是我们重要的一个目标，因此我们只依赖于4种模式构建这种模型图。&lt;/p&gt;
&lt;h3 id="保持简单性"&gt;保持简单性
&lt;/h3&gt;&lt;p&gt;当我们想采用某些做法或流程来帮助彼此理解和沟通时，它与个人为熟练掌握这些方法而进行的学习量成反比。换句话说，如果我们可以更快的掌握一个名叫X的方法时，我们就可以更好的通过这种方式进行知识分享和互动；反之，无论这种方法多么好，昂贵的学习成本总会搞砸一切。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当一本书是团队中的必读书目时，每个人都会说他们读过；但事实上只有一半的人会真正读过；这些真正读过的人中一半的人会声称他们理解了这本书；而这些声称理解的人中只有一半的人真正的理解了这本书；而这些真正理解了这本书的人只有一半的人能够使用它。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;这就是为什么使用3个模块和基于2个想法的4种模式进行事件建模。因为这只需要几分钟就可以将所有的东西向所有人解释清楚。其他的学习则可以在实践中进行。即便理解出现了不足和错误，也可以很快在实践中得到纠正。&lt;/p&gt;
&lt;h3 id="事件"&gt;事件
&lt;/h3&gt;&lt;p&gt;假设我们想为连锁酒店设计一个酒店网站，让我们的客户可以在线预订房间，并让我们安排清洁和任何其他酒店问题。 我们可以显示在该业务的年度时间线上存储了哪些事件。 我们可以假装我们已经有了这个系统，然后问自己随着时间的推移存储了哪些事件。&lt;/p&gt;
&lt;h3 id="线框图"&gt;线框图
&lt;/h3&gt;&lt;p&gt;让我们看一下在图片的最上面的部分的第一个模块。为了让讲故事这个事情更加可视化，我们可以在顶部显示功能的线框图或者网页模拟图。这也可以被具象化为具体的泳道图，以方便不同的人（也可以是系统）与我们的系统进行互动。这里一些自动化的内容可以用齿轮表示，同时说明系统正在做什么。通过这种方式，我们可以非常容易的展示出系统需要实现的功能列表，执行流程和项目完成标记。这里的图是示例了一个酒店的预订、支付和通知系统的过程，我们可以重点关注一下所有相关高亮显示的内容。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://eventmodeling.org/posts/what-is-event-modeling/innovate_large.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;借助这个模块，我们可以很方便的和设计师一起沟通设计系统，当然，这里需要注意在设计中，两个重要的内容需要添加到整个设计中：用户所拥有的权限和用户可以获取的信息。&lt;/p&gt;
&lt;h3 id="命令"&gt;命令
&lt;/h3&gt;&lt;p&gt;大多数信息系统必须给用户一种影响系统的状态的能力，而这种能力就是命令。在我们的例子中，我们必须允许房间预订改变系统状态，这样我们就不会发生超额预订情况。当那个人在未来的预订日期到达时，他们就有一个为他们准备的房间。&lt;/p&gt;
&lt;p&gt;改变系统状态的意图会被封装在一个命令中。相对于简单地将表单数据保存到数据库中的一个表中，这可以让我们以非技术性的方式来显示意图，同时允许任何实现 - 尽管我们可以看到某些方法更具优势。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://eventmodeling.org/posts/what-is-event-modeling/empower_large.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;从UI和UX的角度来看，这就是一个&amp;quot;命令响应式用户界面&amp;quot;，对帮助制作可组合的UI大有帮助。使用这种模式，从技术和商业的角度来看，交易的界限就更清楚了。以酒店入住为例，酒店的客人要么登记成功，要么没有。&lt;/p&gt;
&lt;p&gt;当命令成功的前提条件有细微的差别时，它们会在&amp;quot;Given-When-Then&amp;quot;风格的描述方式中进行阐述。这种方式也是行为测试模式惯用的描述方式，也是一种成功的讲述故事的方式。实际执行过程中，可能会有几个这样的故事来说明一个命令如何能成功执行和不能成功执行。&lt;/p&gt;
&lt;p&gt;这里我们可以用一个例子来描述一下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given：我们已经注册并添加了一个支付方式&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;When：我们试图预订一个房间&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Then：一个房间被预订了&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;这种描述方式也通常被叫做“安排、行动、断言”，在UI/UX的世界中，也被称为“情景、统计、价值”。
在图中我们也可以发现，所有的命令都是用蓝色进行标记的。&lt;/p&gt;
&lt;h3 id="视图或者叫读模型"&gt;视图（或者叫读模型）
&lt;/h3&gt;&lt;p&gt;任何信息系统的一个重要能力是将系统中保存的状态告知用户。我们的酒店客人需要知道他们感兴趣的某些类型的房间在哪一天可以入住。这通常有很多种情况，需要支持信息系统的多个模型。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://eventmodeling.org/posts/what-is-event-modeling/inform_large.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;随着这些新事件的存储，系统中的视图也会一直变化。在我们的酒店系统中，这个日历视图随着影响库存的新事件的发生而被更新。其他视图中清洁团队可以在客户离店事件存储后在其他视图中看到房间已经可以被清理了。&lt;/p&gt;
&lt;p&gt;指定视图的行为方式与我们指定接受命令的方式非常相似，但有一处不同。视图是被动的，并且不能在事件被存储到系统中之后撤销事件。&lt;/p&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given：酒店设置了12间海景房，海景房从4月4日到12日被预订&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Then：日历上应该显示除4月4日到12日以外的所有海景房的日期&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;从上面图中我们也可以注意到，所有的读都是用绿色进行标记的。&lt;/p&gt;
&lt;h3 id="集成"&gt;集成
&lt;/h3&gt;&lt;p&gt;我们刚刚介绍了描述大多数系统所需的 4 种模式中的前 2 种模式。 系统可以从其他系统获取信息并且将信息发送到其他系统。 强迫这 2 个模式成为前 2 个模式的扩展并共享相同的空间是很诱人的选择。 然而这会让交流变得更加困难，因为它们没有人类可见的方面，并且需要一些更高级别的模式。&lt;/p&gt;
&lt;h3 id="翻译"&gt;翻译
&lt;/h3&gt;&lt;p&gt;当我们有一个为我们提供信息的外部系统时，将这些信息转换成我们自己系统中的更熟悉的形式会很有帮助。 在我们的酒店系统中，如果选择让我们的清洁人员反应更加灵敏，我们可以从客人的 GPS 坐标中获取事件。 我们不想使用经度和纬度对作为事件来指定我们系统中的先决条件。 我们宁愿选择对我们有意义的活动，例如“客人离开酒店”、“客人回到酒店房间”。&lt;/p&gt;</description><author>ipfans's Blog</author><pubDate>Tue, 06 Jul 2021 14:41:00 GMT</pubDate><guid isPermaLink="true">https://www.4async.com/2021/07/introducing-event-modeling/</guid></item><item><title>My DotNetPodcast interview</title><link>https://nicolaiarocci.com/my-dotnetpodcast-interview/</link><description>&lt;p&gt;Today I was interviewed by &lt;a href="https://twitter.com/mauroservienti"&gt;Mauro Servienti&lt;/a&gt; on the &lt;a href="https://www.spreaker.com/show/dotnetpodcast"&gt;DotNetPodcast&lt;/a&gt;. The
theme was my experience as an open-source maintainer on both the Python and C#
stacks. We also discussed the ongoing evolution of the dotNET ecosystem,
touching on a few tangent topics.&lt;/p&gt;
&lt;p&gt;The recording is in Italian and is available below here.&lt;/p&gt;
&lt;p&gt;&lt;!-- raw HTML omitted --&gt;Listen to &amp;ldquo;Python, Eve, open source
e fattura elettronica. Con Nicola Iarocci&amp;rdquo; on Spreaker.&lt;!-- raw HTML omitted --&gt;&lt;!-- raw HTML omitted --&gt;&lt;!-- raw HTML omitted --&gt;&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 06 Jul 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/my-dotnetpodcast-interview/</guid></item><item><title>The Death of Ivan Ilych</title><link>https://apurva-shukla.me/bookshelf/the-death-of-ivan-ilych/</link><description>⭐ ⭐ ⭐ ⭐ ⭐ After reading other Russian literature I knew I was getting into another tough read, but suprisingly Tolstoy’s The Death of Ivan…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Tue, 06 Jul 2021 08:39:33 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/bookshelf/the-death-of-ivan-ilych/</guid></item><item><title>List of P2P File Sending Tools</title><link>https://xosh.org/p2p-tools/</link><description>Browser based
FilePizza https://file.pizza/
SnapDrop https://snapdrop.net/
Instant.io https://instant.io/
file ai http://fileai.com/
Send Anywhere https://send-anywhere.com/
Just Beaam it https://www.justbeamit.com/
Yt2fb.in https://yt2fb.in/file-transfer/
CLI
croc https://github.com/schollz/croc
p2pcopy https://github.com/psantosl/p2pcopy
pcp https://github.com/dennis-tra/pcp
wormhole-william https://github.com/psanford/wormhole-william
This list is being maintained with Dynalist</description><author>XOSH.ORG</author><pubDate>Tue, 06 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xosh.org/p2p-tools/</guid></item><item><title>My 2021 New Mac Setup</title><link>https://www.swyx.io/new-mac-setup-2021</link><description>I set up a new Mac for work today. Here's everything I use on a Mac for fullstack web development.</description><author>swyx's site RSS Feed</author><pubDate>Tue, 06 Jul 2021 00:57:11 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/new-mac-setup-2021</guid></item><item><title>On erasing Palestinians. Or everybody else</title><link>https://stop.zona-m.net/2021/07/on-erasing-palestinians.-or-everybody-else/</link><description>&lt;p&gt;Almost everywhere. surveillance and ensorship come from far away.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Mon, 05 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/on-erasing-palestinians.-or-everybody-else/</guid></item><item><title>Virtual 6502 – Update Round 2</title><link>https://www.masswerk.at/nowgobang/2021/virtual6502-update3</link><description>A few new features for the 6502 online disassembler.</description><author>mass:werk – Now Go Bang!</author><pubDate>Mon, 05 Jul 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/virtual6502-update3</guid></item><item><title>A shiny new expLog</title><link>https://explog.in/config.html</link><description>&lt;p&gt;
    I've reworked how expLog is generated to make it easy to publish by a
    Github Action, with a shell script to run emacs in batch mode to make
    this significantly easier.
    &lt;/p&gt;

    &lt;p&gt;
    The RSS feed became a casualty of this work, but I finally got around
    to fixing it today, and I hope it gets picked up seamlessly for anyone
    who had subscribed in the past.
    &lt;/p&gt;

    &lt;p&gt;
    PS. I've been publicly iterating on a series of posts on &lt;a href="https://explog.in/notes/devtools/index.html"&gt;building
    developer tools&lt;/a&gt;; I'm still editing, but I should be finished soon. Let
    me know if you have any feedback!
    &lt;/p&gt;</description><author>RSS</author><pubDate>Mon, 05 Jul 2021 16:05:00 GMT</pubDate><guid isPermaLink="true">https://explog.in/config.html</guid></item><item><title>Bill &amp;amp; Ted Face the Music</title><link>https://olshansky.info/movie/bill__ted_face_the_music/</link><description>Olshansky's review of Bill &amp;amp; Ted Face the Music</description><author>🦉 olshansky 🦁</author><pubDate>Mon, 05 Jul 2021 11:57:51 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/bill__ted_face_the_music/</guid></item><item><title>Agile in action – Information Radiator using Notion</title><link>https://june.kim/agile-in-action-information-radiator/</link><author>june.kim</author><pubDate>Mon, 05 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://june.kim/agile-in-action-information-radiator/</guid></item><item><title>Building Electron.js on Windows using the Visual Studio Build Tools</title><link>https://www.jviotti.com/2021/07/05/building-electron-on-windows-using-the-visual-studio-build-tools.html</link><description>This article describes how to build Electron.js on Windows using only the Visual Studio Build Tools offering</description><author>Juan Cruz Viotti</author><pubDate>Mon, 05 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jviotti.com/2021/07/05/building-electron-on-windows-using-the-visual-studio-build-tools.html</guid></item><item><title>Book summary: 2k to 10k</title><link>https://heidenstedt.org/links/book_summary_2k_to_10k/</link><description>&lt;p&gt;
      &lt;em&gt;Best viewed on the &lt;a href="https://heidenstedt.org/links/book_summary_2k_to_10k/"&gt;original page&lt;/a&gt;, where extended functionality like the
    footnote helper is available.&lt;/em&gt;
    &lt;/p&gt;&lt;p&gt;&lt;a href="https://web.archive.org/web/20211026120823/http://hcoder.org/2016/12/12/book-summary-2k-to-10k/"&gt;Book summary: 2k to 10k&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;TLDR:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;2k to 10k: Writing Faster, Writing Better, and Writing More of What You Love&amp;rdquo; by Rachel Aaron provides practical tips and advice for writers. It&amp;rsquo;s divided into two parts: writing process and background work.&lt;/p&gt;
&lt;p&gt;Part one discusses a process for writing quickly and efficiently, which is based on three factors: knowledge, time, and enthusiasm. It encourages understanding what you&amp;rsquo;re going to write before you start, optimizing your writing sessions based on when/where you&amp;rsquo;re most productive, and writing content that you&amp;rsquo;re enthusiastic about.&lt;/p&gt;
&lt;p&gt;Part two shares advice on plotting, character development, and editing. It provides a five-step process for plotting a story and emphasizes the importance of well-developed characters with agency. It also breaks down the traditional three-act structure, the concept of scenes serving multiple purposes, and provides a practical approach to editing.&lt;/p&gt;
&lt;p&gt;The book underlines that writing is an enjoyable process, characters should be designed to drive their stories, and editing is a skill that can be mastered. It encourages authors to shift their thinking and approach to writing, making it both an enjoyable and efficient process. The final objective of editing, according to the book, is to enhance the reader experience.&lt;/p&gt;</description><author>Mia Heidenstedt</author><pubDate>Sun, 04 Jul 2021 23:29:21 GMT</pubDate><guid isPermaLink="true">https://heidenstedt.org/links/book_summary_2k_to_10k/</guid></item><item><title>Book Wishlist</title><link>https://one.mikro2nd.net/pages/bookwishlist/</link><description>SFF In the Country of the Blind - Michael Flynn Anything by Catherine Valente A Memory of Empire - Arkady Martine Blindsight - Peter Watts Douglas Adams - Evil Machines Aliette de Bodard - Seven of Infinities Embers of War - Gareth Powell Ann Leckie - Translation State Ted Chiang - The Lifecycle of Software Objects Charles Stross - Singularity Sky, Empire Games trilogy Darwin Among the Machines - George Dyson more of Neil Gaiman, but first you&amp;rsquo;d have to ask which ones I already own Retribution Falls - Chris Wooding A Peculiar Peril - Jeff VanderMeer Clint Wastling - Tyrants Rex Rivers Solomon - An Unkindness of Ghosts Rod Duncan - The Queen of all Crows Libba Brey - Going Bovine Mark Danielewski - House of Leaves Annalee Newitz - Autonomous Frank Landis - Hot Earth Dreams Jason Arnopp - The Last Days Of Jack Sparks C Stross - The Atrocity Archives Stevenson - Seveneves, Reamde, Zodiac Christian Cantrell - Containment / Equinox Joel Shephard - The Spiral Wars (series) Books About Writing Blake Snyder - Save the Cat Chuck Wendig - You Can Do Anything, Magic Skeleton Authors I&amp;rsquo;d Like To Read More Of Gareth Powell Jeff Vandermeer Laura Anne Gilman Lovecraft Scott Westerfeld (Steampunk) Natasha Mostert Raymond Chandler Brewing Malting at Home - Dyment The Brewer&amp;rsquo;s Apprentice - Greg Koch, Matt Allyn Others Dan Davies - The Unaccountability Machine: Why Big Systems Make Terrible Decisions and How the World Lost Its Mind Stephen Bates - The Shortest History of the Crown The Independent Scholar&amp;rsquo;s Handbook - Ronald Gross Talib &amp;ndash; Antifragile: Things that Gain from Disorder Dalai Lama &amp;ndash; Beyond Religion The Spiritual Brain Homage to Gaia - James Lovelock Inventing the Future - Albert Cory The Dream Machine - Mitchell Waldrop What Do You Care What Other People Think?</description><author>one mikro2nd</author><pubDate>Sun, 04 Jul 2021 17:10:31 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/bookwishlist/</guid></item><item><title>Time Dilation</title><link>https://utf9k.net/blog/time-dilation/</link><description>Historically, I am often late to a bunch of things even though I plan ahead some of the time</description><author>utf9k</author><pubDate>Sun, 04 Jul 2021 12:53:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/time-dilation/</guid></item><item><title>Connect AWS Cognito to Database</title><link>https://prashamhtrivedi.in/db_to_cognito/</link><description>AWS Cognito is very powerful system of managing users, connecting AWS Cognito with our own database is easy and very important task. This post describes how we leverage best of both Cognito and Our database to our advantage</description><author>Prasham H Trivedi</author><pubDate>Sun, 04 Jul 2021 11:00:35 GMT</pubDate><guid isPermaLink="true">https://prashamhtrivedi.in/db_to_cognito/</guid></item><item><title>No Such Thing as a Fish</title><link>https://sonnet.io/posts/starfish/</link><description>&lt;p&gt;This one will be short. A few days ago I was facilitating a retrospective for a team being a mix of old and new people, all of that in an organisation that has scaled up quite a bit in the past few years. It's not a surprise that many of their questions dealt with their process, practices, figuring out how to live work with each other, how to make stuff.&lt;/p&gt;
&lt;p&gt;I remembered that I had an old template of the &lt;a href="https://www.funretrospectives.com/starfish/"&gt;Starfish&lt;/a&gt; team activity, which fits the use-case perfectly. Also, is quite fun to run. So I dug it out and pasted into Procreate. Check it out if you're running a practice-focused retrospective:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sonnet.io/images/starfish/template.png" target="_blank"&gt;Download here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As always, it should work fine as a background for Mural, Miro, Figma Jam board or whatever is your jam (mine's fig).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Retro template" src="https://sonnet.io/images/starfish/preview.png" /&gt;&lt;/p&gt;
&lt;p&gt;Now, although I'm a fan of Funretros, I still believe that my Starfish is superior to their examples, as it's wearing a slingsuit.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;PS. The making-of video can be found &lt;a class="umami--click--cursed-starfish" href="https://sonnet.io/images/starfish/making-of-nsfw.mp4" target="_blank" title="I'm burnt out"&gt;here&lt;/a&gt;. It's cursed, remember I warned you.&lt;/p&gt;</description><author>Rafał Pastuszak</author><pubDate>Sun, 04 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://sonnet.io/posts/starfish/</guid></item><item><title>Space Invader • Art</title><link>https://muffinman.io/art/space-invader/</link><description>&lt;div class="container"&gt;&lt;div class="art-page__content"&gt;&lt;/div&gt;&lt;table class="text-sm art-page__info"&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Created: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;July 2021&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Size: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;30x21cm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Paper: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;Fabriano Bristol 250gsm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Pens: &lt;/b&gt;&lt;/td&gt;&lt;td&gt;Pigma Micron&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="art-gallery photoswipe-gallery art-gallery--wide"&gt;&lt;div class="art-gallery__cover"&gt;&lt;a class="gallery__link outline" href="./0.jpg"&gt;&lt;img alt="" class="gallery__image" height="2025" src="./0.jpg" width="2700" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="art-gallery__items"&gt;&lt;a class="gallery__link outline" href="./1.jpg"&gt;&lt;img alt="" class="gallery__image" height="1845" src="./w_500-h_500--1.jpg" width="2500" /&gt;&lt;/a&gt;&lt;a class="gallery__link outline" href="./plot_1920x1080.mp4"&gt;&lt;video class="gallery__video" src="./plot_1920x1080.mp4#t=0.001"&gt;&lt;/video&gt;&lt;svg class="gallery__play-icon" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="M405.2,232.9L126.8,67.2c-3.4-2-6.9-3.2-10.9-3.2c-10.9,0-19.8,9-19.8,20H96v344h0.1c0,11,8.9,20,19.8,20 c4.1,0,7.5-1.4,11.2-3.4l278.1-165.5c6.6-5.5,10.8-13.8,10.8-23.1C416,246.7,411.8,238.5,405.2,232.9z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;</description><author>Muffin Man</author><pubDate>Sun, 04 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://muffinman.io/art/space-invader/</guid></item><item><title>The REAL case for "BIG nuclear batteries"</title><link>https://stop.zona-m.net/2021/07/the-real-case-for-big-nuclear-batteries/</link><description>&lt;p&gt;As always, it&amp;rsquo;s not technical, nor environmental.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 03 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/the-real-case-for-big-nuclear-batteries/</guid></item><item><title>A Short Round-Up of Topology-Based Papers at ICLR 2021</title><link>https://bastian.rieck.me/blog/2021/iclr_topology_roundup/</link><description>&lt;p&gt;With &lt;a href="https://iclr.cc/Conferences/2021"&gt;ICLR 2021&lt;/a&gt; having finished
a while ago, I wanted to continue series. See &lt;a href="https://bastian.rieck.me/blog/2020/icml_topology_roundup/"&gt;here&lt;/a&gt;
and &lt;a href="https://bastian.rieck.me/blog/2019/icml_tda_roundup/"&gt;here&lt;/a&gt; for
summaries of previous conferences. This time, I will present three
papers that caught my eye&amp;mdash;they are all making use of topological
concepts to some extent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat lector&lt;/strong&gt;: I might have missed some promising papers. Any
suggestions for additions are more than welcome! Please reach out
to me via &lt;a href="https://twitter.com/Pseudomanifold"&gt;Twitter&lt;/a&gt; or
&lt;a href="mailto:bastian@rieck.me"&gt;e-mail&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="hu-et-al-topology-aware-segmentation-using-discrete-morse-theory"&gt;Hu et al.: Topology-Aware Segmentation Using Discrete Morse Theory&lt;/h1&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="Topology-Aware Segmentation Using Discrete Morse Theory" src="https://bastian.rieck.me/images/iclr21_hu.png" width="500" /&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://openreview.net/pdf?id=LGgdb4TS4Z"&gt;Topology-Aware Segmentation Using Discrete Morse Theory&lt;/a&gt;
by &lt;a href="https://www3.cs.stonybrook.edu/~xiaolhu/"&gt;Xiaoling Hu&lt;/a&gt;, &lt;a href="http://yusu.belkin-wang.org/"&gt;Yusu Wang&lt;/a&gt;,
&lt;a href="http://web.engr.oregonstate.edu/~lif/"&gt;Li Fuxin&lt;/a&gt;, &lt;a href="https://scholar.google.com/citations?user=BxbKTYkAAAAJ"&gt;Dimitris Samaras&lt;/a&gt;, and
&lt;a href="https://bmi.stonybrookmedicine.edu/people/chao_chen"&gt;Chao Chen&lt;/a&gt;
demonstrates how to employ topological concepts in order to improve
segmentation techniques. This is achieved by ensuring that the Betti
numbers&amp;mdash;essentially, counts of topological features&amp;mdash;are maintained
by a segmentation. The paper is part of a strand of research that
develops topology-based loss terms in order to regularise the output of
a model.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; Another interesting aspect of this paper is that introduces
concepts from &lt;a href="https://en.wikipedia.org/wiki/Morse_theory"&gt;Morse theory&lt;/a&gt;
to the community. This, in my opinion, is long overdue&amp;mdash;Morse theory
offers such a great perspective in explaining the loss landscape, for
instance.&lt;/p&gt;
&lt;p&gt;I am also happy to see how topological methods are now sufficiently
mature to be serious alternatives to other methods. The application area
of image segmentation is therefore a perfect fit to demonstrate the
capabilities of additional connectivity constraints; it sidesteps a lot
of the issues that usually arise when we try to allude to the utility of
topological features in high-dimensional spaces.&lt;/p&gt;
&lt;h1 id="kyriakis-et-al-learning-hyperbolic-representations-of-topological-features"&gt;Kyriakis et al.: Learning Hyperbolic Representations of Topological Features&lt;/h1&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="Learning Hyperbolic Representations of Topological Features" src="https://bastian.rieck.me/images/iclr21_kyriakis.png" width="500" /&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://openreview.net/pdf?id=yqPnIRhHtZv"&gt;Learning Hyperbolic Representations of Topological Features&lt;/a&gt; by
&lt;a href="https://scholar.google.com/citations?user=6b8N2RQAAAAJ"&gt;Panagiotis Kyriakis&lt;/a&gt;, &lt;a href="https://scholar.google.com/citations?user=XTLv1v8AAAAJ"&gt;Iordanis Fostiropoulos&lt;/a&gt;, and
&lt;a href="https://viterbi.usc.edu/directory/faculty/Bogdan/Paul"&gt;Paul Bogdan&lt;/a&gt;
deals with an ever-present problem, viz. the representation of
topological features. Practitioners already know that the common
persistence diagram or persistence barcode representations are fraught
with certain shortcomings, such as the computational complexity of their
comparison or how to deal with features of &lt;em&gt;infinite persistence&lt;/em&gt;.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The approach by Kyriakis et al. solves this issue by an ingenious trick:
they learn a hyperbolic representation, in which features of infinite
persistence are put close to the boundary of the respective
representation. This neatly corresponds to such features approaching an
infinite distance to other features of finite persistence. The approach
itself is simple, which I appreciate, involving merely a certain
parametrisation of maps into hyperbolic space. I have not seen
applications of this approach &amp;lsquo;in the wild&amp;rsquo; yet, but I reckon that this
might make a good addition to everyone&amp;rsquo;s toolbox.&lt;/p&gt;
&lt;p&gt;On a more abstract level, this paper exemplifies the growing interest of
the ML community for &amp;rsquo;non-standard&amp;rsquo; spaces. If such endeavours continue
to prove successful, maybe the established paradigm of &amp;lsquo;Euclidean spaces
should be enough for everybody&amp;rsquo; will be toppled.&lt;/p&gt;
&lt;h1 id="zhou-et-al-evaluating-the-disentanglement-of-deep-generative-models-through-manifold-topology"&gt;Zhou et al.: Evaluating the Disentanglement of Deep Generative Models through Manifold Topology&lt;/h1&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="Evaluating the Disentanglement of Deep Generative Models through Manifold Topology" src="https://bastian.rieck.me/images/iclr21_zhou.png" width="500" /&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;Last, but certainly not least, I want to briefly present &lt;a href="https://openreview.net/pdf?id=djwS0m4Ft_A"&gt;Evaluating the Disentanglement of Deep Generative Models through Manifold Topology&lt;/a&gt;
by &lt;a href="https://sharonzhou.me"&gt;Sharon Zhou&lt;/a&gt;, &lt;a href="https://zelikman.me"&gt;Eric Zelikman&lt;/a&gt;, &lt;a href="https://scholar.google.com/citations?user=8BjErXQAAAAJ"&gt;Fred Lu&lt;/a&gt;
&lt;a href="https://www.andrewng.org"&gt;Andrew Ng&lt;/a&gt;, &lt;a href="http://math.stanford.edu/~gunnar"&gt;Gunnar Carlsson&lt;/a&gt;, and
&lt;a href="https://cs.stanford.edu/~ermon"&gt;Stefano Ermon&lt;/a&gt;. This paper is bound to
catch many more eyes, because the author list is highly notable,
containing Gunnar Carlsson, one of the founders of TDA, and Andrew Ng, one of the
&amp;rsquo;evangelists&amp;rsquo; of modern ML. Maybe this will put TDA on the radar of more
ML researchers.&lt;/p&gt;
&lt;p&gt;In any case, the paper discusses a timely topic, viz.  the
quantification of &lt;em&gt;disentanglement&lt;/em&gt; of models. Briefly put, disentangled
representations refer to learned representations in which individual
latent variables correspond to separate factors of a model. The
classical example is that a model containing rotations and scale changes
should ideally be described in a factorised representation by two
&amp;lsquo;orthogonal&amp;rsquo; factors, making it possible to control either one of these
aspects individually. The paper now builds on the previous ICML
publication &lt;a href="http://proceedings.mlr.press/v80/khrulkov18a.html"&gt;&lt;em&gt;Geometry Score: A Method For Comparing Generative
Adversarial
Networks&lt;/em&gt;&lt;/a&gt;, which
presents a general method for comparing the topology of distributions.
The paper by Zhou et al. now measures topological (dis)similarity across
the latent dimensions of the manifold, such that all submanifolds may be
clustered according to it.&lt;/p&gt;
&lt;p&gt;I am excited about such a method because it presents a compelling case
for the utility of topological features. Future work in this area should
try to move from the purely &lt;em&gt;observational&lt;/em&gt; to the &lt;em&gt;interventional&lt;/em&gt;,&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;
though, and enforce such disentanglement automatically via topology.&lt;/p&gt;
&lt;h1 id="summary"&gt;Summary&lt;/h1&gt;
&lt;p&gt;Another year, another conference, another set of papers. As our
&lt;a href="https://gt-rl.github.io/"&gt;Workshop on Geometrical and Topological Representation
Learning&lt;/a&gt; at ICLR 2021 demonstrated, there&amp;rsquo;s
a growing interest in such methods, and interest in TDA is growing. I am
very glad to see that our methods even found a receptive audience at
ICLR, arguably one of the conferences that has a penchant for &amp;lsquo;deep
learning&amp;rsquo; approaches first and foremost.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; We still have numerous
challenges to tackle for the future, but I remain an eternal optimist
and am confident that we will make progress!&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Another example of this concept is our paper &lt;a href="http://proceedings.mlr.press/v119/moor20a.html"&gt;&lt;em&gt;Topological Autoencoders&lt;/em&gt;&lt;/a&gt;, presented at ICML 2020.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;These features are often also dubbed &amp;rsquo;essential,&amp;rsquo; but care must be
taken not to misinterpret this. They are only essential because of
the choice of simplicial complex, typically.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;This is a categorisation we introduced in our recent &lt;a href="https://www.frontiersin.org/articles/10.3389/frai.2021.681108/full"&gt;survey on topological machine learning&lt;/a&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;This is the whole reason why the conference was founded in the
first place. I am not criticising its focus, just expressing my
pleasant surprise about the fact that it accepts TDA papers.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Sat, 03 Jul 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/iclr_topology_roundup/</guid></item><item><title>Problems, then Solutions</title><link>https://camhashemi.com/essays/problems-then-solutions/</link><description>&lt;p&gt;Real-world problems have an infinite number of solutions.&lt;/p&gt;&lt;p&gt;These problems also have an infinite number of factors determining a solution&amp;rsquo;s quality.&lt;/p&gt;&lt;p&gt;But each solution satisfies those factors in just one way.&lt;/p&gt;&lt;p&gt;Over time, problems change, the measures of quality change, and the optional solution changes with them.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Therefore, it&amp;rsquo;s more useful to understand a problem than any one solution.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This may seem obvious, but we&amp;rsquo;re prone to become fixated on solutions.&lt;/p&gt;&lt;p&gt;Like a tiger stalking its prey, we should be fixated on problems.&lt;/p&gt;&lt;p&gt;A tiger fixated on a solution will be caught off-guard when the problem moves in an unexpected way.&lt;/p&gt;&lt;p&gt;A tiger fixated on problems moves with them.&lt;/p&gt;&lt;p&gt;When the critical moment appears, the tiger is prepared to make the critical strike.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;In proposals, &lt;strong&gt;a demonstrated understanding of the problem space is more valuable than the detailed specification of a single solution.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The problem space is all of the factors that go into a problem, &lt;em&gt;across time&lt;/em&gt;. In the real-world, problems change over time, and priorities change over time, so value of any one solution changes as well. Fragile solutions break as soon as the problem shifts. Flexible solutions can handle change, and flexible strategies have an array of solutions at their disposal.&lt;/p&gt;&lt;p&gt;Too often, we get caught up in our favorite solution. Especially when it&amp;rsquo;s &lt;em&gt;our&lt;/em&gt; solution. Confirmation bias takes over. We start to dream about our success, and villainize any ideas that endanger that dream. I&amp;rsquo;ve seen countless proposals dragged into the ground by people willing to die for their pet solution. And I&amp;rsquo;ve died on enough of those hills myself.&lt;/p&gt;&lt;p&gt;Given the problem space, there&amp;rsquo;s a solution space. The solution space holds all of the solutions we can deploy at any one time, but also all of the possible solutions we can deploy &lt;em&gt;across time&lt;/em&gt;. Given that space, we can create strategies that are not only capable of solving the problem today, but across time as the problem moves throughout the problem space.&lt;/p&gt;&lt;p&gt;The best solutions have an evolutionary aspect to them. They start out as one thing; but as time passes, they&amp;rsquo;re capable of evolving to become more robust, precise, and better-fitting to the problem. When that&amp;rsquo;s not possible, adaptive strategies are also great. This means we can start with one solution today, but completely drop it and move to another soultion once the problem changes. We&amp;rsquo;re not tied to our pet solutions at all; we&amp;rsquo;re rather ready to kill them as soon as they become suboptimal.&lt;/p&gt;&lt;p&gt;Problem-solving is like martial arts. Amateurs dream of the one-punch knockout that sends their career into stardom. But professionals have a variety of knockout moves, so they instead watch their opponent for opportunities. If one movement fails, they have a backup plan. If their opponent exposes something unexpected, they adapt. And like a fighting opponent, problems don&amp;rsquo;t sit still. Problems adapt, priorities change over time, and winners adapt with them.&lt;/p&gt;</description><author>Cam Hashemi</author><pubDate>Sat, 03 Jul 2021 14:05:42 GMT</pubDate><guid isPermaLink="true">https://camhashemi.com/essays/problems-then-solutions/</guid></item><item><title>Evangelion: 3.0 You Can (Not) Redo</title><link>https://olshansky.info/movie/evangelion_3.0_you_can_not_redo/</link><description>Olshansky's review of Evangelion: 3.0 You Can (Not) Redo</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 03 Jul 2021 11:28:23 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/evangelion_3.0_you_can_not_redo/</guid></item><item><title>Unicode data file compression: achieving 40-70% reduction over gzip alone</title><link>https://devlog.hexops.org/2021/unicode-data-file-compression/</link><description>&lt;p&gt;A little story about how writing a domain-specific compression algorithm in a few days can sometimes yield big benefits, why it&amp;rsquo;s sometimes worth giving it a shot, and how to tell when you should try. Note: this is about Unicode spec data files, not general purpose text compression.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#background"&gt;Background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#problem"&gt;Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#investigation"&gt;Investigation&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#binary-encoding"&gt;Binary encoding?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#differential-encodingcompression"&gt;Differential encoding/compression?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#go-implementation"&gt;Go implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#zig-implementation"&gt;Zig implementation&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#differential-encoding-state-machine"&gt;Differential encoding state machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#a-stream-of-op-codes"&gt;A stream of op codes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#iteratively-finding-the-most-lucrative-opcodes"&gt;Iteratively finding the most lucrative opcodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#a-stream-of-opcodes-for-a-state-machine-a-natural-progression-from-a-binary-format"&gt;A stream of opcodes for a state machine: a natural progression from a binary format?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#results-better-than-gzipbrotli-and-even-better-with-them"&gt;Results? Better than gzip/brotli; and even better &lt;em&gt;with&lt;/em&gt; them!&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#why-test-with-gzipbrotli-but-not-others"&gt;Why test with gzip/brotli but not others?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#how-complex-is-the-implementation"&gt;How complex is the implementation?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#notable-mention"&gt;Notable mention&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;Two weeks ago, I began using &lt;a href="https://github.com/jecolon/ziglyph"&gt;Ziglyph&lt;/a&gt; (&amp;ldquo;Unicode processing with Zig, and a UTF-8 string type: Zigstr.&amp;rdquo;) - an awesome library by &lt;a href="https://github.com/jecolon"&gt;@jecolon&lt;/a&gt;, for grapheme cluster sorting in &lt;a href="https://github.com/hexops/zorex"&gt;Zorex, an omnipotent regexp engine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t personally have any prior experience working with the lower level details of Unicode, or compression algorithms for that matter.&lt;/p&gt;
&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;
&lt;p&gt;As I stumbled into the wondrous world that is Unicode text sorting (see also my article: &lt;a href="https://devlog.hexops.org/2021/unicode-sorting-why-browsers-added-special-emoji-matching"&gt;Unicode sorting is hard &amp;amp; why browsers added special emoji matching to regexp&lt;/a&gt;) and began using Ziglyph, I came across an issue: the standard Unicode collation algorithm, which Ziglyph implements, depends on some large Unicode data tables for normalization and sort keys - even gzipped these were fairly large:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hexops-mac:zorex emidoots$ du -sh asset/*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;308K asset/uca-allkeys.txt.gz
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;260K asset/ucd-UnicodeData.txt.gz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These file sizes may seem small, but one of my goals is to make Zorex a real competitor to e.g. a browser&amp;rsquo;s native regexp engine. That&amp;rsquo;s challenging because WebAssembly bundle sizes matter &lt;em&gt;a lot&lt;/em&gt; in that context, and using the browser&amp;rsquo;s regexp implementation is virtually free.&lt;/p&gt;
&lt;h2 id="investigation"&gt;Investigation&lt;/h2&gt;
&lt;p&gt;I set out to try and reduce the size of these data files. First I &lt;a href="https://github.com/jecolon/ziglyph/issues/3"&gt;opened an issue and asked&lt;/a&gt; if anyone else had thoughts around reducing the size of this data. The author of Ziglyph &lt;a href="https://github.com/jecolon"&gt;@jecolon&lt;/a&gt; is awesome and readily had some ideas and was able to reduce the two files substantially by removing unnecessary data (such as comments, etc.)&lt;/p&gt;
&lt;p&gt;Curious how much further we could go, I kept squinting at the data files (warning: large):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.unicode.org/Public/UCA/latest/allkeys.txt"&gt;http://www.unicode.org/Public/UCA/latest/allkeys.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.unicode.org/Public/UNIDATA/UnicodeData.txt"&gt;http://www.unicode.org/Public/UNIDATA/UnicodeData.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="binary-encoding"&gt;Binary encoding?&lt;/h3&gt;
&lt;p&gt;My first thoughts were that a binary encoding would likely reduce the size a lot. I pulled in some help from Hobbyist reverse engineer &lt;a href="https://github.com/Andoryuuta"&gt;@Andoryuuta&lt;/a&gt; and he got started on a binary encoding for UnicodeData.txt based on the spec. With that, he was able to reduce the original 1.9M allkeys.txt file down to 250K (125K gzipped) - quite a win.&lt;/p&gt;
&lt;h3 id="differential-encodingcompression"&gt;Differential encoding/compression?&lt;/h3&gt;
&lt;p&gt;My secondary thought was that, scrolling through these data files it was obvious most entries were derived from prior entries. Many entries were long runs of data where the next entry had the same value, plus a small increment. For example, at the start of the &lt;code&gt;allkeys.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;0000 ; [.0000.0000.0000] # NULL (in ISO 6429)
0001 ; [.0000.0000.0000] # START OF HEADING (in ISO 6429)
0002 ; [.0000.0000.0000] # START OF TEXT (in ISO 6429)
0003 ; [.0000.0000.0000] # END OF TEXT (in ISO 6429)
0004 ; [.0000.0000.0000] # END OF TRANSMISSION (in ISO 6429)
0005 ; [.0000.0000.0000] # ENQUIRY (in ISO 6429)
0006 ; [.0000.0000.0000] # ACKNOWLEDGE (in ISO 6429)
0007 ; [.0000.0000.0000] # BELL (in ISO 6429)
0008 ; [.0000.0000.0000] # BACKSPACE (in ISO 6429)
000E ; [.0000.0000.0000] # SHIFT OUT (in ISO 6429)
000F ; [.0000.0000.0000] # SHIFT IN (in ISO 6429)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Of course, not all sections are so sequential. Many sections are a bit more arbitrary:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;FF9A ; [.4304.0020.0012] # HALFWIDTH KATAKANA LETTER RE
32F9 ; [.4304.0020.0013] # CIRCLED KATAKANA RE
3355 ; [.4304.0020.001C][.42FB.0020.001C] # SQUARE REMU
3356 ; [.4304.0020.001C][.430A.0020.001C][.42EE.0020.001C][.42E3.0020.001C][.0000.0037.001C][.430A.0020.001C] # SQUARE RENTOGEN
308D ; [.4305.0020.000E] # HIRAGANA LETTER RO
31FF ; [.4305.0020.000F] # KATAKANA LETTER SMALL RO
30ED ; [.4305.0020.0011] # KATAKANA LETTER RO
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Still, there are obvious patterns one can see in the way these values change.&lt;/p&gt;
&lt;h3 id="go-implementation"&gt;Go implementation&lt;/h3&gt;
&lt;p&gt;I did a quick hacky Go implementation of differential encoding on these files to see how well that would work. The results were pretty good, and already beat just &lt;code&gt;gzip -9&lt;/code&gt; compression of the files:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Original&lt;/th&gt;
&lt;th&gt;Original + &lt;code&gt;gzip -9&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;My compression&lt;/th&gt;
&lt;th&gt;My compression + &lt;code&gt;gzip -9&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Decompositions.txt&lt;/td&gt;
&lt;td&gt;72K&lt;/td&gt;
&lt;td&gt;28K&lt;/td&gt;
&lt;td&gt;48K&lt;/td&gt;
&lt;td&gt;12K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;allkeys-minimal.txt&lt;/td&gt;
&lt;td&gt;500K&lt;/td&gt;
&lt;td&gt;148K&lt;/td&gt;
&lt;td&gt;204K&lt;/td&gt;
&lt;td&gt;36K&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;However, because I chose to do these experiments in Go I found a number of inefficiencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There were a lot of locations where I encoded things as 8-bit unsigned integers (Go&amp;rsquo;s smallest value type) instead of a more optimal 4-bit unsigned integer. I could&amp;rsquo;ve done bit shifting, but it would&amp;rsquo;ve been annoying.&lt;/li&gt;
&lt;li&gt;There were also many places where I encoded Unicode codepoints as 32-bit unsigned integers, rather than a more optimal 21-bit unsigned integer (because valid Unicode codepoints do not exceed that range.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a real implementation, I switched over to Zig.&lt;/p&gt;
&lt;h2 id="zig-implementation"&gt;Zig implementation&lt;/h2&gt;
&lt;p&gt;Actually, two things made working on this in Zig much easier than in Go:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Zig has variable bit-width integers: I could just write &lt;code&gt;u4&lt;/code&gt; and &lt;code&gt;u21&lt;/code&gt; values instead of needing to handle bit packing within larger size integers myself. That was &lt;em&gt;nice&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;In the Zig standard library it provides:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sourcegraph.com/github.com/ziglang/zig@0.8.0/-/blob/lib/std/io/bit_writer.zig?L152-202"&gt;&lt;code&gt;std.io.BitWriter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sourcegraph.com/github.com/ziglang/zig@0.8.0/-/blob/lib/std/io/bit_reader.zig?L176-248"&gt;&lt;code&gt;std.io.BitReader&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these two features, it became incredibly easy to write the most optimal bit-packed encoding of the data.&lt;/p&gt;
&lt;p&gt;In fact, the basic uncompressed binary format &lt;a href="https://github.com/jecolon/ziglyph/pull/7/commits/7d4042d8df21cc11eaf42177c2f4d9b3afd9c4a7"&gt;was only a few lines to encode&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-zig"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;compressTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DecompFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;anytype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf_writer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bufferedWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bitWriter&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;Little&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf_writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@intCast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key_len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key_len&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@enumToInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;@bitSizeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeBits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flushBits&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf_writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="differential-encoding-state-machine"&gt;Differential encoding state machine&lt;/h3&gt;
&lt;p&gt;To handle the compression, I started out &lt;em&gt;really&lt;/em&gt; simple. First I encoded just a binary version of the data with no compression. The most important thing was to get to a point where I could start testing some theories about what would compress the data really well, and validate that it was in fact being losslessly compressed/decompressed without issues via tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-zig"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"compression_is_lossless"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Compress UnicodeData.txt -&amp;gt; Decompositions.bin
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parseFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"src/data/ucd/UnicodeData.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compressToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/data/ucd/Decompositions.bin"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Reset the raw file iterator.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Decompress the file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decompressed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decompressFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"src/data/ucd/Decompositions.bin"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decompressed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decompressed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="a-stream-of-op-codes"&gt;A stream of op codes&lt;/h3&gt;
&lt;p&gt;I settled on a really simple idea: these data files all have basically just a variable number of integers per line. And if I kept &amp;ldquo;registers&amp;rdquo; representing the current value for each integer, I could determine the difference between the past line and the subsequent one to produce a difference. If I encoded that difference as a stream of opcodes with associative data, then to decompress the file I could simply &amp;ldquo;replay&amp;rdquo; those operations based on the opcodes and then iteratively come up with more finely-specified, specific opcodes to handle specific types of data.&lt;/p&gt;
&lt;p&gt;I started out simple, really just with two opcodes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-Zig"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// A UDDC opcode for a decomposition file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Opcode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Sets all the register values with no compression.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Denotes the end of the opcode stream. This is so that we don't need to encode the total
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// number of opcodes in the stream up front (note also the file is bit packed: there may be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// a few remaining zero bits at the end as padding so we need an EOF opcode rather than say
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// catching the actual file read EOF.)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using these two opcodes, I was able to effectively encode the entire file. The &lt;code&gt;set&lt;/code&gt; opcode had some associative data which effectively expressed an entire raw, uncompressed entry in the file (one line.) This increased the file size since it was effectively just adding 4 bits (the opcode) as additional overhead.&lt;/p&gt;
&lt;h3 id="iteratively-finding-the-most-lucrative-opcodes"&gt;Iteratively finding the most lucrative opcodes&lt;/h3&gt;
&lt;p&gt;To find the most lucrative (i.e. compressed) opcodes, I printed the data I would associate with an opcode (like &lt;code&gt;set&lt;/code&gt;) and then looked for repetitions. Sometimes manually, and sometimes by e.g. piping data to a combination of &lt;code&gt;sort|uniq -c|sort -r&lt;/code&gt; to find common patterns.&lt;/p&gt;
&lt;p&gt;Since I was printing &lt;em&gt;differences&lt;/em&gt; between e.g. the current value and previous value, it was really easy to find common patterns that appeared in the file very frequently, such as specific fields incrementing by specific amounts with one field being arbitrary:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-zig"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// increments key[3] += 1; sets value.seq[0]; emits an entry.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1685 instances
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;increment_key_3_and_set_value_seq_0_and_emit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once I had narrowed down to a larger group of opcodes that more specifically represented the data, I was able to print the number of bits required to store the change in specific fields (like &lt;code&gt;value.seq[0]&lt;/code&gt;) and add even more specific opcodes to use variable bit widths:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-zig"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// increments key[3] += 1; sets value.seq[0]; emits an entry.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1685 instances
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;increment_key_3_and_set_value_seq_0_2bit_and_emit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 978 instances, 2323 byte reduction
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;increment_key_3_and_set_value_seq_0_8bit_and_emit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 269 instances, 437 byte reduction
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;increment_key_3_and_set_value_seq_0_21bit_and_emit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 438 instances
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It being a stream of opcodes was quite nice, because it allowed me to determine how much space was being consumed by a given opcode in sum and target further reducing the size of opcodes that took up the most space. It also made it really easy to find opcodes that I though &lt;em&gt;might&lt;/em&gt; help, but in practice turned out to not be that frequent. Just print them, pipe to &lt;code&gt;sort|uniq -c|sort -r&lt;/code&gt; to count them - and remove the lowest hanging fruit.&lt;/p&gt;
&lt;h3 id="a-stream-of-opcodes-for-a-state-machine-a-natural-progression-from-a-binary-format"&gt;A stream of opcodes for a state machine: a natural progression from a binary format?&lt;/h3&gt;
&lt;p&gt;I chose an opcode stream for a reason: so that I could encode some complex logic in the form of a state machine. This came in handy for the &lt;code&gt;allkeys.txt&lt;/code&gt; file in specific, as it allowed me to introduce &lt;em&gt;incrementors&lt;/em&gt; into the mix which would &lt;em&gt;increment register values by a chosen amount each iteration (value &amp;ldquo;emission&amp;rdquo;)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The final opcodes for the allkeys.txt file ended up being:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-zig"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// A UDDC opcode for an allkeys file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Opcode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Sets an incrementor for the key register, incrementing the key by this much on each emission.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 10690 instances, 13,480.5 bytes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inc_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Sets an incrementor for the value register, incrementing the value by this much on each emission.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 7668 instances, 62,970 bytes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inc_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Emits a single value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 31001 instances, 15,500.5 bytes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emit_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emit_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emit_4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emit_8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;emit_32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Denotes the end of the opcode stream. This is so that we don't need to encode the total
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// number of opcodes in the stream up front (note also the file is bit packed: there may be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// a few remaining zero bits at the end as padding so we need an EOF opcode rather than say
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// catching the actual file read EOF.)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This meant I could determine the difference in the &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; fields (what those actually are isn&amp;rsquo;t important, just that they are all minor incremental differences on the prior entry in the file) - set an &lt;em&gt;incrementor&lt;/em&gt; to do some work on each emission, such as say increment the &lt;code&gt;key&lt;/code&gt; array by &lt;code&gt;[0, 1, 5]&lt;/code&gt; each emission, and then say &amp;ldquo;now emit_32 values!&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Suddenly, instead of encoding 32 key entries (32 * 3 key values * 21 bits) I am just setting an incrementor (3 key values * 21 bits) and a single opcode to emit 32 values (3 bits).&lt;/p&gt;
&lt;p&gt;Overall, this gave me a very nice, natural-feeling progression from a &amp;ldquo;raw binary format&amp;rdquo; to something a bit more specific - a bit more &lt;em&gt;compressed.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="results-better-than-gzipbrotli-and-even-better-_with_-them"&gt;Results? Better than gzip/brotli; and even better &lt;em&gt;with&lt;/em&gt; them!&lt;/h2&gt;
&lt;p&gt;For lack of better words, I&amp;rsquo;ll call my compression algorithm here Unicode Data Differential Compression, since it&amp;rsquo;s differential and specifically for the Unicode data table files - or UDDC for short.&lt;/p&gt;
&lt;p&gt;The two files went from the original 568K (with gzip) down to just 61K (with UDDC+gzip). With this, we are able to equal or match both &lt;code&gt;gzip -9&lt;/code&gt; and &lt;code&gt;brotli -9&lt;/code&gt; on their own, AND when combined with gzip or brotli we are able to reduce by 40-70%:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Before (bytes)&lt;/th&gt;
&lt;th&gt;After (bytes)&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Decompositions.bin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;48,242&lt;/td&gt;
&lt;td&gt;19,072&lt;/td&gt;
&lt;td&gt;-60.5% (-29,170 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Decompositions.bin.br&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24,411&lt;/td&gt;
&lt;td&gt;14,783&lt;/td&gt;
&lt;td&gt;-39.4% (-9,628 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Decompositions.bin.gz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30,931&lt;/td&gt;
&lt;td&gt;15,670&lt;/td&gt;
&lt;td&gt;-49.34% (15,261 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allkeys.bin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;373,719&lt;/td&gt;
&lt;td&gt;100,907&lt;/td&gt;
&lt;td&gt;-73.0% (-272,812 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allkeys.bin.br&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;108,982&lt;/td&gt;
&lt;td&gt;44,860&lt;/td&gt;
&lt;td&gt;-58.8% (-64,122 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allkeys.bin.gz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;163,237&lt;/td&gt;
&lt;td&gt;46,996&lt;/td&gt;
&lt;td&gt;-71.2% (-116,241 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;Before represents binary format without UDDC compression.&lt;/li&gt;
&lt;li&gt;After represents binary format with UDDC compression.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.br&lt;/code&gt; represents &lt;code&gt;brotli -9 &amp;lt;file&amp;gt;&lt;/code&gt; compression&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.gz&lt;/code&gt; represents &lt;code&gt;gzip -9 &amp;lt;file&amp;gt;&lt;/code&gt; compression&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-test-with-gzipbrotli-but-not-others"&gt;Why test with gzip/brotli but not others?&lt;/h3&gt;
&lt;p&gt;I chose to compare against gzip/brotli specifically because you get those effectively for free in WebAssembly: browsers already know how to decompress those and ship with gzip/brotli decompressors - so you can use them for free without shipping any additional code.&lt;/p&gt;
&lt;h3 id="how-complex-is-the-implementation"&gt;How complex is the implementation?&lt;/h3&gt;
&lt;p&gt;The final implementation for both files is only a few hundred lines (excluding blank lines, comments, and tests):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jecolon/ziglyph/blob/main/src/collator/AllKeysFile.zig"&gt;&lt;code&gt;AllKeysFile.zig&lt;/code&gt;&lt;/a&gt;: 298 lines&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jecolon/ziglyph/blob/main/src/normalizer/DecompFile.zig"&gt;&lt;code&gt;DecompFile.zig&lt;/code&gt;&lt;/a&gt; 336 lines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have not measured produced machine code size yet, but suspect it is relatively negligible compared to the gains.&lt;/p&gt;
&lt;h2 id="notable-mention"&gt;Notable mention&lt;/h2&gt;
&lt;p&gt;I should mention that the Unicode spec, as &lt;a href="https://github.com/jecolon"&gt;@jecolon&lt;/a&gt; pointed out to me, does suggest ways to reduce sort key lengths and implement Run-length Compression:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://unicode.org/reports/tr10/#Reducing_Sort_Key_Lengths"&gt;https://unicode.org/reports/tr10/#Reducing_Sort_Key_Lengths&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unicode.org/reports/tr10/#Run-length_Compression"&gt;https://unicode.org/reports/tr10/#Run-length_Compression&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wasn&amp;rsquo;t able to locate an implementation of this (I&amp;rsquo;d be curious to compare results!) but suspect that, as the run-length compression does not fit the data as tightly, it would not compress quite as well (although would handle any major changes to the type of data in the files without requiring compression algorithm changes better.)&lt;/p&gt;
&lt;p&gt;Also of note is that their algorithm only seems to be mentioned in the context of allkeys.txt / the Unicode Collation Algorithm, not in the context of normalization/decompositions from &lt;code&gt;UnicodeData.txt&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Ask questions, stay curious, don&amp;rsquo;t be afraid to experiment even if it&amp;rsquo;s outside of your domain of expertise. You might surprise yourself and find something interesting, challenging, and worthwhile.&lt;/p&gt;</description><author>Hexops' devlog</author><pubDate>Sat, 03 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://devlog.hexops.org/2021/unicode-data-file-compression/</guid></item><item><title>Some Thoughts On Streaming Responses</title><link>http://live.julik.nl/2021/07/some-thoughts-on-streaming-responses</link><description/><author>julik live</author><pubDate>Sat, 03 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://live.julik.nl/2021/07/some-thoughts-on-streaming-responses</guid></item><item><title>Where are we going from here? Software engineering needs formal methods</title><link>https://ntietz.com/blog/future-of-software-engineering-is-formal-methods/?utm_source=atom&amp;utm_medium=feed</link><description>&lt;p&gt;The job of a software engineer is not to produce code, but to solve problems; we just happen to solve most of those problems by producing code. Ultimately, producing code is hard, and we need help. That's why GitHub's &lt;a href="https://copilot.github.com/"&gt;Copilot&lt;/a&gt; is exciting, but it's far from ideal, and it's the tip of the iceberg of what's been done and what is to come.&lt;/p&gt;
&lt;p&gt;There have been a &lt;em&gt;lot&lt;/em&gt; of hot takes on Copilot specifically, so I'm not going to get into the flaws of this specific launch very much (ethical issues, introducing bugs, etc). At the end of the day, it's pretty exciting, and it's flawed like all tools based in statistical inference are. (This is a very important area and there is a lot of room for people to make huge strides forward in HCI and UX around ML.) It's helpful because it can reduce the friction of producing code, which is a necessary but ultimately small part of the job of a software engineer. And it's only doing one part of what we do when we code!&lt;/p&gt;
&lt;p&gt;The usual process for coding (for me) looks something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Specify: Figure out what the code needs to do&lt;/li&gt;
&lt;li&gt;Implement: Write the code in question&lt;/li&gt;
&lt;li&gt;Verify: Test the code to make sure it does what it needs to&lt;/li&gt;
&lt;li&gt;Iterate: Rinse and repeat as many times as needed (incremental development, fixing bugs, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that's skipping all the work needed to gather requirements ahead of time; that's &lt;em&gt;just&lt;/em&gt; the coding part.&lt;/p&gt;
&lt;p&gt;So Copilot, and other code generation tools I've seen, handle the implementation bit: they write the code in question, and make no attempts at or guarantees around correctness or completeness. It's a starting point, and that's great. It really emphasizes, though, how much we need to focus on the specification and verification steps. If we have easy code generation available, it's &lt;em&gt;very&lt;/em&gt; easy as a human under pressure to ship code quickly to just say "looks good to me" and ship it. That's how you get subtle bugs and omissions, and in the long run that's just programming, and misses the whole engineering part.&lt;/p&gt;
&lt;p&gt;Wouldn't it just be grand if we could write a spec for some piece of code, then let the machines do the rest? I know, I know, people have been trying that for a long time and it's fraught. I'm not saying we &lt;em&gt;can&lt;/em&gt; do that, theoretically or practically, today or in ten years. But as a goal, that's really what you want: we want to solve the problem by saying "this is the solution" and then &lt;em&gt;poof&lt;/em&gt; the solution appears! To some people, Copilot will feel like exactly that magic, and that's dangerous. It skips the verification step, and I'd argue it also skips specifying what you want (because a docstring is often not very clear, ambiguous, and misses the non-functional components that are oh-so-important like performance and security).&lt;/p&gt;
&lt;p&gt;So, where does that take us? Well, we want to do engineering to solve problems. I think that means, practically speaking, we need to focus on the specification and verification steps and nail down better methods for doing that, while also working to improve the tooling for implementation (better autocomplete, code generation, etc). If we can improve the specification and verification steps, we'll get a lot more mileage out of flawed implementation tools and techniques, and we'll be able to move faster on the implementation step regardless because we'll know that we can move quickly and make mistakes since they'll get caught. Good specification and verification speed up the implementation portion while giving you better outcomes all around.&lt;/p&gt;
&lt;p&gt;The future of software engineering is leaning into formal methods and relying on formal methods to give us higher quality output.&lt;/p&gt;
&lt;p&gt;And the future is here, somewhat! There are already tools you can use to more rigorously specify and verify your code and systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLA+ is the elephant in the room, and has been used quite a bit to verify systems at &lt;a href="https://lamport.azurewebsites.net/tla/formal-methods-amazon.pdf"&gt;AWS&lt;/a&gt; and MS, among others; probably a good starting point!&lt;/li&gt;
&lt;li&gt;Property testing (things like &lt;a href="https://hypothesis.readthedocs.io/en/latest/"&gt;Hypothesis&lt;/a&gt; for Python) is also a form of formal methods that can take you &lt;em&gt;very&lt;/em&gt; far and is low hanging fruit if you already have unit tests. It lets you get higher levels of assurances while not having to fully formally verify your program.&lt;/li&gt;
&lt;li&gt;Even static types are a form of formal methods, and they're increasingly being embraced even in languages like Python and Ruby! In the future we can take it further with &lt;a href="https://en.wikipedia.org/wiki/Refinement_type"&gt;refinement types&lt;/a&gt; to get nice strong compile-time guarantees around values.&lt;/li&gt;
&lt;li&gt;Many more which I'm not aware of, because I'm new to this area. (Email me or tweet at me with recommendations!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's not to say that &lt;em&gt;all&lt;/em&gt; code will need or benefit from formal methods; some one-off scripts or simple web apps can be crafted without it, and would be too expensive using formal methods. That's fine, and that indicates that there's a split in our field: software engineering vs. software development. This rift will probably become more clear over time, as well, as we figure out ways of talking about the engineering side of software engineering and better ways of specifying and verifying our programs.&lt;/p&gt;
&lt;p&gt;I'm leaning into this, personally. I tend to work on things where correctness, stability, reliability, and security are all very important, so formal methods give a way to improve this work and deliver on those values. First on my learning list is TLA+.&lt;/p&gt;
&lt;p&gt;If you have any experience with this stuff, have recommendations of what to learn, or just want to chat about it, reach out to me &lt;a href="mailto:me@ntietz.com"&gt;by email&lt;/a&gt; or &lt;a href="https://twitter.com/_ntietz"&gt;on Twitter&lt;/a&gt;. I'd love to chat about it!&lt;/p&gt;</description><author>ntietz.com blog - technically a blog</author><pubDate>Sat, 03 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ntietz.com/blog/future-of-software-engineering-is-formal-methods/?utm_source=atom&amp;utm_medium=feed</guid></item><item><title>I had to change my GPG key</title><link>https://thatxliner.github.io/posts/i-had-to-change-my-gpg-key/</link><description>And it wasn't my fault.</description><author>ThatXliner's Blog</author><pubDate>Sat, 03 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://thatxliner.github.io/posts/i-had-to-change-my-gpg-key/</guid></item><item><title>How civilizations can collapse</title><link>https://stop.zona-m.net/2021/07/how-civilizations-can-collapse/</link><description>&lt;p&gt;It&amp;rsquo;s simpler than you may think. Ditto for AVOIDING collapse.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 02 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/how-civilizations-can-collapse/</guid></item><item><title>Forever Series</title><link>https://arunmani.in/library/forever-series/</link><description>A modern love tale with suspense and mystery.</description><author>Arun Mani</author><pubDate>Fri, 02 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/forever-series/</guid></item><item><title>In a Grove</title><link>https://arunmani.in/library/in-a-grove/</link><description>Awesome mystery surrounding a murder.</description><author>Arun Mani</author><pubDate>Fri, 02 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/in-a-grove/</guid></item><item><title>Strange Planet</title><link>https://arunmani.in/library/strange-planet/</link><description>After reading, you realize that Earth is really strange.</description><author>Arun Mani</author><pubDate>Fri, 02 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/strange-planet/</guid></item><item><title>Write Coding Videos in Markdown</title><link>https://www.youtube.com/watch?v=EYyw4oyAJgY</link><description>A web app you can clone to create programming tutorials</description><author>Rodrigo Pombo</author><pubDate>Fri, 02 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.youtube.com/watch?v=EYyw4oyAJgY</guid></item><item><title>Hope Makes A Great Breakfast by Francis Bacon</title><link>https://ho.dges.online/words/commonplace/hope-makes-a-great-breakfast-by-francis-bacon/</link><description>&lt;p&gt;![[2022-02-07.jpg]]&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Hope is a good breakfast, but it is a bad supper.”&lt;/p&gt;
&lt;/blockquote&gt;</description><author>ho.dges.online</author><pubDate>Fri, 02 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ho.dges.online/words/commonplace/hope-makes-a-great-breakfast-by-francis-bacon/</guid></item><item><title>Growing a cherry tree on a north-facing balcony 2019-2021</title><link>https://michael-lewis.com/posts/growing-a-cherry-tree-on-a-north-facing-balcony-2019-2021/</link><description>&lt;h2 id="2019"&gt;
  2019
  &lt;a class="heading-link" href="#2019"&gt;
    &lt;i class="fa-solid fa-link" title="Link to heading"&gt;&lt;/i&gt;
    &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="the-origin-story"&gt;
  The origin story
  &lt;a class="heading-link" href="#the-origin-story"&gt;
    &lt;i class="fa-solid fa-link" title="Link to heading"&gt;&lt;/i&gt;
    &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I like cherries, and for many years I&amp;rsquo;ve cultivated the notion that I&amp;rsquo;ll have &amp;ldquo;made it&amp;rdquo; when I can sit in my own garden underneath my very own cherry tree. Unfortunately, the realities of city-living have meant that I&amp;rsquo;ve been in flats for the past few decades. But I do now have a balcony (somewhat optimistically described as a &amp;ldquo;roof terrace&amp;rdquo; in the estate agent brochure), albeit a north-facing one.&lt;/p&gt;</description><author>Michael Ian Lewis</author><pubDate>Fri, 02 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://michael-lewis.com/posts/growing-a-cherry-tree-on-a-north-facing-balcony-2019-2021/</guid></item><item><title>Spanish section added</title><link>https://shadow.sombragris.org/?p=1136</link><description>You may remember that I wondered about adding a Spanish language section to this. Well, no more wondering: I just added a Spanish section to this site. Now you can click on the menu above to access my Spanish languange content. I just want to add that I have no plans to have translations of...</description><author>The Grey Shadow</author><pubDate>Fri, 02 Jul 2021 02:06:06 GMT</pubDate><guid isPermaLink="true">https://shadow.sombragris.org/?p=1136</guid></item><item><title>YOLOv5 object detection experiments</title><link>https://bytepawn.com/yolov5-object-detection-experiments.html</link><description>&lt;p&gt;I run object detection experiments with pre-trained YOLOv5 models. &lt;br /&gt;&lt;br /&gt;&lt;img alt="YOLO object detection example" src="/images/yolov5_webcam1.jpg" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Fri, 02 Jul 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/yolov5-object-detection-experiments.html</guid></item><item><title>TIL how to alter gitlab-ci include-directives</title><link>https://www.zufallsheld.de/2021/07/01/til-how-to-alter-gitlab-ci-include-directives/</link><description>&lt;p&gt;We have a set of common pipeline jobs that are shared among different projects in Gitlab. We include them like&amp;nbsp;this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;include:
  - project: 'shared/gitlab/linting'
    ref: master
    file:
      - markdown.yml
      - ansible.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you do this, these included jobs get executed with the runner that is defined in the …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Thu, 01 Jul 2021 22:47:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/07/01/til-how-to-alter-gitlab-ci-include-directives/</guid></item><item><title>Windows 11? Another wave of pollution</title><link>https://stop.zona-m.net/2021/07/windows-11-another-wave-of-pollution/</link><description>&lt;p&gt;Just like the previous versions, but in a worst moment.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 01 Jul 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/07/windows-11-another-wave-of-pollution/</guid></item><item><title>Colab: Connect to Google Drive</title><link>https://blog.lukesalamone.com/posts/connect-to-colab/</link><description>&lt;p&gt;Here&amp;rsquo;s how to connect your Google Colab notebook to your Drive directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;from google.colab import drive
drive.mount('/content/gdrive')
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Follow the prompts from there. That is all.&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Thu, 01 Jul 2021 06:58:18 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/connect-to-colab/</guid></item><item><title>Stargazer</title><link>https://github.com/pomber/stargazer</link><description>Generate a video with the stars from a GitHub repository</description><author>Rodrigo Pombo</author><pubDate>Thu, 01 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://github.com/pomber/stargazer</guid></item><item><title>Things I wish Git had: Commit groups</title><link>http://blog.danieljanus.pl/commit-groups/</link><description>&lt;div&gt;&lt;h2 id="intro"&gt;Intro&lt;/h2&gt;&lt;p&gt;Everyone &lt;sup class="sidenote-ref"&gt;1&lt;/sup&gt; and their dog &lt;sup class="sidenote-ref"&gt;2&lt;/sup&gt; loves Git. I know I do. It works, it’s efficient, it has a brilliant data model, and it sports &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Rerere"&gt;every feature under the sun&lt;/a&gt;. In 13 years of using it, I’ve never found myself needing a feature it didn’t have. Until recently.&lt;/p&gt;&lt;p&gt;But before I tell you about it, let’s talk about GitHub.&lt;/p&gt;&lt;p&gt;There are three groups of GitHub users, distinguished by how they prefer to merge pull requests:&lt;/p&gt;&lt;img src="/img/blog/3-groups-of-gh-users.png" /&gt;
&lt;p&gt;Merge commit, squash, or rebase? There’s no single best answer to that question. A number of factors are at play in choosing the merge strategy: the type of the project, the size, workflow and preferences of the team, business considerations, and so on. You probably have your own preference if you’ve used GitHub to collaborate with a team.&lt;/p&gt;&lt;p&gt;I’ll talk for a while about the pros and cons of each approach. But first, let’s establish a setting. Imagine that your project has a &lt;code&gt;main&lt;/code&gt; branch, from which a &lt;code&gt;feature&lt;/code&gt; branch was created off at one point. Since then, both branches have seen developments, and now after &lt;code&gt;feature&lt;/code&gt; has undergone reviews and testing, it’s ready to be merged back to &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;&lt;img src="/img/blog/orig.svg" /&gt;
&lt;h2 id="create-a-merge-commit"&gt;Create a merge commit&lt;/h2&gt;&lt;p&gt;Merge commits are the original answer that Git has to combining changes. A merge commit has two or more parents and brings in all the changes from them and their ancestors:&lt;/p&gt;&lt;img src="/img/blog/merge-commit.svg" /&gt;
&lt;p&gt;In this example, Git has created a new commit, number 9, that merges commits 6 and 8. The branch &lt;code&gt;main&lt;/code&gt; now points to that new commit, and so contains all changes in the range 1–8.&lt;/p&gt;&lt;p&gt;Merge commits are extremely versatile and scale well, especially for complicated workflows with multiple maintainers, each responsible for different part of the code; for example, they’re pervasively used by the Linux kernel developers. However, for small, agile teams (especially in the business context), they can be overkill and pose potential problems.&lt;/p&gt;&lt;p&gt;In such a team, you typically have one eternal branch, from which production releases are made, and to which people merge changes from short-lived feature branches. In such a setting, it’s hard to tell how the history of a project has progressed. &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;GitFlow&lt;/a&gt;, a popular way of working with Git, advocates merge commits everywhere, and &lt;a href="https://www.endoflineblog.com/gitflow-considered-harmful"&gt;people are struggling with it&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’ll refer you to the visual argument from that last post:&lt;/p&gt;&lt;img src="/img/blog/gitflow-mess.png" /&gt;
&lt;p&gt;Setting aside the fact that this history is littered with merge commits, the author makes a point that with this kind of an entangled graph, it’s practically impossible to find anything in it. Whether that’s true or not I’ll leave for you to decide, but there’s definitely a case for linear history there.&lt;/p&gt;&lt;p&gt;There’s another, oft-overlooked quirk here. Quick: look again at the second image above, the one with merge commit number 9. Can you tell, from the image alone, which commit was the tip of &lt;code&gt;main&lt;/code&gt; before the merge happened? Surely it must be 8, because it’s on the gray line, right?&lt;/p&gt;&lt;p&gt;Yeah: on the image. But when you look at the merge commit itself, it’s not that obvious. Under the hood, all the commit really says is:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Merge: 8 6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So it tells you that these two parents have been merged together, &lt;em&gt;but it doesn’t tell you which one used to be &lt;code&gt;main&lt;/code&gt;&lt;/em&gt;. You might guess 8, because it’s the leftmost one, but you don’t know for sure. (Remember, branches in Git are just pointers to commits.) The only way (that I know of) to be sure is to use the &lt;a href="https://git-scm.com/docs/git-reflog"&gt;reflog&lt;/a&gt;, but that is ephemeral: Git occassionally prunes old entries from reflogs.&lt;/p&gt;&lt;p&gt;So this prevents you from being able to confidently answer questions such as: “which features were released over the given time period?”, or “what was the state of &lt;code&gt;main&lt;/code&gt; as of a given date?”.&lt;/p&gt;&lt;p&gt;That’s also why you can’t &lt;code&gt;git revert&lt;/code&gt; a merge commit—that is, unless you tell Git which of the parent commits you want to keep and which to discard.&lt;/p&gt;&lt;h2 id="squash-and-merge"&gt;Squash and merge&lt;/h2&gt;&lt;p&gt;In the merge commit-based approach, we don’t rewrite history: once a commit is made, it stays; repository only grows by accretion. In contrast, the other two approaches use Git’s facilities for rewriting history. As we’ll see, the fundamentals are the same: where they differ is commit granularity.&lt;/p&gt;&lt;p&gt;Coming back to our example: when squashing, we mash together the changes introduced by commits 4, 5, and 6 into a single commit (“S”), and then replay that commit on top of &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;&lt;img src="/img/blog/squash-and-merge.svg" /&gt;
&lt;p&gt;The &lt;code&gt;feature&lt;/code&gt; branch is still there, but I didn’t include it on this picture because it’s no longer relevant—it typically gets deleted upon merge (which, as we will see, might not actually be a good idea).&lt;/p&gt;&lt;p&gt;There’s a lot to like about this approach, and &lt;a href="https://blog.dnsimple.com/2019/01/two-years-of-squash-merge/"&gt;some teams&lt;/a&gt; &lt;a href="https://christopher.xyz/2020/07/13/squash-merge.html"&gt;advocate for it&lt;/a&gt;. The biggest and most obvious benefit is likely that &lt;em&gt;the history becomes very legible&lt;/em&gt;. It’s linear and there’s a one-to-one correspondence between commits on &lt;code&gt;main&lt;/code&gt; and pull requests (and, mostly, either features or bugfixes). Such a history can be of great help in project management: it becomes very easy to answer the questions which were nigh impossible to answer in the merge-commit approach.&lt;/p&gt;&lt;h2 id="rebase-and-merge"&gt;Rebase and merge&lt;/h2&gt;&lt;p&gt;This situation is similar to the previous one, except that we don’t squash commits 4–6 together. Instead, we directly replay them on top of &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;&lt;img src="/img/blog/rebase-and-merge.svg" /&gt;
&lt;p&gt;Let me start with a long digression. You might guess, from the GitHub screenshot at the top of this post, that I’m in this camp, and you’d be right. In fact, I used to squash and merge feature branches, but I switched to the rebase-and-merge approach after introducing probably the single biggest improvement to the quality of my work over recent years:&lt;/p&gt;&lt;p&gt;I started writing &lt;a href="https://chris.beams.io/posts/git-commit/"&gt;meaningful commit messages&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;In the not-too-distant past, my commit messages used to be one-liners, as evidenced, for example, in the &lt;a href="https://github.com/nathell/skyscraper/commits/master"&gt;history of Skyscraper&lt;/a&gt;. These first lines haven’t changed much, but now I strive to augment them with explanation of &lt;em&gt;why&lt;/em&gt; the change is being made. When it fixes a bug, I explain what was causing it and how the change makes the bug go away; when it implements a feature, I highlight the specifics of the implementation. I might not write more code these days, but I certainly write more prose: it’s not uncommon for me to write two or three paragraphs about a +1/−1 change.&lt;/p&gt;&lt;p&gt;So my commit messages now look like this (I’m taking a recent random example from the &lt;a href="https://iamfy.co"&gt;Fy!&lt;/a&gt; app’s repo):&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs text"&gt;app/tests: allow to mock config

Tests expected the code-push events to fire, but now that I’ve
disabled CP in dev, and the tests are built with the dev aero profile,
they’d fail.

This could have been fixed by building them with AERO_PROFILE=staging
in CI, but it doesn’t feel right: I think tests shouldn’t depend on
varying configuration. If a test requires a given bit of configuration
to be present, it’s better to configure it that way explicitly.

Hence this commit. It adds a wrap-config mock and a corresponding
:extra-config fixture, which, when present (and it is by default),
will merge the value onto generated-config.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I’m very conscious about having a clean history. I’m aiming for each commit to be small (with the threshold at approximately +20/−20 LOCs) and introduce a coherent, logical change.&lt;/p&gt;&lt;p&gt;That’s not to say I always &lt;em&gt;develop&lt;/em&gt; that way, of course. If you looked at a &lt;code&gt;git log&lt;/code&gt; of my work-in-progress branch, chances are you’d see something like this:&lt;/p&gt;&lt;pre&gt;&lt;code class="hljs text"&gt;5d64b71 wip
392b1e0 wip
0a3ad89 more wip
3db02d3 wip
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But before declaring the PR ready to review, I’ll throw &lt;em&gt;this&lt;/em&gt; history away (by &lt;code&gt;git reset --mixed $(git merge-base feature main)&lt;/code&gt;) and re-commit the changes, dividing them into logical units and writing the rationales, bit by bit.&lt;/p&gt;&lt;p&gt;The net result of rigorously applying this practice is that&lt;/p&gt;&lt;p&gt;&lt;strong&gt;you can do &lt;code&gt;git annotate&lt;/code&gt; anywhere, and learn about why any line of code in the codebase is the way it is.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I can’t emphasize enough how huge, huge impact for the developer’s wellbeing this has. These commits messages, when I read them back weeks or months later, working on something different but related, almost read as little love letters from me-in-the-past to me-now. They reduce the all-important WTFs/minute metric to zero.&lt;/p&gt;&lt;img src="/img/blog/wtfm.jpg" /&gt;
&lt;p&gt;They’re also an aid in reviewing code. My PR notes usually say “please read each commit in isolation.” I’ve found it easier to follow a PR when it tries to tell a story, and each commit is a milestone down that road.&lt;/p&gt;&lt;p&gt;Ending the digression: can you see why I prefer rebase-and-merge over squash-and-merge? Because, all the benefits notwithstanding, squashing &lt;em&gt;irrevocably loses context&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Now, instead of each line being a result of a small, +20/−20 change, you can only tell that it’s part of a set of such changes — maybe ten of them, maybe fifty. You don’t know. Sure you can go look in the original branch, but it’s an overhead, and what if it’s been deleted?&lt;/p&gt;&lt;p&gt;So yeah. Having those love letters all in place, each carefully placed and not glued to others, is just too much of a boon to let go. But it’s not to say that rebasing-and-merging is without downsides.&lt;/p&gt;&lt;p&gt;For example, it’s again hard to tell how many features were deployed over a given period of time. More troublesomely, it’s harder to revert changes: typically you want to operate on a feature level there. With squash-and-merge, it takes one &lt;code&gt;git revert&lt;/code&gt; to revert a buggy feature. With rebase-and-merge, you need to know the range.&lt;/p&gt;&lt;p&gt;Worse yet: it’s more likely for a squashed-and-merged commit to be cleanly undone (or cherry-picked) than for a series of small commits. (I sometimes deliberately commit wrong or half-baked approaches that are changed in subsequent commits, just to tell the story more convincingly, and it’s possible that each of these changes individually causes trouble but that they cancel each other in squash.)&lt;/p&gt;&lt;p&gt;So I’m not completely happy with either of the three approaches. Which finally brings me to my preferred fourth approach, one that Git (yet?) doesn’t allow for:&lt;/p&gt;&lt;h2 id="rebase,-group-and-merge"&gt;Rebase, group and merge&lt;/h2&gt;&lt;p&gt;You know the “group” facility of vector graphics programs? You draw a couple of shapes, you group them together, and then you can apply transformations to the entire group at once, operating on it as if it were an atomic thing. But when need arises, you can “ungroup” it and look deeper.&lt;/p&gt;&lt;p&gt;That’s because sometimes there’s a need to have a “high-level” view of things, and sometimes you need to delve deeper. Each of these needs is valid. Each is prompted by different circumstances that we all encounter.&lt;/p&gt;&lt;p&gt;I’d love to see that same idea applied to Git commits. In Git, a commit group might just be a named and annotated range of commits: &lt;code&gt;feature-a&lt;/code&gt; might be the same as &lt;code&gt;5d64b71..3db02d3&lt;/code&gt;. Every Git command that currently accepts commit ranges could accept group names. I envision groups to have descriptions, so that &lt;code&gt;git log&lt;/code&gt;, &lt;code&gt;git blame&lt;/code&gt;, etc could take &lt;code&gt;--grouped&lt;/code&gt; or &lt;code&gt;--ungrouped&lt;/code&gt; options and act appropriately.&lt;/p&gt;&lt;p&gt;Obviously, details would need to be fleshed out (can groups overlap? can groups be part of other groups?), and I’m not that familiar with Git innards to say with confidence that it’s doable. But the more I think about it, the more sound the idea seems to me.&lt;/p&gt;&lt;p&gt;I think creating a group when doing a rebase-and-merge could bring together the best of all three worlds, so that we can have all our cakes and eat them too.&lt;/p&gt;&lt;hr /&gt;&lt;div class="footnote"&gt;&lt;p&gt;&lt;sup class="footnote-ref"&gt;1&lt;/sup&gt; &lt;a href="https://www.mercurial-scm.org/"&gt;Well,&lt;/a&gt; &lt;a href="https://fossil-scm.org/"&gt;almost&lt;/a&gt; &lt;a href="https://pijul.org/"&gt;everyone&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;div class="footnote"&gt;&lt;p&gt;&lt;sup class="footnote-ref"&gt;2&lt;/sup&gt; It’s Dog Day here in Poland as I write these words. Happy Dog Day!&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;</description><author>code · words · emotions: Daniel Janus’s blog</author><pubDate>Thu, 01 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://blog.danieljanus.pl/commit-groups/</guid></item><item><title>Luis H. Caraveo Rodríguez (1940-2021)</title><link>https://danielsada.tech/notas/luis-caraveo-rodriguez/</link><description>In memoriam, avus magni.
Algunas personas tienen una urna, otras un sepulcro, otras quieren su nombre tallado en una banca o calle. Yo quiero hacer un recuerdo en digital de mi abuelo que falleció la ultima semana de junio para compartir lo hermoso que fue compartir la vida con él. Este texto también existe en mi blog, danielsada (punto) tech para el futuro.
–
Una de las cosas más características de nuestra relación es que siempre nos hablamos en formal: “Señor, ¿cómo está?</description><author>Daniel Sada Caraveo | Developer Productivity &amp;amp; Culture</author><pubDate>Thu, 01 Jul 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://danielsada.tech/notas/luis-caraveo-rodriguez/</guid></item><item><title>New Old Game: Gravi-o-roids!</title><link>https://www.masswerk.at/nowgobang/2021/new-old-game-gravioroids</link><description>Presenting a new old-school video game just a few years late, totally unironically.</description><author>mass:werk – Now Go Bang!</author><pubDate>Thu, 01 Jul 2021 02:59:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/new-old-game-gravioroids</guid></item><item><title>Interpersonal Victimhood is digital</title><link>https://stop.zona-m.net/2021/06/interpersonal-victimhood-is-digital/</link><description>&lt;p&gt;Or, at least, digitally enhanced. A lot.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 30 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/interpersonal-victimhood-is-digital/</guid></item><item><title>Parallax: The Race to Measure the Cosmos</title><link>https://macleodsawyer.com/books/parallax/</link><description>&lt;p&gt;This is one of the best books I’ve read in the past year, an excellent history that surrounds the quest to discover, catalogue, and complete the map of the universe. I believe what makes this book stand out among the other histories of astronomy that I have read, is that this makes a conscious effort to write on the underlying technological improvement and mechanical design of each new iteration of tools. This is the book that describes the beginning of precision science in all the ways that matter. Terrific illustrations, explanations, and small tidbits (wait till you find out who Tycho Brahe had as a ‘pet’!) are found all throughout this book. My only problem is that the book ends too quickly!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The greatest scientific minds applied themselves in vain to the problem across the millennia, beginning with the ancient Greeks. Not until the nineteenth century would three astronomers, armed with the best telescopes of the age, race to conquer this astronomical Everest—their contest ending in a virtual dead heat.
Against a sweeping backdrop filled with kidnappings, dramatic rescue, swordplay, madness, and bitter rivalry, Alan Hirshfeld brings to life the heroes of this remarkable story. Meet the destitute boy plucked from a collapsed building who becomes the greatest telescope maker the world has ever seen; the hot-tempered Dane whose nose is lopped off in a duel over mathematics; the merchant’s apprentice forced to choose between the lure of money and his passion for astronomy; and the musician who astounds the world by discovering a new planet from his own backyard.
Generously illustrated with diagrams, period engravings, and paintings, Parallax is an unforgettable tale that illuminates the distinctly human side of science.&lt;/p&gt;
&lt;/blockquote&gt;</description><author>Macleod Sawyer | Web Portal Feed</author><pubDate>Wed, 30 Jun 2021 15:06:36 GMT</pubDate><guid isPermaLink="true">https://macleodsawyer.com/books/parallax/</guid></item><item><title>Don&amp;amp;#39;t Generate Glue...Exterminate!!</title><link>https://blog.metaobject.com/2021/06/don-generate-glueexterminate.html</link><description>Today I saw the &lt;a href="https://news.ycombinator.com/item?id=27676266&amp;amp;p=2"&gt;news&lt;/a&gt; of github's release of the "AI Autopilot".  As far as I can tell, it's an impressive piece
of engineering that shouldn't exist.  I mean, "Paste Code from Stack Overflow as a Service" was supposed to be
a joke, not a product spec.&lt;p&gt;

&lt;img alt="" border="0" height="" src="http://icodeit.org/images/2016/05/stackoverflow-oreilly.png" title="" width="200" /&gt;
&lt;p&gt;

As of this writing, the first example given on the &lt;a href="https://copilot.github.com"&gt;product page&lt;/a&gt; is some
code to call a REST service that does sentiment analysis, which the AI helpfully completes.  For reference, this is the code:


&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
#!/usr/bin/env ts-node

import { fetch } from "fetch-h2";

// Determine whether the sentiment of text is positive
// Use a web service
async function isPositive(text: string): Promise&amp;lt;boolean&amp;gt; {
  const response = await fetch(`http://text-processing.com/api/sentiment/`, {
    method: "POST",
    body: `text=${text}`,
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
  });
  const json = await response.json();
  return json.label === "pos";
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

Here is the same script in Objective-S:

&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
#-sentiment:text
((ref:http://text-processing.com/api/sentiment/ postForm:#{ #text: text }) at:'label') = 'pos'
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

And once you have it, reuse it.  And keep those Daleks at bay.&lt;p&gt;</description><author>metablog</author><pubDate>Wed, 30 Jun 2021 13:01:06 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/06/don-generate-glueexterminate.html</guid></item><item><title>Book review - Microcopy</title><link>https://river.me/blog/book-review-microcopy/</link><description>A review of &amp;ldquo;Microcopy: The Complete Guide&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 30 Jun 2021 03:08:32 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-microcopy/</guid></item><item><title>Thoughts on the linkpocalypse</title><link>https://www.marginalia.nu/log/00-linkpocalypse/</link><description>&lt;p&gt;For a long while, I have been puzzled by the strangest problem: My attention span is really bad when I use a computer. I&amp;rsquo;m an avid reader of esoteric books. I have (recently) read the notoriously dry Confessions of Saint Augustine in print, it was a slog for certain, but it really doesn&amp;rsquo;t compare with the struggles I have when it comes to bringing myself to reading even a few paragraphs of text on a screen. It surely can&amp;rsquo;t be the screen itself, can it? That doesn&amp;rsquo;t seem plausible.&lt;/p&gt;</description><author>Weblog on marginalia.nu</author><pubDate>Wed, 30 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.marginalia.nu/log/00-linkpocalypse/</guid></item><item><title>Proper conversion from XLS to PDF in Java</title><link>https://dystroy.org/blog/excel-to-pdf-java/</link><description>&lt;p&gt;Generating or modifying Excel files in Java is easy with &lt;a href="https://poi.apache.org/"&gt;Apache POI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But when you generate Excel reports, you always end up having to export them in PDF too.&lt;/p&gt;
&lt;p&gt;If you look for a library for this conversion, you'll find many ones, most of them expensive, and none of them giving acceptable results.&lt;/p&gt;
&lt;p&gt;I explain here the efficient and reliable enough solution I've used in a Java application.&lt;/p&gt;</description><author>dystroy|Canop / blog</author><pubDate>Wed, 30 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dystroy.org/blog/excel-to-pdf-java/</guid></item><item><title>July</title><link>https://gyani.net/blog/july/</link><description>.. the second act - 
		  This is going to be really brief. I am writing this from Istanbul. Retrospective Running: I wanted to run 167k I ran 167.1k. I am at 1014k for the year. ✅ Reading: I read two parts of Musashi and The Bitcoin Standard. ✅ Weight: I was supposed to be down...</description><author>Gyanendra Mishra</author><pubDate>Tue, 29 Jun 2021 21:30:00 GMT</pubDate><guid isPermaLink="true">https://gyani.net/blog/july/</guid></item><item><title>Beware sociopathic innovation</title><link>https://stop.zona-m.net/2021/06/beware-sociopathic-innovation/</link><description>&lt;p&gt;Learn to recognize REAL innovation, instead.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 29 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/beware-sociopathic-innovation/</guid></item><item><title>View in app</title><link>https://voussoir.net/writing/view_in_app</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;View in app&lt;/h1&gt;
&lt;p&gt;Hi! 👋&lt;/p&gt;
&lt;p&gt;It looks like you're viewing this post in a web browser. Posts from voussoir.net look better and have more features when viewed from our official voussoir.net app! That's because we've intentionally reduced the performance and functionality of the web version, which would otherwise do everything you need! So although you seem to be very content browsing the website, we've decided to cut you off, block your scrolling with this modal, and serve 429s for the remainder of your session. Trust us, this really is for your own good. We just can't stand to see you suffer through this non-optimal voussoir.net experience when you could be using our awesome app instead. Thank us later!&lt;/p&gt;
&lt;p&gt;&lt;a href="/app"&gt;Click here to get the app!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(&lt;a href="https://duckduckgo.com/?q=how+to+stop+being+sooooo+lame"&gt;No thanks, I hate myself and I don't deserve the best voussoir.net experience!&lt;/a&gt;)&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/view_in_app/view_in_app.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/9a60192ef4bee72ebf3133215b0d5bccbb010425"&gt;&lt;time datetime="2021-06-28T18:39:29-07:00"&gt;2021-06-28&lt;/time&gt; Add view_in_app.md, app.html.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Tue, 29 Jun 2021 04:39:29 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/view_in_app</guid></item><item><title>Sentimentality is Unearned Emotion</title><link>/2021/06/sentimentality</link><description>Sentimentality in fiction is generally considered something to be avoided. I’m thinking, for example, of the advice Laurie Alberts gives in her book Showing and Telling, though I’m sure similar advice would be given by any writing instructor. Sentimentality is cheap, and particularly if used at the end of a book, gives it an overall saccharine feel that detracts from its impact.</description><author>Elliott Slaughter</author><pubDate>Tue, 29 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/2021/06/sentimentality</guid></item><item><title>Westerners did not get only the Arab Spring wrong</title><link>https://stop.zona-m.net/2021/06/westerners-did-not-get-only-the-arab-spring-wrong/</link><description>&lt;p&gt;And themselves too. They still do.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 29 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/westerners-did-not-get-only-the-arab-spring-wrong/</guid></item><item><title>My New Bash Prompt (PS1)</title><link>https://www.mikekasberg.com/blog/2021/06/28/my-new-bash-prompt.html</link><description>&lt;p&gt;I recently spent some time improving my bash prompt. I already had a lightly
customized prompt, but wanted to see if I could find something I liked that
provided even more information.&lt;/p&gt;

&lt;p&gt;I’ve known about powerline for years, but never actually sat down to experiment
with it on my own machine, so that’s where I started. I installed the Ubuntu
Mono &lt;a href="https://www.nerdfonts.com/"&gt;Nerd Font&lt;/a&gt; and installed and configured
&lt;a href="https://github.com/powerline/powerline"&gt;powerline-status&lt;/a&gt;. The prompt was nice
enough, but right off the bat there were a few things I didn’t like about it.
There was no easy way to add a newline to the prompt, it was noticeably slow,
and customizing the config seemed more complicated than it needed to be.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Powerline Bash prompt" src="https://www.mikekasberg.com/images/my-new-bash-prompt/powerline.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;Because I was a little disappointed with powerline, I looked for some
alternatives. &lt;a href="https://github.com/b-ryan/powerline-shell"&gt;powerline-shell&lt;/a&gt;
seemed like a cool project – based on the original powerline, but with a focus on
the shell prompt. I liked powerline-shell better. The config was easier to
understand, and there was even easy newline support (Hooray 🎉)! But
powerline-shell was still slow enough that there was a noticeable lag to
generate the prompt. It was jarring enough that I kept searching for
alternatives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/justjanne/powerline-go"&gt;powerline-go&lt;/a&gt; looks like a good
alternative to powerline-shell that claims to be much faster. I can’t recommend
it from personal experience though, because I &lt;em&gt;never actually tried it&lt;/em&gt;. After
already installing two different powerline prompts I didn’t like that much, it was
getting a bit tedious to install all these different packages just to realize I
didn’t like them that much. And I’d also begun to realize, after trying a
couple different config variataions of both powerline-status and
powerline-shell, that they looked kinda cool but were generally painful to
set up and didn’t provide much info I couldn’t easily add to my own lightly
customized bash prompt, without installing anything.&lt;/p&gt;

&lt;p&gt;So, that’s what I did. I’d noticed that pretty much every powerline package
worked by breaking the prompt line into “segments” – individual pieces of
information that could be glued together to build the complete prompt. So I
refactored my own PS1 customizations to do something similar.&lt;/p&gt;

&lt;p&gt;Before the change, my PS1 looked &lt;a href="https://github.com/mkasberg/dotfiles/blob/30875c33c7b569204e13e0022e0a56f7fb4f2def/dot_bashrc#L71"&gt;like this&lt;/a&gt;:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\n\[${CYAN}\]${debian_chroot:+($debian_chroot)}\u\[${RESET}${BOLD}\] \[${RED}\]&amp;gt;&amp;gt;\[${GREEN}\]&amp;gt; \[${CYAN}\]\w\[${RESET}\]$(__git_ps1 " (\[${MAGENTA}\]%s\[${RESET}\])")\n\[${BOLD}\]\$\[${RESET}\] '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After the change, I’d refactored my
&lt;a href="https://github.com/mkasberg/dotfiles/blob/99c51be200ad7b81a7a5c26ccf947259ad8a29d8/dot_bashrc#L63-L65"&gt;PS1&lt;/a&gt;
into an entirely &lt;a href="https://github.com/mkasberg/dotfiles/blob/99c51be200ad7b81a7a5c26ccf947259ad8a29d8/executable_dot_mkps1.sh#L1"&gt;separate
file&lt;/a&gt;,
with each “segment” generated in its own function. Because of the functions, the
PS1 is much easier to read.&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;ps1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;__mkps1_box_top&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_debian_chroot&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_exitcode&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_time&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_username&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_arrows&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;__mkps1_workdir&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_git&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;__mkps1_box_bottom&lt;span class="si"&gt;)$(&lt;/span&gt;__mkps1_user_prompt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="so-where-am-i-at-now"&gt;So, where am I at now?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;✅ I’m still using &lt;a href="https://github.com/powerline/powerline"&gt;powerline-status&lt;/a&gt; as
a statusline in Vim. I like the info it provides, and I haven’t noticed any
lag on the Vim statusline that’s significant enough to bother me.&lt;/li&gt;
  &lt;li&gt;❌ I’m not using any powerline fonts. Not in my Vim statusline, and not in my
shell prompt. Although they look kind of cool, I realized I value portability
and ease of setup over the look. (I don’t want to configure every application
that might render a terminal to use a powerline font.) I’ve
&lt;a href="https://github.com/mkasberg/dotfiles/blob/99c51be200ad7b81a7a5c26ccf947259ad8a29d8/private_dot_config/powerline/config.json#L3"&gt;configured&lt;/a&gt;
the powerline Vim plugin to use ascii mode, and I didn’t include any powerline
characters in my own prompt.&lt;/li&gt;
  &lt;li&gt;➡ I am using a few Unicode characters in my own prompt though. They seem to be
supported by default pretty much everywhere these days, and it seemed like a
good compromise to get a cool look without the powerline font.&lt;/li&gt;
  &lt;li&gt;😍 I love the &lt;strong&gt;newlines&lt;/strong&gt; in my prompt, and I’d never switch back to a prompt
without newlines. I use a newline above the prompt to create separation from
the previous output, and I put the &lt;code class="language-plaintext highlighter-rouge"&gt;$&lt;/code&gt; on a newline because I like having lots
of room to type and I like having it consistently on the left side of the
screen. Overall, both these newlines make it easier to visually scan through
terminal output.&lt;/li&gt;
  &lt;li&gt;⏱ I added a timestamp to my prompt. I’ve found myself wishing I had this several
times in the past when I wanted to know how long a command’s been running.&lt;/li&gt;
  &lt;li&gt;❓ I also added an exit code indicator to my prompt – it only shows if the
previous command failed.&lt;/li&gt;
  &lt;li&gt;📦 Did you know &lt;code class="language-plaintext highlighter-rouge"&gt;__git_ps1()&lt;/code&gt; (the prompt function that ships with git) includes
several &lt;a href="https://github.com/git/git/blob/670b81a890388c60b7032a4f5b879f2ece8c4558/contrib/completion/git-prompt.sh#L38-L71"&gt;environment
variables&lt;/a&gt;
you can use to show more info? I enabled several of these, and they’re great!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="sharing-my-work"&gt;Sharing My Work&lt;/h2&gt;

&lt;p&gt;I &lt;a href="https://www.mikekasberg.com/blog/2021/05/12/my-dotfiles-story.html"&gt;share my dotfiles&lt;/a&gt; on GitHub, so
you can look at my
&lt;a href="https://github.com/mkasberg/dotfiles/blob/99c51be200ad7b81a7a5c26ccf947259ad8a29d8/dot_bashrc#L63-L65"&gt;.bashrc&lt;/a&gt;
or &lt;a href="https://github.com/mkasberg/dotfiles/blob/99c51be200ad7b81a7a5c26ccf947259ad8a29d8/executable_dot_mkps1.sh#L3-L8"&gt;.mkps1.sh
script&lt;/a&gt;
if you’re curious to see what I’m doing, or if you’d like to try it yourself.
You don’t need to install anything special to make it work - it’s all just shell
script. (I found it somewhat odd that many of the various bash prompt
generators require other languages like NodeJS, Python, etc.) If you like it,
&lt;a href="https://twitter.com/mike_kasberg"&gt;let me know&lt;/a&gt; – I’m considering writing a
more detailed tutorial about how you can do something similar to what I’ve done
to customize your own Bash or Zsh prompt.&lt;/p&gt;

&lt;p&gt;And if you came here looking for a recommendation about how to set up a
powerline prompt in Bash, I’d probably start by trying
&lt;a href="https://github.com/justjanne/powerline-go#installation"&gt;powerline-go&lt;/a&gt;. It
should be easy to install with precompiled binaries. (You don’t need to install
Go yourself - just put the binaries in &lt;code class="language-plaintext highlighter-rouge"&gt;~/bin&lt;/code&gt; or any similar location, run
&lt;code class="language-plaintext highlighter-rouge"&gt;chmod +x powerline-go&lt;/code&gt;, and when editing your &lt;code class="language-plaintext highlighter-rouge"&gt;.bashrc&lt;/code&gt; as in the readme make
sure the path to the &lt;code class="language-plaintext highlighter-rouge"&gt;powerline-go&lt;/code&gt; binary is where you saved it.)  Powerline-go
comes with reasonable defaults, it will look nice, and it should be faster than
some of the other options since it’s written in Go.&lt;/p&gt;

&lt;p&gt;Finally, if you’re using zsh instead of bash (as you probably are if you’re on a
modern version of macOS), you should check out &lt;a href="https://ohmyz.sh/"&gt;Oh My Zsh&lt;/a&gt; as
a starting point if you want to customize your prompt. It comes with a good
selection of &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Themes"&gt;themes&lt;/a&gt; that are
easy to install by setting the &lt;code class="language-plaintext highlighter-rouge"&gt;ZSH_THEME&lt;/code&gt; variable in your &lt;code class="language-plaintext highlighter-rouge"&gt;~/.zshrc&lt;/code&gt;, and you
can add additional customizations if you want to. If you choose a theme that
uses a powerline-like prompt, you’ll still need to install a &lt;a href="https://www.nerdfonts.com/"&gt;Nerd
Font&lt;/a&gt; to render the special characters correctly.
Either way, I’m sure you can find something you like!&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Mon, 28 Jun 2021 18:30:00 GMT</pubDate><guid isPermaLink="true">https://www.mikekasberg.com/blog/2021/06/28/my-new-bash-prompt.html</guid></item><item><title>Generating ARM Assembly: First Steps</title><link>https://blog.metaobject.com/2021/06/generating-arm-assembly-first-steps.html</link><description>Finally took the plunge to start generating ARM64 assembly.  As expected, the actual coding was much
easier than overcoming the barrier to just start doing it.&lt;p&gt;

The following snippet generates a program that prints a message to &lt;code&gt;stdout&lt;/code&gt;, so 
a classic "Hello World":
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
#-gen:msg

messageLabel ← 'message'.
main ← '_main'.

framework:ObjSTNative load
arm := MPWARMAssemblyGenerator stream 
arm global: main;
    align:2;
    label:main;
    mov:0 value:1;
    adr:1 address:messageLabel;
    mov:2 value: msg length;
    mov:16 value:4;
    svc:128;
    mov:0 value:0;
    ret;
    label:messageLabel;
    asciiz:msg.

file:hello-main.s := arm target 
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
One little twist is that the message to print gets passed to the generator.  I like how 
Smalltalk's keyword syntax keeps the code uncluttered, and often pretty close to the
actual assembly that will be generated.&lt;p&gt;

Of particular help here is message &lt;em&gt;cascading&lt;/em&gt; using the semicolon.  This 
means I don't have to repeat the receiver of the message, but can just keep
sending it messages.  Cascading works well together with streams, because
there are no return values to contend with, we just keep appending to
the stream.&lt;p&gt;

When invoked using &lt;code&gt;./genhello-main.st 'Hi Marcel, finish that blog post and get on your bike!'&lt;/code&gt;, the generated code is as follows:&lt;p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;code&gt;
&lt;pre&gt;
.global	_main
.align	2
_main:
	mov	X0, #1
	adr	X1, message
	mov	X2, #54
	mov	X16, #4
	svc	#128
	mov	X0, #0
	ret
message:
	.asciz	"Hi Marcel, finish that blog post and get on your bike!"
&lt;/pre&gt;
&lt;/code&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;

And now I am going to do what it says :-)&lt;p&gt;</description><author>metablog</author><pubDate>Mon, 28 Jun 2021 11:28:59 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/06/generating-arm-assembly-first-steps.html</guid></item><item><title>Explorations in The Card Game SET®</title><link>http://blog.untrod.com/2021/06/set-solver-in-python.html</link><description>&lt;p&gt;&lt;a href="https://www.playmonster.com/brands/set/"&gt;SET&lt;/a&gt; is a classic pattern-matching card game that's been around since the 1970s, but really took off in the 90s. It hits all the right marks for a great game casual game; easy to learn, works for any number of players, you can play for 5 minutes or for hours …&lt;/p&gt;</description><author>Untrod</author><pubDate>Mon, 28 Jun 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">http://blog.untrod.com/2021/06/set-solver-in-python.html</guid></item><item><title>An unexpected return to the Alps</title><link>https://nicolaiarocci.com/an-unexpected-return-to-the-alps/</link><description>&lt;p&gt;I recently had the unexpected opportunity of spending a few days in the Alps,
climbing and hiking with my daughters. After a few years of hiatus, it felt
great to be back romping.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Monte Stevia" src="https://nicolaiarocci.com/images/monte-stevia.jpg#center" /&gt;&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 28 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/an-unexpected-return-to-the-alps/</guid></item><item><title>How to mess up a simple ThinkPad X230 BIOS flash and how to recover from it</title><link>https://ounapuu.ee/posts/2021/06/28/breaking-and-fixing-thinkpad-x230/</link><description>At least it won't end up in a landfill!</description><author>./techtipsy</author><pubDate>Mon, 28 Jun 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/06/28/breaking-and-fixing-thinkpad-x230/</guid></item><item><title>ARM Templates: list*() &amp;amp; reference() functions in variables workaround</title><link>https://pricey.uk/blog/arm-template-list-variables/</link><description>&lt;p&gt;Use ARM templates enough and eventually you'll wish to use one of the &lt;code&gt;list*()&lt;/code&gt; functions or &lt;code&gt;reference()&lt;/code&gt; in your variables.&lt;/p&gt;
&lt;p&gt;For example, you have multiple app services which require near identical appsettings. You'd like to define this object/array once, then reuse multiple times elsewhere in the template. Who likes repeating themselves? Unfortunately one of those settings includes e.g. a storage account access key or a &lt;code&gt;reference()&lt;/code&gt; to grab an application insights key...&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Azure/azure-quickstart-templates/issues/1503" rel="external"&gt;Unfortunately, it's well documented that this is not supported:&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The template function 'listKeys' is not expected at this location&lt;/p&gt;
&lt;/blockquote&gt;</description><author>Joseph Price</author><pubDate>Mon, 28 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://pricey.uk/blog/arm-template-list-variables/</guid></item><item><title>Vertigo</title><link>https://blog.imraniqbal.org/vertigo/</link><description>&lt;p&gt;For the first time in my life I have gotten vertigo.
It's not clear from where I got it, or how.
All I know is that it sucks quite a bit.&lt;/p&gt;
&lt;p&gt;Here's to hoping it passes soon and my body adjusts to my new sense of balance.&lt;/p&gt;</description><author>Imran's Blog</author><pubDate>Mon, 28 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.imraniqbal.org/vertigo/</guid></item><item><title>Put on the ceiling</title><link>https://dustin.lammiman.ca/teardrop/ceiling/</link><description>&lt;p&gt;Got most of the ceiling on. Front window is just in temporarily to test the fit.&lt;/p&gt;</description><author>Dustin.Lammiman</author><pubDate>Mon, 28 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/teardrop/ceiling/</guid></item><item><title>Making a Crude ML Powered Chatbot in Swift using CoreML</title><link>https://web.navan.dev/posts/2021-06-27-Crude-ML-AI-Powered-Chatbot-Swift.html</link><description>Writing a simple Machine-Learning powered Chatbot (or, daresay virtual personal assistant ) in Swift using CoreML.</description><author>Navan's Archive</author><pubDate>Mon, 28 Jun 2021 02:26:00 GMT</pubDate><guid isPermaLink="true">https://web.navan.dev/posts/2021-06-27-Crude-ML-AI-Powered-Chatbot-Swift.html</guid></item><item><title>Golang - how to write to a spreadsheet</title><link>https://yasha.solutions/posts/golang-how-to-write-to-a-spreadsheet/</link><description>Golang - how to connect to google spreadsheet? Credentials You need to set up Google Credentials for your app. This part is a tad annoying. Mostly due to the UX of the Google Cloud Plateform. Let&amp;rsquo;s hope this will once be fixed.
Since it may be changing, I suggest you check the docs on Google. At the time of writing of this short tutorial, we need to:
 Go to Google Cloud Console Create Project if needed.</description><author>Yasha Solutions</author><pubDate>Mon, 28 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://yasha.solutions/posts/golang-how-to-write-to-a-spreadsheet/</guid></item><item><title>2021–06–28: Pinephone keyboard's final summary</title><link>https://xnux.eu/log/#042</link><author>megi's PinePhone Development Log</author><pubDate>Mon, 28 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#042</guid></item><item><title>Extracting Data from the Swiss Vaccination Certificate</title><link>https://bastian.rieck.me/blog/2021/vaccination_certificate/</link><description>&lt;p&gt;With the Swiss COVID vaccination campaign picking up some steam, I am
grateful to have finally received two doses of a vaccine.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; I was
mightily impressed when I also received a nice certificate with a QR
code that I may now use to transfer information about my vaccination to
a smartphone app&amp;mdash;the idea being that every vaccinated person can
present this as a proof.
I tried to scan the QR code with my normal camera app and instead of
a link or some other textual information, I just received what looked
like gibberish at first glance. My curiosity thus being piqued,
I searched for some way to extract more meaningful information from this
code and quickly stumbled over &lt;a href="https://gir.st/blog/greenpass.html"&gt;Tobias Girstmair&amp;rsquo;s awesome description of how he decoded the Austrian vaccination certificate&lt;/a&gt;.
In true &amp;lsquo;duct-tape programmer&amp;rsquo; fashion, I was looking for a way to
extend Tobias&amp;rsquo;s great description so that I could extract all this
information with command-line tools. This post shows what I came up
with.&lt;/p&gt;
&lt;h1 id="ingredients"&gt;Ingredients&lt;/h1&gt;
&lt;p&gt;You need a few utilities for this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://zbar.sourceforge.net"&gt;&lt;code&gt;zbar&lt;/code&gt;&lt;/a&gt;, for reading the QR code.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;base45&lt;/code&gt; decoder such as &lt;a href="https://github.com/kirei/python-base45"&gt;&lt;code&gt;python-base45&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A way to extract &lt;code&gt;zlib&lt;/code&gt; data, such as &lt;a href="https://zlib.net/pigz"&gt;&lt;code&gt;pigz&lt;/code&gt;&lt;/a&gt;
or a version of &lt;a href="https://www.openssl.org"&gt;OpenSSL&lt;/a&gt; with &lt;code&gt;zlib&lt;/code&gt;
support enabled.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dflemstr/rq"&gt;&lt;code&gt;rq&lt;/code&gt;&lt;/a&gt;, for working with &lt;a href="https://en.wikipedia.org/wiki/CBOR"&gt;CBOR&lt;/a&gt; data.&lt;/li&gt;
&lt;li&gt;A set of POSIX core tools, namely &lt;code&gt;cut&lt;/code&gt; and &lt;code&gt;tr&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are on Mac OS X, all of these&amp;mdash;except &lt;code&gt;python-base45&lt;/code&gt;&amp;mdash;can be
installed using &lt;a href="https://brew.sh"&gt;Homebrew&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="recipe"&gt;Recipe&lt;/h1&gt;
&lt;p&gt;Assuming your QR code is a file called &lt;code&gt;QR.png&lt;/code&gt;, this is how to extract
and nicely visualise its information:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;zbarimg --raw QR.png    \
    | cut -b5-          \
    | tr -d '\n'        \
    | base45 --decode   \
    | pigz -d           \
    | cut -b20-         \
    | cut -b1-384       \
    | rq -c
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s briefly dissect these commands to uncover their meaning:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We extract the raw QR code data.&lt;/li&gt;
&lt;li&gt;We remove the outermost header (we are not interested in verifying
the signature or the authenticity of that certificate; we just want
the data).&lt;/li&gt;
&lt;li&gt;We remove the newline (to enable downstream processing).&lt;/li&gt;
&lt;li&gt;We decode the data (it uses &lt;code&gt;base45&lt;/code&gt; for efficiency purposes).&lt;/li&gt;
&lt;li&gt;We uncompress the data (it is compressed using &lt;code&gt;zlib&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;We remove some more header information (this was based on my educated guess about the length of the header section; you can also just keep the original data and look at it using &lt;code&gt;xxd&lt;/code&gt; to see that I did not remove anything relevant here).&lt;/li&gt;
&lt;li&gt;We remove the digital signature (we could also leave it in, but then the last command will not result in a pretty output; this &lt;em&gt;is&lt;/em&gt; the duct-tape version, after all).&lt;/li&gt;
&lt;li&gt;We finally pretty-print everything.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This should result in an output like this:&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{
  "1": {
    "dob": "1943-02-01",
    "nam": {
      "fn": "Müller",
      "fnt": "MUELLER",
      "gn": "Céline",
      "gnt": "CELINE"
    },
    "v": [
      {
        "ci": "urn:uvci:01:CH:2987CC9617DD5593806D4285",
        "co": "CH",
        "dn": 2,
        "dt": "2021-04-30",
        "is": "Bundesamt für Gesundheit (BAG)",
        "ma": "ORG-100031184",
        "mp": "EU/1/20/1507",
        "sd": 2,
        "tg": "840539006",
        "vp": "1119349007"
      }
    ],
    "ver": "1.0.0"
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There you have it&amp;mdash;a pretty amazing amount of information contained in a nice QR code. The format
appears to follow the &lt;a href="https://github.com/ehn-dcc-development/hcert-spec/blob/main/README.md"&gt;Electronic Health Certificate&lt;/a&gt;
to the letter,&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; which is surprising to me because the relationship
between the EU and Switzerland is somewhat complicated. I am glad
to see that in these times, authorities are rallying under a common
banner and creating interoperable standards.&lt;/p&gt;
&lt;h1 id="aftermath"&gt;Aftermath&lt;/h1&gt;
&lt;p&gt;This was fun to fiddle around with&amp;mdash;I find command-line data processing
pipelines neat because they tend to contain a mixture of tools from
different decades. That being said, if you want to parse such
certificates properly, you should read the &lt;a href="https://github.com/ehn-dcc-development/hcert-spec/releases"&gt;most recent specification&lt;/a&gt;.
This duct-tape version should &lt;strong&gt;not&lt;/strong&gt; be used in production.&lt;/p&gt;
&lt;p&gt;Stay healthy, until next time!&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;em&gt;Insert obligatory 5G/telepathy joke here.&lt;/em&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;This is &lt;em&gt;not&lt;/em&gt; my real certificate. Your output might also contain
a few additional lines that only specify some information about the
issuer. Adjust the first &lt;code&gt;cut&lt;/code&gt; command to remove them.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I got my test data from this repository, but the code as shown
here &lt;em&gt;also&lt;/em&gt; works with my personal certificate.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Sun, 27 Jun 2021 22:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/vaccination_certificate/</guid></item><item><title>Boredom, Books &amp;amp; Bugs - A tale of my COVID-19 experience</title><link>https://rajkumaar.co.in/posts/boredom-books-bugs/</link><description/><author>Online Home of Rajkumar</author><pubDate>Sun, 27 Jun 2021 18:12:11 GMT</pubDate><guid isPermaLink="true">https://rajkumaar.co.in/posts/boredom-books-bugs/</guid></item><item><title>Evangelion: 2.22 You Can (Not) Advance</title><link>https://olshansky.info/movie/evangelion_2.22_you_can_not_advance/</link><description>Olshansky's review of Evangelion: 2.22 You Can (Not) Advance</description><author>🦉 olshansky 🦁</author><pubDate>Sun, 27 Jun 2021 12:19:58 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/evangelion_2.22_you_can_not_advance/</guid></item><item><title>Thoughts on MQTT</title><link>https://blog.herlein.com/post/mqtt/</link><description>&lt;p&gt;Some Thoughts on MQTT&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Sun, 27 Jun 2021 11:00:01 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/mqtt/</guid></item><item><title>A Beginner's Guide to Miles Davis</title><link>https://nicolaiarocci.com/a-beginners-guide-to-miles-davis/</link><description>&lt;p&gt;Sam Enright assembled a friendly &lt;a href="https://samenright.com/2021/06/06/a-beginners-guide-to-miles-davis/"&gt;Beginner&amp;rsquo;s Guide to Miles Davis&lt;/a&gt;. If
you&amp;rsquo;ve always been curious about jazz but never really managed to get into it,
then this resource might serve as a good starting point. I cannot say I&amp;rsquo;m one
hundred per cent aligned with his choices, but we&amp;rsquo;re close. One remarkable
statement I concur with is this one:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jazz is so interesting to me because of its fusion of intricate underlying
structure with improvisation and spontaneity. As Ken Burns put it, jazz is
“familiar, but brand new every night”. Moreover, I enjoy the intellectual
demandingness of jazz as a genre. Jazz musicians seem to be the most
thoughtful and intelligent of any genre. Many of the more Avant Garde songs
mentioned in this post don’t sound good unless you’re really concentrating.
Some of it sounds cacophonous to a newcomer. This is why jazz is considerably
more difficult to get into than other genres and has a lack of listenership
among the youth.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sun, 27 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/a-beginners-guide-to-miles-davis/</guid></item><item><title>The Pandemic</title><link>https://savraj.co/2021/06/27/the-pandemic.html</link><description>I don’t write many blog posts or make many public reflections, but the coronavirus pandemic of 2020-2021 seems to merit a quick writeup.</description><pubDate>Sun, 27 Jun 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://savraj.co/2021/06/27/the-pandemic.html</guid></item><item><title>Emacs: smarter search and replace</title><link>https://xenodium.com/emacs-smarter-search-and-replace</link><description>&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-smarter-search-and-replace/smarter_replace.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Not long ago, I made a note to go back and read &lt;a href="https://mac4translators.blogspot.com"&gt;Mac for Translators&lt;/a&gt;'s &lt;a href="https://mac4translators.blogspot.com/2021/06/regex-with-elisp.html"&gt;Emacs regex with Emacs lisp&lt;/a&gt; post. The author highlights Emacs's ability to apply additional logic when replacing text during a search-and-replace session. It does so by leveraging elisp expressions.&lt;/p&gt;
&lt;p&gt;Coincidentally, a redditor recently asked &lt;a href="https://www.reddit.com/r/emacs/comments/o878am/what_is_the_simplest_way_to_apply_a_math_formula/"&gt;What is the simplest way to apply a math formula to all numbers in a buffer/region?&lt;/a&gt; Some of the answers also point to &lt;em&gt;search and replace&lt;/em&gt; leveraging elisp expressions.&lt;/p&gt;
&lt;p&gt;While I rarely need to apply additional logic when replacing matches, it's nice to know we have options available in our Emacs toolbox. This prompted me to check out &lt;a href="https://github.com/emacs-mirror/emacs/blob/b8f9e58ef72402e69a1f0960816184d52e5d2d29/lisp/replace.el#L709"&gt;replace-regexp&lt;/a&gt;'s documentation (via M-x &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Name-Help.html"&gt;describe-function&lt;/a&gt; or my favorite M-x &lt;a href="https://github.com/Wilfred/helpful"&gt;helpful-callable&lt;/a&gt;). There's lots in there. Go check its docs out. You may be pleasantly surprised by all the features packed under this humble function.&lt;/p&gt;
&lt;p&gt;For instance, \&amp;amp; expands to the current match. Similarly, \#&amp;amp; expands to the current match, fed through &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/String-Conversion.html"&gt;string-to-number&lt;/a&gt;. But what if you'd like to feed the match to another function? You can use \, to signal evaluation of an elisp expression. In other words, you could multiply by 3 using \,(* 3 \#&amp;amp;) or inserting whether a number is odd or even with something like \,(if (oddp \#&amp;amp;) &amp;quot;(odd)&amp;quot; &amp;quot;(even)&amp;quot;).&lt;/p&gt;
&lt;p&gt;Take the following text:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-example"&gt;1
2
3
4
5
6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can label each value &amp;quot;(odd)&amp;quot; or &amp;quot;(even)&amp;quot; as well as multiply by 3, by invoking &lt;em&gt;replace-regexp&lt;/em&gt; as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;M-x replace-regexp&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;[PCRE] Replace regex:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[-0-9.]+&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Replace regex [-0-9.]+:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;\&amp;amp; \,(if (oddp \#&amp;amp;) &amp;quot;(odd)&amp;quot; &amp;quot;(even)&amp;quot;) x 3 = \,(* 3 \#&amp;amp;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class="language-example"&gt;1 (odd) x 3 = 3
2 (even) x 3 = 6
3 (odd) x 3 = 9
4 (even) x 3 = 12
5 (odd) x 3 = 15
6 (even) x 3 = 18
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's worth noting that &lt;em&gt;replace-regexp&lt;/em&gt;'s cousin &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Query-Replace.html"&gt;query-replace-regexp&lt;/a&gt; also handles all this wonderful magic.&lt;/p&gt;
&lt;p&gt;Happy searching and replacing!&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 27 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/emacs-smarter-search-and-replace</guid></item><item><title>Unicode sorting is hard &amp;amp; why browsers added special emoji matching to regexp</title><link>https://devlog.hexops.org/2021/unicode-sorting-why-browsers-added-special-emoji-matching/</link><description>&lt;p&gt;As I work on &lt;a href="https://github.com/hexops/zorex"&gt;Zorex, an omnipotent regexp engine&lt;/a&gt; I have stumbled into a world of tales about why Unicode text sorting is so annoying in the modern day. Let&amp;rsquo;s talk about that.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#why-ascii-sorting-is-not-enough"&gt;Why ASCII sorting is not enough&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#twitters-emoji-problem---or-when-unicode-locale-aware-sorting-really-matters"&gt;Twitter&amp;rsquo;s emoji problem - or when Unicode locale-aware sorting Really Matters™&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#browsers-added-special-emoji-matching-to-regexp"&gt;Browsers added special emoji matching to regexp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#language-comparison"&gt;Language comparison&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#javascript-collator-sorting-is-not-guaranteed-across-browsers"&gt;JavaScript Collator sorting is not guaranteed across browsers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#go-sortstrings-is-not-locale-aware"&gt;Go sort.Strings is not locale aware&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#rust-vec-sorting-is-not-locale-aware"&gt;Rust Vec sorting is not locale aware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#swifts-default-is-not-locale-aware-but-unicode-support-is-notable"&gt;Swift&amp;rsquo;s default is not locale aware, but unicode support is notable&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#zigs-ziglyph-package"&gt;Zig&amp;rsquo;s ziglyph package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#why-is-localized-text-sorting-hard"&gt;Why is localized text sorting hard?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#webassembly-may-make-things-worse"&gt;WebAssembly may make things worse?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="why-ascii-sorting-is-not-enough"&gt;Why ASCII sorting is not enough&lt;/h2&gt;
&lt;p&gt;Perhaps you are sorting strings in JavaScript like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Bears'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Beetle'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kiss'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Similar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Apples'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [ "Apples", "Bears", "Beetle", "Similar", "kiss" ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that works pretty well, until someone translates it to German:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Bären'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Käfer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'küssen'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ähnlich'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Äpfel'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [ "Bären", "Käfer", "küssen", "Ähnlich", "Äpfel" ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The preferred alphabetical sorting would be &lt;code&gt;[ &amp;quot;Ähnlich&amp;quot;, &amp;quot;Äpfel&amp;quot;, &amp;quot;Bären&amp;quot;, &amp;quot;Käfer&amp;quot;, &amp;quot;küssen&amp;quot; ]&lt;/code&gt; - &lt;code&gt;Array.sort&lt;/code&gt; doesn&amp;rsquo;t do that.&lt;/p&gt;
&lt;p&gt;That is because it is sorting lexicographically by byte values in the string, and not taking into account locales.&lt;/p&gt;
&lt;h2 id="twitters-emoji-problem---or-when-unicode-locale-aware-sorting-really-matters"&gt;Twitter&amp;rsquo;s emoji problem - or when Unicode locale-aware sorting Really Matters™&lt;/h2&gt;
&lt;p&gt;Twitter is &lt;a href="https://9to5google.com/2018/05/21/twitter-android-emoji-updates/"&gt;no stranger to issues with emojis&lt;/a&gt;, but have you ever thought about how they check if a hashtag contains only legal characters and emojis? Regexp, of course!&lt;/p&gt;
&lt;p&gt;You might think one could just use a regexp unicode character class, like &lt;code&gt;[\u{1f300}-\u{1f5ff}]&lt;/code&gt; - but that only covers a single codepoint! Emojis and other text rely on combining multiple Unicode codepoints to compose &lt;em&gt;grapheme clusters&lt;/em&gt; - and often what we see as a single visible character on our screen.&lt;/p&gt;
&lt;p&gt;The full regexp needed to match all emojis with codepoints would be:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-regexp"&gt;(?:\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c\udffc|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d
[102,816 characters omitted]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For your sake, I&amp;rsquo;ve omitted the other 102,816 characters of that regexp. You can view it here: &lt;a href="https://regex101.com/r/2ia4m2/7"&gt;https://regex101.com/r/2ia4m2/7&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="browsers-added-special-emoji-matching-to-regexp"&gt;Browsers added special emoji matching to regexp&lt;/h2&gt;
&lt;p&gt;Luckily for Twitter and others, ECMAScript&amp;rsquo;s &lt;a href="https://github.com/tc39/proposal-regexp-unicode-property-escapes"&gt;TC39 proposal a few years back&lt;/a&gt; extended the regexp engine to support Unicode property escapes for emojis and a few other Unicode properties so you can write e.g.:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-regexp"&gt;\p{Emoji_Presentation}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Without packing several thousand bytes of Unicode data tables or regexp into your JS bundle.&lt;/p&gt;
&lt;h2 id="language-comparison"&gt;Language comparison&lt;/h2&gt;
&lt;p&gt;As &lt;a href="https://lemire.me/blog/2018/12/17/sorting-strings-properly-is-stupidly-hard/"&gt;Daniel Lemire said&lt;/a&gt;: &lt;em&gt;sorting strings is stupidly hard&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="javascript-collator-sorting-is-not-guaranteed-across-browsers"&gt;JavaScript Collator sorting is not guaranteed across browsers&lt;/h3&gt;
&lt;p&gt;You may have found browser&amp;rsquo;s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare"&gt;&lt;code&gt;String.prototype.localCompare&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator"&gt;&lt;code&gt;Intl.Collator&lt;/code&gt;&lt;/a&gt; and they &lt;strong&gt;DO&lt;/strong&gt; fix the issue&lt;a href="https://ourcodeworld.com/articles/read/958/how-to-sort-an-array-of-strings-alphabetically-with-special-characters-properly-with-javascript"&gt;[1]&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const words = ['Bären', 'Käfer', 'küssen', 'Ähnlich', 'Äpfel'];
words.sort(Intl.Collator().compare);
// [ "Ähnlich", "Äpfel", "Bären", "Käfer", "küssen" ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(note, however, you may wish to use &lt;code&gt;Intl.Collator('de').compare&lt;/code&gt; instead to sort according to German language customs)&lt;/p&gt;
&lt;p&gt;However, beware that if you look at &lt;a href="https://tc39.es/ecma402/#sec-collator-comparestrings"&gt;the ECMA spec&lt;/a&gt; for this you will find:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is &lt;strong&gt;recommended&lt;/strong&gt; that the CompareStrings abstract operation be implemented following Unicode Technical Standard 10, Unicode Collation Algorithm [&amp;hellip;]&lt;/p&gt;
&lt;p&gt;Applications should not assume that the behaviour of the CompareStrings abstract operation for Collator instances with the same resolved options will remain the same for different versions of the same implementation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Although many browsers may produce similar sorting results - not all will. For one thing, not all locales are available across browsers.&lt;/p&gt;
&lt;p&gt;Further, different browsers may choose to sort things differently. For example IE 11 sorting &amp;ldquo;co-op&amp;rdquo; after &amp;ldquo;coop&amp;rdquo; while other browsers do the opposite.&lt;a href="https://stackoverflow.com/questions/33919257/sorting-strings-with-punctuation-using-intl-collator-is-inconsistent-across-brow"&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="go-sortstrings-is-not-locale-aware"&gt;Go sort.Strings is not locale aware&lt;/h2&gt;
&lt;p&gt;It may be interesting to note that Go&amp;rsquo;s &lt;code&gt;sort.Strings&lt;/code&gt; operates on byte comparisons, and has the same issue as JavaScript&amp;rsquo;s &lt;code&gt;Array.prototype.sort&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-Go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Bären"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Käfer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"küssen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ähnlich"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Äpfel"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Strings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [Bären Käfer küssen Ähnlich Äpfel]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One can easily perform unicode code point (rune) sorting in Go, which would fix the above example - but note that rune sorting is not locale-aware, and importantly that &lt;a href="https://www.reddit.com/r/golang/comments/o1o5hr/fyi_a_single_go_rune_is_not_the_same_as_a_single"&gt;a Go rune is not the same as a visible character&lt;/a&gt; and would not take into account grapheme clusters.&lt;/p&gt;
&lt;p&gt;For proper Unicode locale-aware sorting in Go, you need to use the Unicode Collation Algorithm via &lt;a href="https://pkg.go.dev/golang.org/x/text/collate"&gt;golang.org/x/text/collate&lt;/a&gt; but be sure to also apply normalization to your text first via &lt;a href="https://pkg.go.dev/golang.org/x/text@v0.3.6/unicode/norm"&gt;golang.org/x/text/unicode/norm&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="rust-vec-sorting-is-not-locale-aware"&gt;Rust Vec sorting is not locale aware&lt;/h3&gt;
&lt;p&gt;A Rust &lt;code&gt;Vec&lt;/code&gt; of strings implements sorting&lt;a href="https://doc.rust-lang.org/std/primitive.str.html#impl-Ord"&gt;[3]&lt;/a&gt; lexicographically by their byte values, consistent with Go&amp;rsquo;s &lt;code&gt;sort.Strings&lt;/code&gt; and JavaScripts &lt;code&gt;Array.prototype.sort&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let mut vec = Vec::new();
vec.push("Bären");
vec.push("Käfer");
vec.push("küssen");
vec.push("Ähnlich");
vec.sort("Äpfel");
println!("{:?}", vec);
// ["Bären", "Käfer", "küssen", "Ähnlich", "Äpfel"]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Locale-aware sorting in Rust is provided &lt;a href="https://github.com/google/rust_icu"&gt;by ICU4C bindings by Google, google/rust_icu&lt;/a&gt; (note however, there have been a number of &lt;a href="https://github.com/rust-lang/rust/issues/14656#issuecomment-45164318"&gt;vulnerabilities in the ICU4C library&lt;/a&gt;) and there is ongoing work to implement internationalization in pure Rust as a safer alternative: &lt;a href="https://github.com/unicode-org/icu4x"&gt;unicode-org/icu4x&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="swifts-default-is-not-locale-aware-but-unicode-support-is-notable"&gt;Swift&amp;rsquo;s default is not locale aware, but unicode support is notable&lt;/h3&gt;
&lt;p&gt;Swift remains consistent with other languages in sorting strings lexicographically by byte value:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;words&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Bären"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Käfer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"küssen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ähnlich"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Äpfel"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ["Bären", "Käfer", "küssen", "Ähnlich", "Äpfel"]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, it is notable that Swift includes locale sensitive sorting out of the box&lt;a href="https://sarunw.com/posts/different-ways-to-sort-array-of-strings-in-swift/"&gt;[4]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;words&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Bären"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Käfer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"küssen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ähnlich"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Äpfel"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sorted&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;sorted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Bool&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localizedStandardCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orderedAscending&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ["Ähnlich", "Äpfel", "Bären", "Käfer", "küssen"]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It also seems quite notable just &lt;a href="https://developer.apple.com/documentation/swift/string"&gt;how very unicode-aware the Swift documentation is on their String type&lt;/a&gt;. Other languages could learn a thing or two here in educating developers.&lt;/p&gt;
&lt;h2 id="zigs-ziglyph-package"&gt;Zig&amp;rsquo;s ziglyph package&lt;/h2&gt;
&lt;p&gt;Zig&amp;rsquo;s standard library is still quite under development, however it seems likely that major unicode functionality will be outside the stdlib.&lt;/p&gt;
&lt;p&gt;Luckily, &lt;a href="https://github.com/jecolon"&gt;@jecolon&lt;/a&gt; in the Zig community is working on an excellent package for this: &lt;a href="https://github.com/jecolon/ziglyph"&gt;ziglyph&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I mention this because I&amp;rsquo;m a fan of the language and have recently begun contributing to that package; but otherwise Zig isn&amp;rsquo;t any different than other languages listed here aside from there being no real &amp;ldquo;default&amp;rdquo; way to sort strings from what I know.&lt;/p&gt;
&lt;h2 id="why-is-localized-text-sorting-hard"&gt;Why is localized text sorting hard?&lt;/h2&gt;
&lt;p&gt;I believe there are a combination of factors at play:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Most languages leave Unicode locale-aware text sorting as an afterthought.&lt;/li&gt;
&lt;li&gt;Most developers don&amp;rsquo;t care enough to use Unicode, let alone implement locale-aware text sorting. Internationalization is always &amp;ldquo;that thing we&amp;rsquo;ll do if somebody complains&amp;rdquo; or an afterthought.&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s hard. It wasn&amp;rsquo;t until recently that we got semi-decent support for it across browsers, and what is there still leaves a lot to be desired.&lt;/li&gt;
&lt;li&gt;Many are still running into dated software, like NodeJS versions from ~2019 ish that &lt;a href="https://github.com/nodejs/node/issues/19214"&gt;didn&amp;rsquo;t have full ICU support on by default&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="webassembly-may-make-things-worse"&gt;WebAssembly may make things worse?&lt;/h2&gt;
&lt;p&gt;As a closing thought, I just want to hint at why I think WebAssembly will make things worse before they get better.&lt;/p&gt;
&lt;p&gt;Whether your application is in Go and has it&amp;rsquo;s own Unicode Collation Algorithm (UCA) implementation, or Rust and uses bindings to the popular ICU4C library - one thing is going to remain true: it requires large data files to work.&lt;/p&gt;
&lt;p&gt;The UCA algorithm depends on two quite large data table files to work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.unicode.org/Public/9.0.0/ucd/UnicodeData.txt"&gt;UnicodeData.txt&lt;/a&gt; for normalization, a step required before sorting can take place.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.unicode.org/Public/UCA/12.0.0/allkeys.txt"&gt;allkeys.txt&lt;/a&gt; for weighting certain text above others.&lt;/li&gt;
&lt;li&gt;And more, if you want truly locale-aware sorting and not just &amp;ldquo;the default&amp;rdquo; the UCA algorithm gives you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these files can add up to over a half a megabyte.&lt;/p&gt;
&lt;p&gt;While WASM languages could shell out to JavaScript browser APIs for collation, I suspect they won&amp;rsquo;t due to the lack of guarantees around those APIs.&lt;/p&gt;
&lt;p&gt;A more likely scenario is languages continuing to leave locale-aware sorting as an optional, opt-in feature - that also makes your application larger.&lt;/p&gt;
&lt;p&gt;I think this a worthwhile problem to solve, so I am working on &lt;a href="https://github.com/jecolon/ziglyph/issues/3"&gt;compression algorithms for these files specifically&lt;/a&gt; in Zig to reduce them to only a few tens of kilobytes.&lt;/p&gt;</description><author>Hexops' devlog</author><pubDate>Sun, 27 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://devlog.hexops.org/2021/unicode-sorting-why-browsers-added-special-emoji-matching/</guid></item><item><title>UNIX/Linux is now a core skill for anyone working in tech</title><link>https://jasoneckert.github.io/myblog/linux-is-now-a-core-skill/</link><description>&lt;p&gt;&lt;img alt="Tech Worker" src="techworker.png#center" title="Tech Worker" /&gt;&lt;/p&gt;
&lt;p&gt;If you had asked me in the 1990s whether most people in the tech world would be using a UNIX or Linux shell to manage their servers, I would have chuckled and said &amp;ldquo;No.&amp;rdquo; But two decades later we find ourselves in a world where 96% of the cloud is comprised of Linux servers running Web apps made with open source frameworks, and the majority of software development is done on UNIX (macOS) or Linux workstations. Nearly all network hardware, security appliances, smart appliances, cars, wearables and IoT devices also run UNIX or Linux, as do our smartphones and tablets. Want to secure your systems or network, or test your security using ethical hacking? You&amp;rsquo;ll need UNIX or Linux to do that as well.&lt;/p&gt;</description><author>Jason Eckert's Website and Blog</author><pubDate>Sun, 27 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jasoneckert.github.io/myblog/linux-is-now-a-core-skill/</guid></item><item><title>The easiest policy against negative impacts of Artificial Intelligence on children</title><link>https://stop.zona-m.net/2021/06/the-easiest-policy-against-negative-impacts-of-artificial-intelligence-on-children/</link><description>&lt;p&gt;It is much closer to children than most people think.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 26 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/the-easiest-policy-against-negative-impacts-of-artificial-intelligence-on-children/</guid></item><item><title>Cheminformatics on the Web (2021)</title><link>https://web.navan.dev/posts/2021-06-26-Cheminformatics-On-The-Web-2021.html</link><description>Summarising Cheminformatics on the web in 2021.</description><author>Navan's Archive</author><pubDate>Sat, 26 Jun 2021 16:04:00 GMT</pubDate><guid isPermaLink="true">https://web.navan.dev/posts/2021-06-26-Cheminformatics-On-The-Web-2021.html</guid></item><item><title>Focus vs Coordination</title><link>https://camhashemi.com/essays/focus-vs-coordination/</link><description>&lt;p&gt;Teamwork is a dance between focus and coordination.&lt;/p&gt;&lt;h2 id="focus"&gt;Focus&lt;/h2&gt;&lt;p&gt;The fundamental unit of focus is action. If a perfectly focused individual is in &amp;ldquo;flow&amp;rdquo;, a perfectly focused team flows like a well-oiled machine. Teammates work perfectly in parallel to produce maximum efficiency on the whole. These teams are completely silent in their work, but extremely loud in their results.&lt;/p&gt;&lt;p&gt;By contrast, unfocused teams spend their days scrambling from one thread to the next. They end those days exhausted, only to ask themselves &lt;em&gt;&amp;ldquo;what did I even do today?&amp;rdquo;&lt;/em&gt;. The answer is often to stay in late to get &amp;ldquo;real work&amp;rdquo; done, but overtime is not enough. As days without rain create droughts, days without focus lead to missed deadlines, production incidents, and low-quality products. Exhaustion plus failure leads to burnout. Unfocused teams face higher costs, more stress, and less output.&lt;/p&gt;&lt;p&gt;But focused teams don&amp;rsquo;t assemble themselves, nor do the products they assemble. To direct and capture the value that focus creates, we need coordination.&lt;/p&gt;&lt;h2 id="coordination"&gt;Coordination&lt;/h2&gt;&lt;p&gt;Teams are like cloth-makers. Focus creates threads that coordination weaves into a cohesive whole. Seamless products are the result of perfect coordination.&lt;/p&gt;&lt;p&gt;Although focus is killed by communication, coordination is borne from it. Whether through chat, email, docs, stand-ups, all-hands, plannings, or groomings: communication coordinates. If the fundamental unit of focus is action, the fundamental unit of coordination is information.&lt;/p&gt;&lt;p&gt;And perfectly coordinated teammates have perfect information. Small meetings establish plans, large meetings share plans, and regular syncs maintain them. As new information is discovered, coordinated teams adapt quickly and effectively. A perfectly coordinated team has neither secrets nor surprises between its members.&lt;/p&gt;&lt;p&gt;But project-based coordination only scratches the surface. Like an elite military unit or world-class sports team, perfectly coordinated teams are connected on a human level. When it&amp;rsquo;s time to focus, the fewest bits of information keep these teams in sync. These teams feel understood and fulfilled in ways that isolated focus can&amp;rsquo;t provide. Each teammate collaborates with, complements, and supports one another; and each member is made to play a vital role in the team&amp;rsquo;s success.&lt;/p&gt;&lt;p&gt;And if a coordinated team is like a complex engine, an uncoordinated team is like a dryer with a brick thrown in it. Sure, the machine spins&amp;hellip; but each spin chips away at the integrity of the team and their product. Initial plans are absent or unclear, progress is made in conflicting directions, and releases are halted by statements like &amp;ldquo;wait, I didn&amp;rsquo;t know we were doing it like &lt;em&gt;that!&lt;/em&gt;&amp;rdquo; Coordination failures &lt;em&gt;disconnect&lt;/em&gt; us on a human level. Uncoordinated teammates suffer from either conflict or indifference; they either feel at war with their teammates, or like cogs in a useless machine.&lt;/p&gt;&lt;h2 id="the-tension"&gt;The Tension&lt;/h2&gt;&lt;p&gt;Teams need both focus and coordination to thrive. Teams without focus are unproductive, while teams without coordination are counterproductive. A team without focus may be on the same page, but that page never turns. And a team without coordination may turn many pages, without ever stopping to ask if the book makes any sense.&lt;/p&gt;&lt;p&gt;But there&amp;rsquo;s a tension between our needs and our capabilities. &lt;strong&gt;We cannot focus and coordinate at the same time&lt;/strong&gt;. Focus demands attention towards problems, while coordination demands attention towards people. Focus and coordination are opposing forces, pulling on the single thread of attention. This tension creates the focus-coordination tradeoff.&lt;/p&gt;&lt;p&gt;If you&amp;rsquo;ve ever tried working during a meeting, you&amp;rsquo;ve personally felt the pull of this tension. There&amp;rsquo;s either work &lt;em&gt;or&lt;/em&gt; a meeting &amp;ndash; never both. The switch away from work to the meeting is always followed by a panicked prayer that we can catch up on the conversation. And the switch back from the meeting to work is always met with a few dumbfounded seconds of &amp;ldquo;okay, what am I looking at again?&amp;rdquo;&lt;/p&gt;&lt;p&gt;It&amp;rsquo;s hard enough to focus in isolation without distracting ourselves. And it&amp;rsquo;s hard enough to coordinate, with everyone in the same room, without communication breaking down. As individuals, focusing and coordinating at the exact same time is simply impossible.&lt;/p&gt;&lt;p&gt;But the problem is worse than that: we can&amp;rsquo;t even focus and coordinate &lt;em&gt;near&lt;/em&gt; the same time. When we context-switch between focus and coordination, our minds stash mental bits from the previous task to free up resources for the next. Like computers, these stashing-and-loading costs add up when we frequently switch between tasks. But for computers, context-switching is merely slow&amp;hellip;&lt;/p&gt;&lt;p&gt;Human context-switching is like a child cleaning their room. Some toys seem to fall down a black hole, never to return, while other toys remain scattered across the floor, ever in the way. We forget some things and &lt;em&gt;fail&lt;/em&gt; to forget others, and both faults prevent us from fully attending to the task at hand. So while focus and coordination in parallel are impossible, context-switching between them is only deceivingly possible.&lt;/p&gt;&lt;h3 id="the-spectrum"&gt;The Spectrum&lt;/h3&gt;&lt;p&gt;Thankfully, focus and coordination are not binary categories: they&amp;rsquo;re colors of a spectrum. On one side of the spectrum, there&amp;rsquo;s focused, isolated &amp;ldquo;deep work&amp;rdquo;. On the other side, there&amp;rsquo;s all-hands meetings, where plans are shared, questions are answered, and excitement builds in anticipation of what&amp;rsquo;s to come. But between these extremes, there&amp;rsquo;s a variety of work that mixes focus and coordination in different ways.&lt;/p&gt;&lt;p&gt;Pair programming is an example of &amp;ldquo;coordinated focus&amp;rdquo;. Rather than working in parallel isolation, pair programming has two coders tackle the same problem at a single computer. These sessions are a classic exchange of focus for coordination. The most focused pairing session is one where both partners agree on every decision without discussion; but in that case, pairing was not very useful! The pair would have achieved the same result, and more, by working in parallel. But if the pair disagrees on every decision, their energy is spent resolving differences instead of moving forward on the problem at hand. The more energy we spend exchanging information, the less energy we have to put that information to use.&lt;/p&gt;&lt;p&gt;And if pair programming sessions are &amp;ldquo;coordinated focus&amp;rdquo;, then group planning sessions are &amp;ldquo;focused coordination&amp;rdquo;. Great all-hands meetings rarely start with: &amp;ldquo;alright everyone: what&amp;rsquo;s the plan?&amp;rdquo; But transforming dark, cloudy horizons into bright and compelling futures is the explicit purpose of planning sessions. Planning sessions &lt;em&gt;should&lt;/em&gt; foster divergent ideas, then converge those ideas into a coordinated plan. But in order to pull off that transformation, these sessions need to be focused. Unfocused planning sessions feel like sprinting in place, with lots of talk and nothing to show for it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The more people we add to a focus session, the harder it will be to focus&lt;/strong&gt;. More time will be spent aligning ourselves instead of moving forward, and the more chances there are that communication either blows up or falls flat. Anyone who&amp;rsquo;s had a stressful but unproductive meeting knows this pain. &lt;strong&gt;Conversely, smaller groups are more focused, but their output will be less coordinated&lt;/strong&gt;. Useful perspectives are more likely to be missing, and more people will need to be informed after the fact. You know this pain if you&amp;rsquo;ve ever come out of a meeting with an exciting plan, only to learn that important information was missing because critical people were not involved. This tradeoff between moving forward and moving together is just the focus-coordination tradeoff at play.&lt;/p&gt;&lt;h2 id="the-balance"&gt;The Balance&lt;/h2&gt;&lt;p&gt;So, how do we navigate this tension?&lt;/p&gt;&lt;p&gt;To answer that question, two types of communicators emerge: hippies and soldiers. On one side, hippies believe in open communication. They feel that their simple-yet-powerful purpose is to bring people together. Soldiers, on the other side, believe in disciplined communication. They punish distractions, create handbooks, and impose rules. Their mission is to get their team walking the walk instead of just talking about it. Hippies stand for coordination, while soldiers fight for focus.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;While hippies and soldiers have their place, neither can win the tug-of-war between focus and coordination&lt;/strong&gt;. One style does not fit all, because our needs for focus and coordination change across multiple dimensions over time. The best way to navigate the focus-coordination spectrum is to constantly harmonize these dimensions as they dance.&lt;/p&gt;&lt;h3 id="the-dimensions"&gt;The Dimensions&lt;/h3&gt;&lt;p&gt;And what are those dimensions?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;As we saw earlier, group size is a critical dimension of the focus-coordination tradeoff&lt;/strong&gt;. Group size includes who we invite to meetings, who we send messages to, and who we choose to collaborate on a project. When our teams aren&amp;rsquo;t turning enough pages, we should limit group sizes in favor of more focused sessions. But when our teams are struggling to coordinate, we should encourage larger group sizes, so that people can figure out how to move gracefully together instead of in an uncoordinated chaos.&lt;/p&gt;&lt;p&gt;Another dimension where focus and coordination vary is people. &lt;strong&gt;Some teammates are suited for focus, while others are suited for coordination&lt;/strong&gt;. The clearest such distinction is between managers and individual contributors (or &amp;ldquo;ICs&amp;rdquo;). Managers are explicitly &amp;ldquo;coordinators&amp;rdquo;, while ICs are explicitly &amp;ldquo;concentrators&amp;rdquo;. A common cause for imbalance is when managers isolate themselves for &amp;ldquo;focus time&amp;rdquo;, as their team struggles to get on the same page. The converse mistake is when everyone wants to manage instead of doing the work. Zooming out a bit, this same balance applies across teams of people. We should be worried if our sales team is as silently focused as the engineering team should be, or if the engineering team is as chatty as the sales team should be. The balance between &amp;ldquo;coordinators&amp;rdquo; and &amp;ldquo;concentrators&amp;rdquo; within and across teams is critical to a company&amp;rsquo;s success.&lt;/p&gt;&lt;p&gt;We can also balance coordinators and concentrators by rotating coordination responsibilities. A good example of this is an on-call rotation, where engineers take turns being coordinators for a week, so that the rest of the team can focus. Other examples include stand-up leaders, meeting scribes, and release managers. &lt;strong&gt;By regularly rotating these responsibilities, we achieve coordination as a team, even though most teammates are fully focused on their problems at hand&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Another important lever we have is how we communicate&lt;/strong&gt;. In the dance between focus and coordination, communication is our basic step. Ineffective communicators blast trivial messages through large, coordination-heavy channels, or slip urgent messages into quiet, focus-optimized channels. This is where the discussion between synchronous vs asynchronous communication comes in. Effective communicators choose a medium that best balances the focus-coordination needs of their message, their team, and the moment.&lt;/p&gt;&lt;p&gt;But the critical dimension where focus and coordination vary is time. &lt;strong&gt;Healthy teams know which moments require more focus, and which ones require more coordination&lt;/strong&gt;. Failing teams call meetings when focus is needed and go into isolation when coordination is needed. And even deeper: all of the above dimensions &lt;em&gt;change&lt;/em&gt; over time. Teams grow and shrink, personalities come and go, and tools are added and removed from our communication stacks. &lt;strong&gt;Our changing focus-coordination needs require us to constantly adapt to the present&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;&lt;p&gt;The focus-coordination tradeoff is constant in an ever-changing workplace. Agile vs waterfall, sync vs async, remote vs colocated: all discussions about teamwork sit atop the focus-coordination spectrum. And since the focus-coordination spectrum has several dimensions, which each changing over time, there is not one answer to all these questions, only those that better or worse fit a team&amp;rsquo;s current context. The answer here isn&amp;rsquo;t just &amp;ldquo;it depends&amp;rdquo;: it&amp;rsquo;s an investigation of how it depends and what it depends on. As teammates and as leaders, we need to constantly listen to our team&amp;rsquo;s focus and coordination needs, and adapt accordingly.&lt;/p&gt;</description><author>Cam Hashemi</author><pubDate>Sat, 26 Jun 2021 11:47:29 GMT</pubDate><guid isPermaLink="true">https://camhashemi.com/essays/focus-vs-coordination/</guid></item><item><title>The Particle/Wave Duality Theory of Knowledge</title><link>https://www.swyx.io/particle-wave-duality</link><description>Learning is BOTH a discrete and a continuous process. If the tools we use don't respect this duality, information is lost — either writing involves too much effort, or reading requires too much context.</description><author>swyx's site RSS Feed</author><pubDate>Fri, 25 Jun 2021 23:29:35 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/particle-wave-duality</guid></item><item><title>Facebook can ban you for two hundred, Fifty THOUSANDS years</title><link>https://stop.zona-m.net/2021/06/facebook-can-ban-you-for-two-hundred-fifty-thousands-years/</link><description>&lt;p&gt;No, not really that long. But it&amp;rsquo;s too stupid anyway.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 25 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/facebook-can-ban-you-for-two-hundred-fifty-thousands-years/</guid></item><item><title>Basic NFC Music Cards for iOS</title><link>https://web.navan.dev/posts/2021-06-25-NFC-Music-Cards-Basic-iOS.html</link><description>Basic NFC Music Cards on iOS with Shortcuts</description><author>Navan's Archive</author><pubDate>Fri, 25 Jun 2021 19:20:00 GMT</pubDate><guid isPermaLink="true">https://web.navan.dev/posts/2021-06-25-NFC-Music-Cards-Basic-iOS.html</guid></item><item><title>TIL that nginx http_limit_conn module can wreak havoc to your website</title><link>https://www.zufallsheld.de/2021/06/25/til-that-nginx-http_limit_conn-module-can-wreak-havoc-to-your-website/</link><description>&lt;p&gt;The &lt;a href="https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html"&gt;nginx http_limit_conn&lt;/a&gt; module to limit http connections works as advertised. It limits the number of connections an ip-address (or other directives) can make to the&amp;nbsp;nginx.&lt;/p&gt;
&lt;p&gt;Also: the Ansible hardening-role for nginx as a default limit of 5 connections (&lt;a href="https://github.com/dev-sec/ansible-collection-hardening/blob/d72db6037c32cca05f0a966eca753f23d591e40a/roles/nginx_hardening/defaults/main.yml#L13"&gt;see&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This bit me hard: One website I host makes …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Fri, 25 Jun 2021 16:42:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/06/25/til-that-nginx-http_limit_conn-module-can-wreak-havoc-to-your-website/</guid></item><item><title>Convert Python Scripts to Windows Executables (.py to .exe) - Pyinstaller and Pandas | 2022 WORKING VERSION</title><link>https://geo.rocks/post/python-to-exe/</link><description>&lt;p&gt;Python and pandas are a great team for data science. But what if you need to deliver a script to a Windows client who does not have Python installed?&lt;/p&gt;
&lt;p&gt;This is a quick tutorial for converting Python scripts (&lt;code&gt;.py&lt;/code&gt;) to standalone, one-file Windows executables (&lt;code&gt;.exe&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pyinstaller -F my_script.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Get a 20Mb exe file that is fully functional on every Windows machine!&lt;/strong&gt;&lt;/p&gt;</description><author>Geography &amp;amp; Coding</author><pubDate>Fri, 25 Jun 2021 10:53:27 GMT</pubDate><guid isPermaLink="true">https://geo.rocks/post/python-to-exe/</guid></item><item><title>Posting Blog Posts as Twitter Threads Part 1/n</title><link>https://web.navan.dev/posts/2021-06-25-Blog2Twitter-P1.html</link><description>Converting Posts to Twitter Threads</description><author>Navan's Archive</author><pubDate>Fri, 25 Jun 2021 03:08:00 GMT</pubDate><guid isPermaLink="true">https://web.navan.dev/posts/2021-06-25-Blog2Twitter-P1.html</guid></item><item><title>Foxtrot</title><link>https://mattkeeter.com/projects/foxtrot</link><description>A fast STEP file viewer</description><author>Matt Keeter</author><pubDate>Fri, 25 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://mattkeeter.com/projects/foxtrot</guid></item><item><title>Richer, Wiser, Happier: How the World's Greatest Investors Win in Markets and Life</title><link>https://olshansky.info/book/richer_wiser_happier/</link><description>Olshansky's review of Richer, Wiser, Happier: How the World's Greatest Investors Win in Markets and Life by William Green</description><author>🦉 olshansky 🦁</author><pubDate>Fri, 25 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/book/richer_wiser_happier/</guid></item><item><title>S'More Ice Cream</title><link>https://dustin.lammiman.ca/icecream/smore/</link><description>&lt;h2 id="s-more-ice-cream" tabindex="-1"&gt;S'More Ice Cream &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#s-more-ice-cream"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#ingredients"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;100g Marshmallows (I used 4 Jumbo No Name Marshmallows)&lt;/li&gt;
&lt;li&gt;1/4 Cup Granulated Sugar&lt;/li&gt;
&lt;li&gt;2 Tbsp Skim Milk Powder&lt;/li&gt;
&lt;li&gt;1/4 Tsp Xanthan Gum&lt;/li&gt;
&lt;li&gt;1 1/3 Cups Milk (I used 2%)&lt;/li&gt;
&lt;li&gt;2 Tbsp Light Corn Syrup&lt;/li&gt;
&lt;li&gt;1 1/3 Cup Whipping Cream&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#instructions"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Place marshmallows on a piece of parchment paper on an 8x12 cookie sheet. Broil in oven until tops are well toasted (almost burnt, but not quite). Rotate and repeat until all side of the mashmallows are toasted.&lt;/li&gt;
&lt;li&gt;In a bowl whisk together sugar, milk powder, and xanthan gum.&lt;/li&gt;
&lt;li&gt;In a pot add milk and corn syrup.&lt;/li&gt;
&lt;li&gt;Whisk dry ingredients into the pot of liquid ingredients. Be sure to mix well.&lt;/li&gt;
&lt;li&gt;Add toasted marshmallows and heat ingredients for ~5 minutes on medium heat, stirring well, until sugar is disolved.&lt;/li&gt;
&lt;li&gt;Add remaining cream and allow mixture to cool for a few minutes.&lt;/li&gt;
&lt;li&gt;Blend mixture in a blender, then place in a sealed contained. Refridgerate for 12+ hours.&lt;/li&gt;
&lt;li&gt;Churn according to your ice cream maker's instructions.&lt;/li&gt;
&lt;li&gt;About 5 minutes before churning is complete, add graham cracker chunks and ganache bits.&lt;/li&gt;
&lt;li&gt;Layer about 1/2 of the ice cream into a container, then top with chocolate syrup, and layer the remaining ice cream on top. Freeze for 4+ hours before serving.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="graham-cracker-chunks" tabindex="-1"&gt;Graham Cracker Chunks &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#graham-cracker-chunks"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients-1" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#ingredients-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;6 Whole Graham Crackers&lt;/li&gt;
&lt;li&gt;1/3 Cup Brown Sugar&lt;/li&gt;
&lt;li&gt;3 Tbsp Melted Butter&lt;/li&gt;
&lt;li&gt;1/2 Tsp Vanilla&lt;/li&gt;
&lt;li&gt;1/2 Tsp Salt&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions-1" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#instructions-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Preheat over to 350 F&lt;/li&gt;
&lt;li&gt;Use your hands to crush graham crackers into a mixing bowl. Some chunks should be bite size, some smaller.&lt;/li&gt;
&lt;li&gt;Add brown sugar, salt, vanilla, and butter and mix well.&lt;/li&gt;
&lt;li&gt;Line a cookie sheet with parchment paper. Spread mixture onto cookie sheet.&lt;/li&gt;
&lt;li&gt;Bake for about 10 minutes&lt;/li&gt;
&lt;li&gt;Once cooled, break into pieces and store in a container until ready to add to ice cream&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="ganache-bits" tabindex="-1"&gt;Ganache Bits &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#ganache-bits"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients-2" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#ingredients-2"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;2/3 Cup Whipping Cream&lt;/li&gt;
&lt;li&gt;1 Tbsp Granulated Sugar&lt;/li&gt;
&lt;li&gt;1 Cup Chocolate (I used 80g of 80% Dark Chocolate + 2/3 Cup Chocolate Chips)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions-2" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#instructions-2"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Mix cream and sugar in a pot. Stir well and heat until the mixture comes to a simmer.&lt;/li&gt;
&lt;li&gt;Remove mixture from the heat and add chocolate. Stir well for about 2 minutes.&lt;/li&gt;
&lt;li&gt;Place mixture in a shallow airtight container and freeze for at least 2 hours.&lt;/li&gt;
&lt;li&gt;Before adding to your ice cream, cut into small cubes.&lt;/li&gt;
&lt;li&gt;NOTE: Use 1/2 of this ganache in a batch of ice cream.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="chocolate-syrup" tabindex="-1"&gt;Chocolate Syrup &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#chocolate-syrup"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ingredients-3" tabindex="-1"&gt;Ingredients &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#ingredients-3"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;1/4 Cup Cocoa Powder&lt;/li&gt;
&lt;li&gt;1/4 Cup Sugar&lt;/li&gt;
&lt;li&gt;1/4 Cup Water&lt;/li&gt;
&lt;li&gt;Pinch of Salt&lt;/li&gt;
&lt;li&gt;1/2 Tsp Vanilla&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="instructions-3" tabindex="-1"&gt;Instructions &lt;a class="direct-link" href="https://dustin.lammiman.ca/icecream/smore/#instructions-3"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Whisk together cocoa and sugar in a saucepan.&lt;/li&gt;
&lt;li&gt;Add water and salt and bring to a boil over medium-high heat, continuing to whisk the mixture.&lt;/li&gt;
&lt;li&gt;Reduce to a simmer and continue to whisk for about 30 seconds.&lt;/li&gt;
&lt;li&gt;Remove from heat and add vanilla.&lt;/li&gt;
&lt;li&gt;Store in the refridgerator in a sealed container.&lt;/li&gt;
&lt;/ol&gt;</description><author>Dustin.Lammiman</author><pubDate>Fri, 25 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/icecream/smore/</guid></item><item><title>How to Swap Column Values in SQL</title><link>https://www.dannyguo.com/blog/how-to-swap-column-values-in-sql</link><author>Danny Guo</author><pubDate>Fri, 25 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.dannyguo.com/blog/how-to-swap-column-values-in-sql</guid></item><item><title>Controlled HTML select element in React has weird default UX</title><link>http://notes.eatonphil.com/controlled-select-element-in-react-has-weird-ux.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-06-25-select-in-react-broken-by-default.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Fri, 25 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/controlled-select-element-in-react-has-weird-ux.html</guid></item><item><title>Tech is the new oil in Saudi Arabia</title><link>https://stop.zona-m.net/2021/06/tech-is-the-new-oil-in-saudi-arabia/</link><description>&lt;p&gt;As in &amp;ldquo;Follow the money, especially when it looks smart&amp;rdquo;.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 25 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/tech-is-the-new-oil-in-saudi-arabia/</guid></item><item><title>zef.studio</title><link>https://zef.studio/journal/zef-studio</link><description>Thoughts about my new website, zef.studio.</description><author>Zef Houssney</author><pubDate>Thu, 24 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://zef.studio/journal/zef-studio</guid></item><item><title>TIL there’s a limit on Gitlabs job output</title><link>https://www.zufallsheld.de/2021/06/24/til-theres-a-limit-on-gitlabs-job-output/</link><description>&lt;p&gt;And it is (at least for my instance) 4194304 bytes. That is 4,19 mb. You can easily achieve this by running some Ansible playbooks in diff-mode that unpack many files.&amp;nbsp;:)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Job's log exceeded limit of 4194304 bytes.

Job execution will continue but no more output will be collected.
&lt;/code&gt;&lt;/pre&gt;</description><author>zufallsheld</author><pubDate>Thu, 24 Jun 2021 15:55:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/06/24/til-theres-a-limit-on-gitlabs-job-output/</guid></item><item><title>Virtual 6502 — Update Round 2</title><link>https://www.masswerk.at/nowgobang/2021/virtual6502-update2</link><description>Another major update to the venerable “Virtual 6502” emulator – assembler – disassembler suite.</description><author>mass:werk – Now Go Bang!</author><pubDate>Thu, 24 Jun 2021 11:28:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/virtual6502-update2</guid></item><item><title>The Evening Redness in the West</title><link>https://blog.janissary.xyz/posts/blood-meridian</link><author>janissary</author><pubDate>Thu, 24 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.janissary.xyz/posts/blood-meridian</guid></item><item><title>Nothing new about online voting</title><link>https://stop.zona-m.net/2021/06/nothing-new-about-online-voting/</link><description>&lt;p&gt;Of course.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 24 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/nothing-new-about-online-voting/</guid></item><item><title>Solving Machine Learning Performance Anti-Patterns: a Systematic Approach</title><link>https://paulbridger.com/posts/nsight-systems-systematic-optimization/</link><description>This article is a high-level introduction to an efficient worfklow for optimizing runtime performance of machine learning systems running on the GPU. Using traces from Nsight Systems to show real production scenarios, I introduce a set of common utilization patterns and outline effective approaches to improve performance.</description><author>Machine Learning Articles on paulbridger.com</author><pubDate>Thu, 24 Jun 2021 01:02:03 GMT</pubDate><guid isPermaLink="true">https://paulbridger.com/posts/nsight-systems-systematic-optimization/</guid></item><item><title>Dark Mode in SvelteKit with and without JavaScript</title><link>https://www.davidwparker.com/posts/dark-mode-in-sveltekit-with-and-without-javascript</link><description>Dark mode is all the rage. In 2021, SvelteKit provides a dead simple way to implement a dark mode toggle that works for all users- regardless of their JavaScript preference.</description><author>David W Parker</author><pubDate>Thu, 24 Jun 2021 00:31:57 GMT</pubDate><guid isPermaLink="true">https://www.davidwparker.com/posts/dark-mode-in-sveltekit-with-and-without-javascript</guid></item><item><title>I made 56874 calls to explore the telephone network. Here's what I found</title><link>http://shufflingbytes.com/posts/wardialing-finnish-freephones/</link><description>What kind of systems are there in the Finnish telephone network today?
This question appeared to me a while ago when leafing through an old telephone directory from the early 2000s.
There are at least ordinary subscribers, business customer support lines, voice mails, and fax machines. Subscribers and customer support lines are not of special interest to me. I am more interested in automated systems that let the caller interact with them somehow.</description><author>Valtteri Lehtinen</author><pubDate>Wed, 23 Jun 2021 11:06:16 GMT</pubDate><guid isPermaLink="true">http://shufflingbytes.com/posts/wardialing-finnish-freephones/</guid></item><item><title>About</title><link>https://evancordell.com/about/</link><description>I work at RedHat, formerly CoreOS.
Sometimes I post things.</description><author>Evan Cordell</author><pubDate>Wed, 23 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://evancordell.com/about/</guid></item><item><title>My LRPS Panel</title><link>/photography/2021/06/23/lrps_panel.html</link><description>I'm very happy to have achieved my LRPS. Here's my successful LRPS panel</description><author>Ben Taylor</author><pubDate>Wed, 23 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/photography/2021/06/23/lrps_panel.html</guid></item><item><title>Why Immuni failed</title><link>https://stop.zona-m.net/2021/06/why-immuni-failed/</link><description>&lt;p&gt;Contact tracing failed in Italy, and it&amp;rsquo;s important to know WHY.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 23 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/why-immuni-failed/</guid></item><item><title>PipeWire Under The Hood</title><link>https://venam.net/blog/unix/2021/06/23/pipewire-under-the-hood.html</link><description>The PipeWire project is slowly getting popular as it matures. Its documentation is still relatively sparse but is gradually growing. However, it's always a good idea to have people from outside the project try to grasp and explain it to others in their own words, reiterating ideas, seeing them from their own perspective.</description><author>Venam's Blog — Patrick Louis (Lebanon)</author><pubDate>Wed, 23 Jun 2021 00:00:00 GMT</pubDate><guid isPermaLink="true">https://venam.net/blog/unix/2021/06/23/pipewire-under-the-hood.html</guid></item><item><title>The 4 Types of Activity timeouts in Temporal</title><link>https://www.swyx.io/temporal-activity-timeouts</link><description>This post (together with the embedded talk) aims to give you a solid mental model on what each Activity timeout in Temporal does and when to use it.</description><author>swyx's site RSS Feed</author><pubDate>Tue, 22 Jun 2021 23:57:31 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/temporal-activity-timeouts</guid></item><item><title>Ninja is a surprisingly nice build system for tiny projects</title><link>https://duncanlock.net/blog/2021/06/22/ninja-is-a-surprisingly-nice-build-system-for-tiny-projects/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;&lt;a href="https://ninja-build.org/"&gt;Ninja&lt;/a&gt; is a surprisingly nice build system for tiny projects. That&amp;#8217;s more or less the opposite of what it&amp;#8217;s designed for, but it works really well for tiny things&amp;#8201;&amp;#8211;&amp;#8201;things that are small enough that you can just create the little build file by hand. You just need to &lt;a href="https://github.com/ninja-build/ninja/releases"&gt;install it (you can just put the binary on the $&lt;span class="caps"&gt;PATH&lt;/span&gt; somewhere)&lt;/a&gt;, then create a &lt;code&gt;ninja.build&lt;/code&gt; file in your folder. Here&amp;#8217;s an example that published a couple of AsciiDoc files and re-built the index if any of the pages&amp;nbsp;changed:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;opts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;highlighter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rouge&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;rouge&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;monokai&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;asciidoctor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;html5s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;html5s&lt;/span&gt;

&lt;span class="nt"&gt;rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;adoc
  &lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asciidoctor&lt;/span&gt; &lt;span class="vg"&gt;$opts&lt;/span&gt; &lt;span class="vg"&gt;$in&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="vg"&gt;$out&lt;/span&gt;

&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out/page1&lt;span class="nc"&gt;.html&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;adoc&lt;span class="w"&gt; &lt;/span&gt;src/page1&lt;span class="nc"&gt;.adoc&lt;/span&gt;
&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out/page2&lt;span class="nc"&gt;.html&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;adoc&lt;span class="w"&gt; &lt;/span&gt;src/page2&lt;span class="nc"&gt;.adoc&lt;/span&gt;
&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out/index&lt;span class="nc"&gt;.html&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;adoc&lt;span class="w"&gt; &lt;/span&gt;src/index&lt;span class="nc"&gt;.adoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;|&lt;span class="w"&gt; &lt;/span&gt;src/page1&lt;span class="nc"&gt;.adoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;src/page2&lt;span class="nc"&gt;.adoc&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s super fast, incremental &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; idempotent&amp;#8201;&amp;#8211;&amp;#8201;just re-run it and it&amp;#8217;ll just redo anything that needs doing&amp;#8201;&amp;#8211;&amp;#8201;and &lt;em&gt;nothing that doesn&amp;#8217;t&lt;/em&gt;.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://ninja-build.org/manual.html#_writing_your_own_ninja_files"&gt;Writing Your Own Ninja&amp;nbsp;Files&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://jvns.ca/blog/2020/10/26/ninja&amp;#8211;a-simple-way-to-do-builds/"&gt;ninja: a simple way to do&amp;nbsp;builds&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 22 Jun 2021 23:39:25 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/22/ninja-is-a-surprisingly-nice-build-system-for-tiny-projects/</guid></item><item><title>Gadget - Top-of-page categories</title><link>https://river.me/blog/top-of-page-categories/</link><description>A gadget to show categories at the top of pages</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 22 Jun 2021 03:27:26 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/top-of-page-categories/</guid></item><item><title>Leaders, you need to share organization success stories more frequently</title><link>http://notes.eatonphil.com/leaders-share-company-success-stories.html</link><description>&lt;p&gt;This post goes out to anyone who leads a team: managers, directors,
VPs, executives. You need to share organization success stories with
your organization on a regular and frequent basis. Talk about sales
wins, talk about new services released, talk about the positive impact
of a recent organizational change. Just get in front of your entire
organization and tell them how the organization is making a positive
difference.&lt;/p&gt;
&lt;p&gt;Do this at least every other week.&lt;/p&gt;
&lt;p&gt;And in case it's not clear, by "success stories" I don't mean nonsense,
or opinions. I mean concrete, measurable things that moved the
organization forward.&lt;/p&gt;
&lt;p&gt;Everyone in your organization is contributing to these stories and
it's your job to feed the stories back.&lt;/p&gt;
&lt;p&gt;Leaders have a tendency to hear about successes but don't always
remember to propagate the stories down. I've been guilty of this
myself. This post is your (and my own) friendly reminder.&lt;/p&gt;
&lt;p&gt;If you don't keep reminding your folks their organization is making a
positive impact, they're going to forget it. You'll miss out on the
freely available chance to give reassurance to your best people.&lt;/p&gt;
&lt;p&gt;Talented folks want to be invested in an organization that is
succeeding.&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Wrote a post for all the people managers, directors, VPs out there: you need to regularly share success stories with your whole organization. Everyone wants to be part of an organization that is doing good work.&lt;a href="https://t.co/XgaY5Ri1tA"&gt;https://t.co/XgaY5Ri1tA&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1407451413156929537?ref_src=twsrc%5Etfw"&gt;June 22, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Tue, 22 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/leaders-share-company-success-stories.html</guid></item><item><title>Stop silencing the world</title><link>https://stop.zona-m.net/2021/06/stop-silencing-the-world/</link><description>&lt;p&gt;No matter how great it feels, it&amp;rsquo;s bad.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 22 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/stop-silencing-the-world/</guid></item><item><title>Better content-type guessing in AWS CLI</title><link>https://duncanlock.net/blog/2021/06/21/better-content-type-guessing-in-aws-cli/</link><description>&lt;p&gt;The content-type guessing done by &lt;span class="caps"&gt;AWS&lt;/span&gt; &lt;span class="caps"&gt;CLI&lt;/span&gt; is based on the mimetype definitions available on your system. You can improve the mimetype guessing by updating these&amp;nbsp;definitions.&lt;/p&gt;
&lt;p&gt;It uses the python &lt;code&gt;mimetypes&lt;/code&gt; lib to do this and &lt;a href="https://github.com/python/cpython/blob/cedc9b74202d8c1ae39bca261cbb45d42ed54d45/Lib/mimetypes.py#L42-L52"&gt;accumulates its list of mimetyoes from the following files&lt;/a&gt;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;knownfiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"/etc/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"/etc/httpd/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;# Mac OS X
&lt;/span&gt;    &lt;span class="s"&gt;"/etc/httpd/conf/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;# Apache
&lt;/span&gt;    &lt;span class="s"&gt;"/etc/apache/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;# Apache 1
&lt;/span&gt;    &lt;span class="s"&gt;"/etc/apache2/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;# Apache 2
&lt;/span&gt;    &lt;span class="s"&gt;"/usr/local/etc/httpd/conf/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"/usr/local/lib/netscape/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"/usr/local/etc/httpd/conf/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# Apache 1.2
&lt;/span&gt;    &lt;span class="s"&gt;"/usr/local/etc/mime.types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;# Apache 1.3
&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I would suggest leaving &lt;code&gt;/etc/mime.types&lt;/code&gt; (and any of the other files in this list that already exist on your system) alone. I would create one of the files that you don&amp;#8217;t have&amp;#8201;&amp;#8211;&amp;#8201;something like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/apache2
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apache2/mime.types&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This downloads the latest mimetypes definitions from the Apache project and saves it as &lt;code&gt;/etc/apache2/mime.types&lt;/code&gt;. I don&amp;#8217;t have the Apache web server installed on this system, so that file wasn&amp;#8217;t there before. It &lt;a href="https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types"&gt;looks like&lt;/a&gt; this file get updated about once a&amp;nbsp;year.&lt;/p&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 22 Jun 2021 00:59:26 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/21/better-content-type-guessing-in-aws-cli/</guid></item><item><title>Fixing content types in s3 using the AWS CLI</title><link>https://duncanlock.net/blog/2021/06/21/fixing-content-types-in-s3-using-the-aws-cli/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;I had an issue where the &lt;span class="caps"&gt;AWS&lt;/span&gt; &lt;span class="caps"&gt;CLI&lt;/span&gt; wasn&amp;#8217;t guessing the content-type of &lt;span class="caps"&gt;SVG&lt;/span&gt; files correctly on sync and was setting them to &lt;code&gt;application/octet-stream&lt;/code&gt; - the default &amp;#8220;I don&amp;#8217;t know&amp;#8221; mimetype. &lt;a href="https://duncanlock.net/blog/2021/06/21/better-content-type-guessing-in-aws-cli/"&gt;There&amp;#8217;s a proper fix to make the mimetype guessing work here&lt;/a&gt;. This is a quick fix for stuff that&amp;#8217;s already been uploaded to&amp;nbsp;s3:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt; &lt;span class="s2"&gt;"*.svg"&lt;/span&gt; &lt;span class="nt"&gt;--content-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"image/svg+xml"&lt;/span&gt; &lt;span class="nt"&gt;--metadata-directive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"REPLACE"&lt;/span&gt; &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;--acl&lt;/span&gt; public-read ./output/ s3://&amp;lt;bucketname&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This &lt;em&gt;probably&lt;/em&gt; wipes out some of the other metadata on the files, but sets &lt;code&gt;content-type&lt;/code&gt; &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; &lt;code&gt;acl&lt;/code&gt; correctly for uploading &lt;span class="caps"&gt;SVG&lt;/span&gt; files to a website hosted on&amp;nbsp;s3.&lt;/p&gt;
&lt;aside class="admonition-block note"&gt;&lt;h6 class="block-title label-only"&gt;&lt;span class="title-label"&gt;Note: &lt;/span&gt;&lt;/h6&gt;&lt;p&gt;I found out later that this was affecting more files &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; filetypes than I thought, so the simplest fix was to delete everything in the bucket and re-upload it, after &lt;a href="https://duncanlock.net/blog/2021/06/21/better-content-type-guessing-in-aws-cli/"&gt;fixing the &lt;span class="caps"&gt;AWS&lt;/span&gt; CLIs content type guessing&lt;/a&gt;.&lt;/p&gt;&lt;/aside&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/50856831/copy-to-s3-with-aws-cli-with-proper-content-type"&gt;Copy to S3 with &lt;span class="caps"&gt;AWS&lt;/span&gt; &lt;span class="caps"&gt;CLI&lt;/span&gt; with proper content&amp;nbsp;type
&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://serverfault.com/questions/725562/recursively-changing-the-content-type-for-files-of-a-given-extension-on-amazon-s"&gt;Recursively changing the content-type for files of a given extension on Amazon&amp;nbsp;S3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 22 Jun 2021 00:59:02 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/21/fixing-content-types-in-s3-using-the-aws-cli/</guid></item><item><title>&amp;amp;lsquo;What is a Manifold?&amp;amp;rsquo;, Redux: Some Non-Examples</title><link>https://bastian.rieck.me/blog/2021/manifold_redux/</link><description>&lt;p&gt;&lt;em&gt;Manifolds&lt;/em&gt; are still the &amp;lsquo;bread and butter&amp;rsquo; of modern machine learning
research. I discussed them in &lt;a href="https://bastian.rieck.me/blog/2019/manifold/"&gt;an earlier post&lt;/a&gt;,
aptly titled &amp;lsquo;What is a manifold?&amp;rsquo;. Here, I want to describe some
&lt;em&gt;non-examples&lt;/em&gt;, i.e. spaces that are &lt;em&gt;not&lt;/em&gt; manifolds. The main
motivation for this post is to dispel some false notions about
manifolds&amp;mdash;the term has been so common in certain circles that it is
almost synonymous with &amp;lsquo;data.&amp;rsquo; However, as I tried to explain in my
previous post, a manifold is a space that is &lt;em&gt;exceptionally
well-behaved&lt;/em&gt; and, most importantly, &lt;em&gt;homogeneous&lt;/em&gt;.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; To reiterate my favourite
definition:&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; a $d$-dimensional manifold $\mathcal{M}$ is a space that
locally looks like a $d$-dimensional Euclidean space, i.e. like some
$\mathbb{R}^d$. The important thing is that $d$ must not vary;
every point in $\mathcal{M}$ needs to satisfy this definition for the
&lt;em&gt;same&lt;/em&gt; value of $d$.&lt;/p&gt;
&lt;h1 id="the-ubiquitous-manifold-hypothesis"&gt;The ubiquitous manifold hypothesis&lt;/h1&gt;
&lt;p&gt;The &amp;lsquo;manifold hypothesis&amp;rsquo; refers to the assumption that a given data set
$\mathbf{X}$ is actually a discrete sample of some manifold $\mathcal{M}$.
Often, the hypothesis is extended by stating that $\mathcal{M}$ must have
a significantly lower dimension than the data $\mathbf{X}$. If $\mathbf{X}$
is sampled from some $\mathbb{R}^D$, we would hope that $\mathcal{M}$ is
$d$-dimensional, with $d \ll D$.&lt;/p&gt;
&lt;p&gt;This hope is not in vain! For instance, consider $n = 1000$ samples from
a unit circle. While we only need two dimensions to describe them, we could
artificially extend our samples with $998$ zeroes, resulting in
a set of $n = 1000$ samples from a $1000$-dimensional space.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="A circle" src="https://bastian.rieck.me/images/manifolds_circle.svg" /&gt;&lt;figcaption&gt;
			&lt;p&gt;A circle. With a sufficient number of samples, it appears to be &lt;em&gt;one&lt;/em&gt; object. If we were to live on this space like the depicted bug, we would think that it is $1$-dimensional&amp;mdash;we may only move forward or backward.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;Yet, the underlying model of this space only requires a &lt;em&gt;single&lt;/em&gt;
parameter, viz. the &lt;em&gt;angle&lt;/em&gt;. The other dimensions are superfluous in the
sense that they are not related to the parameter(s) creating the
underlying shape of the space. This, in a nutshell, is an example
illustrating the manifold hypothesis. It explains why we use
dimensionality reduction methods such as &lt;a href="https://en.wikipedia.org/wiki/Autoencoder"&gt;autoencoders&lt;/a&gt;, &lt;a href="https://github.com/KrishnaswamyLab/PHATE"&gt;PHATE&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Principal_component_analysis"&gt;principal
component analysis&lt;/a&gt;, &lt;a href="https://lvdmaaten.github.io/tsne/"&gt;t-SNE&lt;/a&gt;, and &lt;a href="https://umap-learn.readthedocs.io/en/latest/"&gt;UMAP&lt;/a&gt; to create low-dimensional
representations of our data sets.&lt;/p&gt;
&lt;p&gt;Now that we are on the same page again when it comes to the manifold
hypothesis, let&amp;rsquo;s consider a few spaces that are &lt;em&gt;not&lt;/em&gt; manifolds.&lt;/p&gt;
&lt;h1 id="manifold-soup"&gt;Manifold soup&lt;/h1&gt;
&lt;p&gt;The aforementioned restriction to the dimension $d$ already rules out
certain spaces. Since $d$ must remain fixed, spaces that I would like to
be known as &amp;lsquo;manifold soup&amp;rsquo; are not manifolds.
For instance, taking samples from spheres of different
dimensions does &lt;em&gt;not&lt;/em&gt; result in a data set that satisfies the manifold
hypothesis. At best, one could say that such a data set can be
partitioned into different manifolds, provided that the spheres are not
otherwise entangled or intersecting.&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="A 1-sphere and a 2-sphere" src="https://bastian.rieck.me/images/manifold_soup.svg" /&gt;&lt;figcaption&gt;
			&lt;p&gt;A tasty manifold soup, containing a 1-sphere, i.e. a circle, and a 2-sphere.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;This example might be considered a little bit pedantic, but I think it
neatly highlights that the manifold hypothesis might need
a slight reformulation here. Notice that if we were to take samples
from this soup, we might inadvertently conclude that the space has
dimension $d = 2$ because&amp;mdash;unless the circle is more densely sampled
than the $2$-sphere&amp;mdash;we have a higher probability of encountering samples
from the $2$-sphere than from the circle.
This, in turn, could lead to problems in analyses; we might not even be able to
detect that there &lt;em&gt;are&lt;/em&gt; different dimensions in the data set&amp;hellip;&lt;/p&gt;
&lt;h1 id="manifold-wedges"&gt;Manifold wedges&lt;/h1&gt;
&lt;p&gt;We can also turn two manifolds into something that is &lt;em&gt;not&lt;/em&gt; a manifold
by calculating its &lt;a href="https://en.wikipedia.org/wiki/Wedge_sum"&gt;wedge sum&lt;/a&gt;.
Staying within the realm of culinary terminology, such &amp;lsquo;manifold wedges&amp;rsquo;
are obtained by taking two manifolds $\mathcal{M}$ and $\mathcal{N}$ and
&lt;em&gt;identifying&lt;/em&gt; their basepoints. This has the effect of &amp;lsquo;gluing together&amp;rsquo;
$\mathcal{M}$ and $\mathcal{N}$ in exactly one point.&lt;/p&gt;
&lt;p&gt;Calculating the wedge sum of two copies of $S^2$, i.e. the &amp;lsquo;standard&amp;rsquo;
sphere, we obtain $S^2 \vee S^2$, which, with colours based on the $z$
coordinate, looks surprisingly like meatballs:&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="The wedge sum of two 2-spheres" src="https://bastian.rieck.me/images/S2vS2.svg" width="300" /&gt;&lt;figcaption&gt;
			&lt;p&gt;The wedge sum of two $2$-spheres.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;Again, this space is &lt;em&gt;not&lt;/em&gt; a manifold&amp;mdash;the point at which we identified
the two spheres does not locally look like a $2$-dimensional Euclidean
space. In fact, the connection point is a &lt;em&gt;singularity&lt;/em&gt; in the sense
that it is different than all the other points.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s briefly process this: just by virtue of changing a &lt;em&gt;single&lt;/em&gt; point,
we were able to turn two nice manifolds into &amp;lsquo;something else.&amp;rsquo; Now, the
implications of this change are more nuanced than in the manifold soup
example above. There are two common scenarios that I encountered when it
comes to the behaviour of manifold learning algorithms here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The space is treated as a single large sphere.&lt;/li&gt;
&lt;li&gt;The space is treated as two (disconnected) spheres.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the first case, the singularity is given &lt;em&gt;way too much&lt;/em&gt; importance,
whereas in the second case, it is completely ignored. Both cases fail to
properly describe this data set; in my experience, there are few
algorithms capable of handling such spaces.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;To offer a more conciliatory perspective: existing algorithms might not
be able to fully handle such spaces, but at the same time, they are at
least not &amp;lsquo;crashing.&amp;rsquo; The existence of some (isolated!)
singularities is therefore not an issue for these algorithms.
Moreover, in the presence of noisy samples, it is sometimes hard to
decide whether such singularities are actual features of a data set or
just a by-product of noise.&lt;/p&gt;
&lt;h1 id="just-a-pinch"&gt;Just a pinch&amp;hellip;&lt;/h1&gt;
&lt;p&gt;Last, I want to show you what happens if you &lt;em&gt;pinch&lt;/em&gt; a manifold. This is
best illustrated by the most famous of all pinched manifolds, viz. the
&lt;em&gt;pinched torus&lt;/em&gt;&amp;mdash;also known as a croissant surface or, as I like to
call it, a &lt;em&gt;gipfeli&lt;/em&gt;.&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;figure&gt;&lt;img alt="A pinched torus" src="https://bastian.rieck.me/images/pinched_torus.svg" width="300" /&gt;&lt;figcaption&gt;
			&lt;p&gt;A pinched torus (a &lt;em&gt;gipfeli&lt;/em&gt;), colour-coded by its mean curvature.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;
&lt;p&gt;You can see that this looks indeed like a &lt;em&gt;gipfeli&lt;/em&gt;. To show the effects
of the pinching somewhat better, I colour-coded the object by its mean curvature (I am discussing curvature
in &lt;a href="https://bastian.rieck.me/blog/2019/curvature/"&gt;another post&lt;/a&gt;, in case you
are interested). This object is also &lt;em&gt;not&lt;/em&gt; a manifold because of the
pinch point, which&amp;mdash;you guessed it&amp;mdash;constitutes a singularity.&lt;/p&gt;
&lt;p&gt;When I first learned about this procedure, I was intrigued: here is
another very simple operation that destroys the &amp;lsquo;manifoldness&amp;rsquo; of a space.
Again, most algorithms will ignore the singularity and treat this object
as a thickened circle, thus losing some structural information in the
process.&lt;/p&gt;
&lt;h1 id="whats-next"&gt;What&amp;rsquo;s next?&lt;/h1&gt;
&lt;p&gt;These examples demonstrate how quickly one can escape the confines of
the manifold hypothesis. Simple transformations give rise to spaces that
are &lt;em&gt;almost, but not quite&lt;/em&gt; manifolds. Most algorithms are not
sufficiently sensitive to detect these differences, so we need a new
class of methods to tackle such data sets.&lt;/p&gt;
&lt;p&gt;All of these examples have one thing in common, by the way: the data set
might not be a manifold &lt;em&gt;in total&lt;/em&gt;, but it &lt;em&gt;is&lt;/em&gt; a manifold if considered
in parts! For instance, removing the &amp;lsquo;pinch point&amp;rsquo; of the pinched torus
results in a manifold data set.&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt; The same holds for the wedge
sum&amp;mdash;removing the &amp;lsquo;glue point&amp;rsquo; also turns the data set into a manifold
again. Interestingly, &lt;em&gt;both&lt;/em&gt; the singularity and the &amp;lsquo;space sans
singularity&amp;rsquo; are manifolds (albeit of different dimensions, as
in the manifold soup example).&lt;/p&gt;
&lt;p&gt;Now, mathematics would not be mathematics if someone had not
considered what to do in this situation! In fact, there is a nice
underlying theory of studying spaces with such singularities: instead of
studying the whole space at once&amp;mdash;thereby causing problems because the
manifold hypothesis is violated&amp;mdash;it is suggested that the space be
partitioned into &lt;em&gt;strata&lt;/em&gt; that need to fit together.&lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt; Each stratum has to
be a manifold, but the dimension is allowed to vary between
strata (in a controlled fashion, not willy-nilly). This leads to
the definition of a &lt;a href="https://en.wikipedia.org/wiki/Topologically_stratified_space"&gt;topologically stratified space&lt;/a&gt;,
which is beyond the scope of this article.&lt;/p&gt;
&lt;p&gt;If you are interested in a more practical discussion of this issues, you
might enjoy the paper &lt;a href="https://arxiv.org/abs/1907.13485"&gt;Persistent Intersection Homology for the Analysis of Discrete
Data&lt;/a&gt;. Here, my co-authors
and I describe how to &amp;lsquo;sort of&amp;rsquo; detect singularities in data, thus
turning a data set into a stratified space, and how to analyse its
properties. We show that this increases the expressive power of methods,
leading to a more holistic understanding.&lt;/p&gt;
&lt;p&gt;The manifold hypothesis thus remains highly relevant&amp;mdash;but if you
suspect that your data set suffers from singularities, care should be
taken when it comes to interpreting the results.&lt;/p&gt;
&lt;p&gt;Hoping your manifolds remain tasty and fresh, until next time!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Acknowledgements:&lt;/strong&gt; This post was inspired by discussions with members
of &lt;a href="https://sumry.yale.edu"&gt;SUMRY 2021&lt;/a&gt;, the summer programme of Yale
university&amp;rsquo;s maths department. I have the honour of serving as
a mentor this year.&lt;/p&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I use the term &lt;em&gt;homogeneous&lt;/em&gt; here in order to denote that the
space has the same dimension everywhere. In other words, it &amp;lsquo;behaves&amp;rsquo;
the same at all points. Thanks to &lt;a href="https://twitter.com/ZenoRogue"&gt;ZenoRogue&lt;/a&gt; and &lt;a href="https://twitter.com/BEBischof"&gt;Dr. Donut (aka Bryan
Bischof)&lt;/a&gt; for pointing this out.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;It is not the most precise definition, but it does not require
a lot of additional jargon.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Topology already has a lot of agricultural metaphors. Why not
introduce a few culinary ones, as well?&amp;#160;&lt;a class="footnote-backref" href="#fnref:3"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;We will learn about some solutions later on.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;This is the Swiss-German word for croissant, and it rolls off the
tongue so much better, I think.&amp;#160;&lt;a class="footnote-backref" href="#fnref:5"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;The reader may convince themselves by a sketch&amp;mdash;the nicest way of
proving things in topology.&amp;#160;&lt;a class="footnote-backref" href="#fnref:6"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;A formal definition of this requires some additional technical
details that are best covered in another article.&amp;#160;&lt;a class="footnote-backref" href="#fnref:7"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Mon, 21 Jun 2021 23:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/manifold_redux/</guid></item><item><title>Nine Art Installations</title><link>https://mbutler.org/nine-art-installations/</link><description>Gallery visitors feel simulated weather phenomenon generated from data collected by scientific instruments in a local nature preserve Puppeteers control real mollusk “puppets” through office equipment. Simple stage directions are translated into electrical impulses Visitors can harvest virtual organs of a simulated cadaver by scanning barcodes. Virtual organs can then be reassembled to form new [&amp;#8230;]</description><author>mbutler</author><pubDate>Mon, 21 Jun 2021 16:58:41 GMT</pubDate><guid isPermaLink="true">https://mbutler.org/nine-art-installations/</guid></item><item><title>TIL how to ship an executable with Poetry</title><link>https://www.zufallsheld.de/2021/06/21/til-how-to-ship-an-executable-with-poetry/</link><description>&lt;p&gt;I wanted to build an executable python script on PyPi, so it can be run&amp;nbsp;like &lt;code&gt;MyPackage&lt;/code&gt; instead&amp;nbsp;of &lt;code&gt;python MyPackage.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finding out how to do this with Poetry was hard. Googling for &amp;#8220;Python [distribute\|publish] executable&amp;#8221; didn&amp;#8217;t yield any useful results. I don&amp;#8217;t even know how …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Mon, 21 Jun 2021 16:28:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/06/21/til-how-to-ship-an-executable-with-poetry/</guid></item><item><title>Review: Do More Better</title><link>http://evantravers.com/articles/2021/06/21/review-do-more-better/</link><description>&lt;blockquote&gt;
&lt;p&gt;Productivity is effectively stewarding my gifts, talents, time, energy, and enthusiasm for the good of others and the glory of God.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This concise book starts with a clear theology of work, and then (borrowing heavily from other books on productivity) boils the topic down to some essential practices. This is a good thing: there is no need to reinvent the wheel, just to re-examine through a biblical worldview.&lt;/p&gt;
&lt;p&gt;I like that Challies starts by discussing through the lens of &lt;strong&gt;stewardship&lt;/strong&gt;. Challies argues that is not enough for the Christian to do the right things, &lt;em&gt;we must do them for the right reason&lt;/em&gt;… the glory of God.&lt;/p&gt;
&lt;p&gt;He resets our worldview of productivity in the light of Genesis 3: we are hindered by laziness and busyness within and thorns and thistles without.&lt;/p&gt;
&lt;p&gt;He then guides the reader through a stripped-back GTD setup, focusing on crafted mission statements for each of our varied roles.&lt;/p&gt;
&lt;p&gt;Challies does present a wonderful perspective on execution: that we should live &lt;em&gt;coram deo&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;to live &lt;em&gt;coram deo&lt;/em&gt; is to live once’s entire life in the presence of God, under the authority of God, to the glory of God.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This idea is &lt;em&gt;like&lt;/em&gt; the &amp;quot;Greatness in the moment&amp;quot; concept from &lt;a href="http://evantravers.com/articles/2021/05/29/review-12-week-year/"&gt;12 Week Year&lt;/a&gt;, but with a biblical bent.&lt;/p&gt;
&lt;p&gt;He reset my ideas on interruptions with this great quote from C.S. Lewis:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the great thing, if one can, is to stop regarding all the unpleasant things as interruptions of one’s “own,” or “real” life. The truth is of course that what one calls the interruptions are precisely one’s real life – the life God is sending day by day; what one calls one’s “real life” is a phantom of one’s own imagination.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To measure how you are doing in review use the “serve and surprise” paradigm. Serve, and you’ll succeed. Surprise and you’ll excel. Ask yourself this question for each area.&lt;/p&gt;
&lt;p&gt;Do More Better didn't contribute much to my tactical knowledge of productivity, but it did redeem and recalibrate my strategy. My action items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add prayer to daily plan and review&lt;/li&gt;
&lt;li&gt;Contemplate productivity &amp;quot;coram deo&amp;quot;.&lt;/li&gt;
&lt;li&gt;Add “How can I serve and surprise?” to my questions for weekly planning for each area.&lt;/li&gt;
&lt;li&gt;Write mission statements for each area of life.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I believe this would be a good book for someone wanting to get to the core of &amp;quot;how can I get organized?&amp;quot; or a producivity-minded believer who wants to recenter on the reason we work in  the first place.&lt;/p&gt;</description><author>trv.rs</author><pubDate>Mon, 21 Jun 2021 16:26:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/06/21/review-do-more-better/</guid></item><item><title>BERT vs GPT-2 Performance</title><link>https://blog.lukesalamone.com/posts/bert-vs-gpt2/</link><description>&lt;p&gt;There are quite a few BERT vs GPT-2 breakdowns online, mostly focusing on the architectural differences between the two models. However, I am more interested in the performance differences between the two models, specifically their predictive capabilities. This blog post outlines the results of my experiments.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/lukesalamone/gpt2-vs-bert"&gt;The code used in this experiment can be found on my Github&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="bert"&gt;BERT&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://arxiv.org/pdf/1810.04805.pdf"&gt;Devlin et al. model&lt;/a&gt; was released in November 2018. It is a transformer-based language model pretrained on masked input (also known as the &lt;em&gt;cloze&lt;/em&gt; task). During pretraining, 15% of tokens are hidden from the model, and it is trained to predict the masked tokens. As a result, I was able to evaluate its ability to correctly predict a masked token at a random position in a fixed-size input.&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Mon, 21 Jun 2021 09:04:42 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/bert-vs-gpt2/</guid></item><item><title>Joining hypergrowth startups 😬</title><link>https://klinger.io/posts/joining-hypergrowth-startups-%F0%9F%98%AC</link><description>I am writing this for a friend joining an early-stage breakout startup (year 2; $200M+ valuation). I wrote it as bullet points as...</description><author>Blog posts of Andreas Klinger</author><pubDate>Mon, 21 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://klinger.io/posts/joining-hypergrowth-startups-%F0%9F%98%AC</guid></item><item><title>Now</title><link>https://www.jskherman.com/now/</link><description>What&amp;rsquo;s the current status of jskherman.</description><author>jskherman</author><pubDate>Mon, 21 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jskherman.com/now/</guid></item><item><title>The Past</title><link>https://www.jskherman.com/past/</link><description>Archived entries from the Now page</description><author>jskherman</author><pubDate>Mon, 21 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.jskherman.com/past/</guid></item><item><title>Exposing dockerfile content to an EFS volume in Fargate 1.4.0</title><link>https://yehudacohen.substack.com/p/exposing-dockerfile-contents-to-an</link><description>Semantic docker volume and Fargate EFS mount differences...</description><author>Fun With The Cloud</author><pubDate>Sun, 20 Jun 2021 10:01:36 GMT</pubDate><guid isPermaLink="true">https://yehudacohen.substack.com/p/exposing-dockerfile-contents-to-an</guid></item><item><title>Lost River (a bit further)</title><link>https://sam.hooke.me/trip/2021/06/lost-river-a-bit-further/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Sun, 20 Jun 2021 05:45:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/06/lost-river-a-bit-further/</guid></item><item><title>Previewing SwiftUI layouts in Emacs (revisited)</title><link>https://xenodium.com/previewing-swiftui-layouts-in-emacs-revisited</link><description>&lt;p&gt;Back in May 2020, I shared a snippet to extend &lt;a href="https://github.com/zweifisch/ob-swift"&gt;ob-swift&lt;/a&gt; to &lt;a href="https://xenodium.com/swiftui-layout-previews-using-emacs-org-blocks/"&gt;preview SwiftUI layouts using Emacs org blocks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/swiftui-layout-previews-using-emacs-org-blocks/ob-swiftui.gif" /&gt;&lt;/p&gt;
&lt;p&gt;When I say extend, I didn't quite modify ob-swift itself, but rather &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html"&gt;advised&lt;/a&gt; &lt;a href="https://github.com/zweifisch/ob-swift/blob/ed478ddbbe41ce5373efde06b4dd0c3663c9055f/ob-swift.el#L37"&gt;org-babel-execute:swift&lt;/a&gt; to modify its behavior at runtime.&lt;/p&gt;
&lt;p&gt;Fast-forward to June 2021 and Scott Nicholes &lt;a href="https://github.com/zweifisch/ob-swift/issues/4#issuecomment-858196354"&gt;reminded me there's still interest&lt;/a&gt; in org babel SwiftUI support. ob-swift &lt;a href="https://github.com/zweifisch/ob-swift/commits/master"&gt;seems a little inactive&lt;/a&gt;, but no worries there. The package offers great general-purpose Swift support. On the other hand, SwiftUI previews can likely live as a single-purpose package all on its own… and so I set off to bundle the rendering functionality into a new &lt;a href="https://github.com/xenodium/ob-swiftui"&gt;ob-swiftui&lt;/a&gt; package.&lt;/p&gt;
&lt;p&gt;Luckily, org babel's documentation has a straightforward section to help you &lt;a href="https://orgmode.org/worg/org-contrib/babel/languages/index.html"&gt;develop support for new babel languages&lt;/a&gt;. They simplified things by offering &lt;a href="https://code.orgmode.org/bzg/worg/raw/master/org-contrib/babel/ob-template.el"&gt;template.el&lt;/a&gt;, which serves as the foundation for your language implementation. For the most part, it's a matter of searching, replacing strings, and removing the bits you don't need.&lt;/p&gt;
&lt;p&gt;The elisp core of ob-swiftui is fairly simple. It expands the org block body, inserts the expanded body into a temporary buffer, and finally feeds the code to the Swift toolchain for execution.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(defun org-babel-execute:swiftui (body params)
  &amp;quot;Execute a block of SwiftUI code in BODY with org-babel header PARAMS.
This function is called by `org-babel-execute-src-block'&amp;quot;
  (message &amp;quot;executing SwiftUI source code block&amp;quot;)
  (with-temp-buffer
    (insert (ob-swiftui--expand-body body params))
    (shell-command-on-region
     (point-min)
     (point-max)
     &amp;quot;swift -&amp;quot; nil 't)
    (buffer-string)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expansion in &lt;em&gt;ob-swiftui–expand-body&lt;/em&gt; is a little more interesting. It decorates the block's body, so it can become a fully functional and stand-alone SwiftUI macOS app. If you're familiar with Swift and SwiftUI, the code should be fairly self-explanatory.&lt;/p&gt;
&lt;p&gt;From an org babel's perspective, the expanded code is executed whenever we press &lt;em&gt;C-c C-c&lt;/em&gt; (or M-x &lt;a href="https://orgmode.org/manual/The-Very-Busy-C_002dc-C_002dc-Key.html#The-Very-Busy-C_002dc-C_002dc-Key"&gt;org-ctrl-c-ctrl-c&lt;/a&gt;) within the block itself.&lt;/p&gt;
&lt;p&gt;It's worthing mentioning that our new implementation supports two babel &lt;a href="https://www.orgmode.org/worg/org-contrib/babel/header-args.html"&gt;header arguments&lt;/a&gt; (results and view). Both extracted from params using &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/map.el#L106"&gt;map-elt&lt;/a&gt; and replaced in the expanded Swift code to enable/disable snapshotting or explicitly setting a SwiftUI root view.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(defun ob-swiftui--expand-body (body params)
  &amp;quot;Expand BODY according to PARAMS and PROCESSED-PARAMS, return the expanded body.&amp;quot;
  (let ((write-to-file (member &amp;quot;file&amp;quot; (map-elt params :result-params)))
        (root-view (when (and (map-elt params :view)
                              (not (string-equal (map-elt params :view) &amp;quot;none&amp;quot;)))
                     (map-elt params :view))))
    (format
     &amp;quot;
// Swift snippet heavily based on Chris Eidhof's code at:
// https://gist.github.com/chriseidhof/26768f0b63fa3cdf8b46821e099df5ff

import Cocoa
import SwiftUI
import Foundation

let screenshotURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString + \&amp;quot;.png\&amp;quot;)
let preview = %s

// Body to run.
%s

extension NSApplication {
  public func run&amp;lt;V: View&amp;gt;(_ view: V) {
    let appDelegate = AppDelegate(view)
    NSApp.setActivationPolicy(.regular)
    mainMenu = customMenu
    delegate = appDelegate
    run()
  }

  public func run&amp;lt;V: View&amp;gt;(@ViewBuilder view: () -&amp;gt; V) {
    let appDelegate = AppDelegate(view())
    NSApp.setActivationPolicy(.regular)
    mainMenu = customMenu
    delegate = appDelegate
    run()
  }
}

extension NSApplication {
  var customMenu: NSMenu {
    let appMenu = NSMenuItem()
    appMenu.submenu = NSMenu()

    let quitItem = NSMenuItem(
      title: \&amp;quot;Quit \(ProcessInfo.processInfo.processName)\&amp;quot;,
      action: #selector(NSApplication.terminate(_:)), keyEquivalent: \&amp;quot;q\&amp;quot;)
    quitItem.keyEquivalentModifierMask = []
    appMenu.submenu?.addItem(quitItem)

    let mainMenu = NSMenu(title: \&amp;quot;Main Menu\&amp;quot;)
    mainMenu.addItem(appMenu)
    return mainMenu
  }
}

class AppDelegate&amp;lt;V: View&amp;gt;: NSObject, NSApplicationDelegate, NSWindowDelegate {
  var window = NSWindow(
    contentRect: NSRect(x: 0, y: 0, width: 414 * 0.2, height: 896 * 0.2),
    styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
    backing: .buffered, defer: false)

  var contentView: V

  init(_ contentView: V) {
    self.contentView = contentView
  }

  func applicationDidFinishLaunching(_ notification: Notification) {
    window.delegate = self
    window.center()
    window.contentView = NSHostingView(rootView: contentView)
    window.makeKeyAndOrderFront(nil)

    if preview {
      screenshot(view: window.contentView!, saveTo: screenshotURL)
      // Write path (without newline) so org babel can parse it.
      print(screenshotURL.path, terminator: \&amp;quot;\&amp;quot;)
      NSApplication.shared.terminate(self)
      return
    }

    window.title = \&amp;quot;press q to exit\&amp;quot;
    window.setFrameAutosaveName(\&amp;quot;Main Window\&amp;quot;)
    NSApp.activate(ignoringOtherApps: true)
  }
}

func screenshot(view: NSView, saveTo fileURL: URL) {
  let rep = view.bitmapImageRepForCachingDisplay(in: view.bounds)!
  view.cacheDisplay(in: view.bounds, to: rep)
  let pngData = rep.representation(using: .png, properties: [:])
  try! pngData?.write(to: fileURL)
}

// Additional view definitions.
%s
&amp;quot;
     (if write-to-file
         &amp;quot;true&amp;quot;
       &amp;quot;false&amp;quot;)
     (if root-view
         (format &amp;quot;NSApplication.shared.run(%s())&amp;quot; root-view)
       (format &amp;quot;NSApplication.shared.run {%s}&amp;quot; body))
     (if root-view
         body
       &amp;quot;&amp;quot;))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For rendering inline SwiftUI previews in Emacs, we rely on NSView's &lt;a href="https://developer.apple.com/documentation/appkit/nsview/1483440-bitmapimagerepforcachingdisplay"&gt;bitmapImageRepForCachingDisplay&lt;/a&gt; to capture an image snapshot. We write its output to a temporary file and piggyback-ride off org babel's &lt;em&gt;:results file&lt;/em&gt; header argument to automatically render the image inline.&lt;/p&gt;
&lt;p&gt;Here's ob-swiftui inline rendering in action:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/previewing-swiftui-layouts-in-emacs-revisited/obswiftui50.gif" /&gt;&lt;/p&gt;
&lt;p&gt;When rendering SwiftUI externally, we're effectively running and interacting with the generated macOS app itself.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/previewing-swiftui-layouts-in-emacs-revisited/ob-swiftui-window.gif" /&gt;&lt;/p&gt;
&lt;p&gt;The two snippets give a general sense of what's needed to enable org babel to handle SwiftUI source blocks. Having said that, the full source and setup instructions are both available on &lt;a href="https://github.com/xenodium/ob-swiftui"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/xenodium/ob-swiftui"&gt;ob-swiftui&lt;/a&gt; is now available on &lt;a href="https://melpa.org/#/ob-swiftui"&gt;melpa&lt;/a&gt;.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 20 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/previewing-swiftui-layouts-in-emacs-revisited</guid></item><item><title>Markha Valley - The Classic Route</title><link>/2021/06/20/markha-valley-the-classic-route.html</link><description>In October 2019, I did a solo trek through the Markha valley taking the classic route that goes from Stok to Hemis and crosses three high passes - Stok La, Ganda La and Kongmaru La. The trek can be done independently in the summers as most of the villages have basic homestays or campsites with food available on demand. I spent the first night in my own tent, the next 4 nights in homestays and the last one in a hired tent. All campsites along the route were closed at this time of year except for the one in Nimaling. However, most of the homestays were open. Before starting, I spent a couple of days walking around Leh town for acclimatization and one night in a homestay in Stok village sipping copious amounts of namkeen chai (salted butter tea). Homestay in Stok Stok to Yurutse After saying goodbye to my hosts in Stok, I hoisted my backpack and started off straight into the Stok gorge on a bright sunny morning. The trail from Stok is well marked and lavender and wild roses adorn its surroundings. I continued along the Stok stream to reach the campsite of Chang Ma (4100m) where the the trail divides into two. The wider trail crosses immediately across a small stream and climbs steeply towards the Stok Kangri base camp. However, the trail to Stok La goes further along the smaller stream and crosses it about a 100 metres upstream. I confirmed the route with some porters and made my way along a dry stream that leads to the base of the pass. After 2 hours of steady uphill, I reached the campsite of Doksar (4450m), a high altitude grazing area used by locals to tend to their herds. The campsite was desolate with grass and small shrubs being the only sign of life around. A few stone shelters built by shepherds were empty as well and there was a flat stretch just above them to pitch my tent. The air was really thin at this altitude and mundane tasks like getting things in and out of the tent got my heart rate high. What made matters worse was the fact that the water source was about a 100 metres downhill from the campsite. It started getting cold by afternoon and after having an early dinner of instant noodles, I got into my sleeping bag by 6:00 PM. The high altitude made the whole night a struggle for sleep. I couldn’t get good sleep that night but was otherwise comfortable in my tent. Looking back from Doksar campsite Frost flowers in the morning I got up early next morning and had some tea and a couple of granola bars before starting off for Stok La (4850m). The path goes straight up the mountain and joins the wider trail coming from the Stok Kangri base camp before heading south west through long, moderately steep switchbacks. Stok La is a double pass and the main pass is visible only after reaching the first one. There is a staggering change of scenery just close to the pass. From magenta and smooth on one side, the mountains change to light brown and spiny on the other. One can see the eastern face of Stok Kangri as well as the Ladakh range from the pass. To the south of the pass lies the village of Rumbak, cradled on all sides by smooth, barren mountains of various colours. Stok La. Notice how the mountain suddenly changes color and texture The Ladakh range from Stok La View to the south of the Stok La The path going down the pass is steep and mostly covered with scree. It gets easier once you cross a stream and take its true right to reach Rumbak. The village has a few houses most of which double up as homestays. I had a hearty meal in one of the homestays that included local bread, omelette and many cups of tea. I then followed it up with a nice afternoon nap which got me rejuvenated for the one hour hike to Yurutse. A traditional Ladakhi kitchen. Just outside of Rumbak, the trail from Stok La meets the dirt road coming from Jingchen. It then crosses the Rumbak stream as well as the bigger Jingchen nallah to move towards Yurutse. There is a single, large homestay in Yurutse with basic rooms but the hospitality and food offered by the hosts is second to none. After a sleepless night at Doksar and a long day of walking, I was happy to settle down and grab some more namkeen tea. The lone house in Yurutse Stok Kangri from Ganda La Yurutse to Skiu From Yurutse the trail ascends gradually to reach a tea tent at the base of the Ganda La (4950m). The ascent to the pass from there is not too hard if you are well acclimatised and the pass is reached in about 2 hours of steady walking. You get great views of the Zanskar range and the Stok Kangri group from the top with the Ladakh range visible in the distant north. The descent from the pass is fairly gradual and in about 2 hours you reach the beautiful hamlet of Shingo which in early October was shimmering in autumn colours. Close to Ganda La Shingo in autumn colours The path goes through a narrow gorge after Shingo and crosses the stream a few times to finally settle on its true right. On both sides, it is flanked by steep, rocky cliffs of various colours which are home to a variety of high mountain wildlife. I was able to spot a big herd of Himalayan Tahr on one of the cliffs. In about 2 hours you reach Skiu which lies at the junction of the Shingo stream and the Markha river. Skiu is a large, green village with an old monastery and historic ruins that are worth exploring. Reaching Skiu Skiu to Markha The walk from Skiu to Markha is quite long with a few villages along the way. A large part of the trail is now replaced by a dirt road that runs along the river all the way to Markha. I started off from Skiu when I noticed a herd of bharal (Himalayan blue sheep) behind some willow trees at the edge of the road. By the time I took out my phone for a photo, they had quickly moved up the steep, rocky slope. Threshing wheat in the fields of Skiu Bharal herd near Skiu The trail keeps to the true right of the river till it crosses the iconic old wooden bridge over the Markha river. At Sara, which is a small village with a few houses, I had instant noodles for lunch at a tea shop run by an elderly couple. They also treated me to a couple of glasses of chhang, the local liquor made from fermented barley, which refreshed me for the next part of the trail. You cross the river again after Sara and get on to the higher old trail which ends with a tall gateway marked by prayer flags. You get a breathtaking view of snow covered peaks over the wide Markha valley through this gateway while colourful trees and shrubs cover the valley floor. The old wooden bridge on the Markha river The last part of the trail goes along the valley floor to cross the river a couple of times to reach Markha. There is a school on the outskirts the village where the trail divides into two - a really steep path goes towards the monastery and a flatter one goes along a school wall. I took the easier route which turned out to be much longer as it went around the village to enter it at the far end. I finally got to stretch my legs at the end of a tiring day in a small homestay that I found just before dark. A couple of bowls of soup followed by the Ladakhi delicacy chu-taagi, a vegetable broth with fresh home-made pasta, made me feel good before going to bed. A crisp morning in Markha Markha village with the old citadel in the background Markha to Hangkar The next morning was crisp after a thunderstorm struck that night. Markha has some interesting ruins as well as a relatively new monastery that can be visited if time permits. About 2 km upstream of Markha village, there is a stream crossing but the water was low at this time of the year and a log across the stream allowed me to cross without getting wet. The path then enters a shrub filled valley flanked by a sharp, pointed pinnacle on one side. The remarkable scenery looked like a frame from a Sergio Leone western. The pinnacle also marks the turn into a side valley that leads to Zanskar over the Rabrang La. The route known as Jumlam used to be a major trade Route in the past but is hardly in use now. After an hour’s walk from Markha, I reached the Tacha monastery which sits precariously on a cliff top. The path to the monastery is steep but a visit to the top is well worth the effort. The old structure is made almost entirely of stones with nothing to bind them together. A lone monk affiliated to the Hemis monastery is posted in Tacha as caretaker. He was gracious enough to ask me into his quarters where we had tea and talked about how it feels to be alone in such a place. The Pinnacle of Jumlam The Tacha monastery Red Temarisk bushes adorn the valley floor near Umlung I had a light lunch at Umlung and reached Hangkar by late afternoon. The village of Hangkar is spread into a lower and an upper part. Lower Hangkar is situated downstream on the Markha river where a side stream from the north meets it. I had decided to stay at the first homestay available and stopped for the night in the lower part. With some extra time in hand, I went in to explore the side valley only to find a small village named Doltok at the base of a magenta coloured mountain range. Next day turned out to be another long day of walking. The day started with a zigzag climb through a narrow ravine that leads to upper Hangkar. This part of the village turned out to be more interesting with the remains of an old fortress built on top of a steep crag. It is hard to imagine that a place with no electricity and roads used to be important enough to have a fortified town centuries ago. Towards the east, Kang Yatse and Dzo Jongo stand like guardians overlooking the fields of Hangkar. From Hangkar, it is an easy walk along the river till you reach the bridge where the Markha valley splits into two. The path across the bridge goes along the Langthang Chen river towards Zanskar whereas the path to Nimaling diverges just before the bridge and goes along the left bank of the smaller stream named Nimaling Chu. The fortress of Hangkar Upper Hangkar and the fortressed crag Kang Yatse and Dzo Jongo overlook the fields of Hangkar Nimaling and Kongmaru La The walk along the Nimaling Chu stayed easy and I reached the grazing grounds of Tachungste within an hour. The ascent gets steep from there till you reach a high point where a couple of small, serene lakes can be seen with the western face of Kang Yatse in the background. The lakes are probably holy as one of them has a statue of Padmasambhava in the middle with prayer flags along the periphery. A little further up from the lakes, the path descends into the vast meadow of the Nimaling valley which extends for several kilometers along the stream. There are tents available for rent but the nights can get extremely cold here. It was good to have some company as two more groups had reached Nimaling the same evening - two Polish men and two Korean women who were with a local guide. Interestingly, I had met the two Polish guys before at a lesser known beach in Goa. So much for six degrees of separation!! Small lake before Nimaling The vast Nimaling valley After a cold and windy night during which I managed to get some sleep, the next morning turned out to be sunny and welcoming. I had a hearty breakfast and then left along with the other folks on a long, slow march towards Kongmaru La (5250m). The process to reach the pass is simple - walk a few steps, get tired, rest, look back at the mountains, and repeat. The bleak and lifeless surroundings make the walk even more tiring. However, the constantly changing views of Kang Yatse with clouds flirting around its twin peaks did provide the inspiration to soldier on. Finally, after an endless hour and a half, I was at the summit of Kongmaru La - the highest point I had ever reached on foot. The pass itself was cold and windy with great views of variegated mountains to the north. I clicked a few photographs at the summit and continued down the other side where it was sunny and warm. Sunrise Clouds flirting with Kang Yatse View from Kongmaru La The descent from the pass is steep with mountains of different colours and textures all around. After crossing a campsite at the base of the pass, the trail enters a narrow red sandstone gorge with a stream running through it. It is an interesting walk along the banks of the stream and as you come out of the gorge, formations shaped like the Easter Island giants can be seen along the steep valley slopes. The entire stretch from Kongmaru La to Chogdo is like a walk through the innards of the Himalayas - a place where the mountains reveal the true nature of their creation. The narrow gorge Somewhere between Chogdo and Shang Sumdo Hemis monastery The valley starts widening as one reaches the village of Chogdo. The village is now connected by a proper road and you can easily get a taxi to Leh from there. There was still some time left in the day, so I decided to visit to the Hemis monastery which lies half an hour off the route. The monastery is one of the most important in the region and has grand temples dedicated to the Buddha and Padmasambhava. It is home to some jaw dropping murals and also houses one of the best museums of medieval Buddhist artefacts from the Himalayan region. A thunderstorm was approaching and I quickly made my way back to the car. With that my week long journey into the mountains came to an end. I had come to be impressed by the sights of nature but it was the forgotten ruins of the Markha valley that really left me speechless. Info For more information on the route, check out Depi Chaudhry’s Trekking Guide to the Western Himalayas AllTrails GPS log, Covers Spituk to Hemis (Shang). A great research paper on the historic ruins of the Markha valley - An Archaeological Account of the Markha Valley, Ladakh. Trip dates: 1-7 Oct 2019</description><author>Over the Hills</author><pubDate>Sun, 20 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/2021/06/20/markha-valley-the-classic-route.html</guid></item><item><title>Fake circuits. Fake circuits everywhere</title><link>https://stop.zona-m.net/2021/06/fake-circuits.-fake-circuits-everywhere/</link><description>&lt;p&gt;There is only one thing worst than waiting months for a shiny new computer.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 20 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/fake-circuits.-fake-circuits-everywhere/</guid></item><item><title>2021–06–20: Pinephone keyboard's firmware I2C interface</title><link>https://xnux.eu/log/#041</link><author>megi's PinePhone Development Log</author><pubDate>Sun, 20 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#041</guid></item><item><title>Predicting party affiliation of US politicians using fasttext</title><link>https://bytepawn.com/predicting-party-affiliation-of-us-politicians-using-fasttext.html</link><description>&lt;p&gt;I train a fasttext classifier on 1.2M data points to predict US politicians' party affiliations from their twitter messages. &lt;br /&gt;&lt;br /&gt;&lt;img alt="Trump Schiff" src="https://www.rollingstone.com/wp-content/uploads/2019/10/DTAS.jpg" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sun, 20 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/predicting-party-affiliation-of-us-politicians-using-fasttext.html</guid></item><item><title>Memento Vivere</title><link>https://www.swyx.io/memento-vitae</link><description>Remember what life can do.</description><author>swyx's site RSS Feed</author><pubDate>Sun, 20 Jun 2021 00:47:04 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/memento-vitae</guid></item><item><title>Linux kernel flame graphs</title><link>https://dorotac.eu/posts/flame_graph/</link><description>&lt;h1&gt;Linux kernel flame graphs&lt;/h1&gt;
&lt;p&gt;This post is a nothing-burger. All I wanted was to have those words together on the Internet: "Linux kernel flame graph profiling", because I spent about an hour looking for something I already knew would take 2 minutes to execute.&lt;/p&gt;
&lt;p&gt;So, to whoever comes to ask the same question as I did back then, the solution is not &lt;code&gt;perf&lt;/code&gt;, it' also not &lt;code&gt;trace-cmd&lt;/code&gt;, but instead it's eBPF-based. It's &lt;a href="https://github.com/iovisor/bcc/"&gt;bcc&lt;/a&gt;. Read the &lt;a href="https://github.com/iovisor/bcc/blob/master/tools/profile_example.txt"&gt;description of the profile tool&lt;/a&gt;. TL;DR:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./bcc/tools/profile.py -dfK &amp;gt; profile.flame
flamegraph.pl &amp;lt; profile.flame &amp;gt; flame.svg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conveniently, Fedora has both flamegraph and the bcc library (not the tools) in the repos already.&lt;/p&gt;
&lt;p&gt;This is what comes out the other end:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flame graph showing multiple programs including Web, Xorg, plasmashell. There are 2 large spikes named , and two spikes called , with  up on the stack. Those two take up almost 50% width" src="flame.svg" /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, my system is spending &lt;em&gt;a lot&lt;/em&gt; of time in the kernel, messing with btrfs. Then it overheats, causing it to spend &lt;em&gt;even more&lt;/em&gt; time in the kernel, due to idle injection. Then, it gets really sluggish, making the operator look for kernel profiling tools, and the operator finally writes a blog post about this.&lt;/p&gt;
&lt;p&gt;The bug looks like &lt;a href="https://bugzilla.kernel.org/show_bug.cgi?id=212185"&gt;this report&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Nothing-burger&lt;/h2&gt;
&lt;p&gt;If you thought I had something valuable to say, then… Well okay, maybe I shouldn't write blog posts if they are useless to anyone but me. You win. Give me a moment, I'll come up with something insightful to say.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Maybe you're not familiar with flame graphs, but they are a really nice way to check for performance issues. They show the amount of time the resource (in this case the CPU) is busy with each task. The width of the graph is 100% time, and the width of the different bars is the portion of time occupied by something.&lt;/p&gt;
&lt;p&gt;This flame graph does not distinguish between different runs of the same task. If we were monitoring tasks done by Joe over the day, and he'd spend the morning slicing potatoes, then chopped wood, and then sliced potatoes again, this kind of a graph would only distinguish two tasks: slicing potatoes and chopping wood.&lt;/p&gt;
&lt;p&gt;This makes sense when you consider why the bars are stacked vertically: each bar on top of another is a sub-task. If a task is composed of a hundred sub-tasks, presenting them together makes the picture clear. When we care about optimization, then it's useful to know how the total time spent peeling compares to the time spent cutting. The times for each potato aren't so relevant. It doesn't even matter a lot how we order the results on the left-right axis!&lt;/p&gt;
&lt;p&gt;It's especially relevant for computers, where flame graphs may be used to get an idea how much time is spent in a procedure that may be called a thousand times. While the graph won't say how many times it was executed, it will show where it was called from.&lt;/p&gt;
&lt;h2&gt;Bonus&lt;/h2&gt;
&lt;p&gt;This is about the kernel, which is notoriously hard to debug, so let me share my most useful snippets for debugging. Those use trace-cmd, and give extra information without the need to recompile.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prints the names of functions (with some exceptions like inlining) along the execution path:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;sudo trace-cmd record -p function_graph -g start_procedure -F ./program_under_test
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Prints the call stack whenever the chosen procedure is executed:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;sudo trace-cmd record -p function_graph -g procedure_under_test -n printk -n dev_printk_emit -F ./program_under_test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View results with &lt;code&gt;trace-cmd report&lt;/code&gt;.&lt;/p&gt;</description><author>dcz's posts</author><pubDate>Sat, 19 Jun 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://dorotac.eu/posts/flame_graph/</guid></item><item><title>WeWork: Or the Making and Breaking of a $47 Billion Unicorn</title><link>https://olshansky.info/movie/wework_or_the_making_and_breaking_of_a_47_billion_unicorn/</link><description>Olshansky's review of WeWork: Or the Making and Breaking of a $47 Billion Unicorn</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 19 Jun 2021 11:53:12 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/wework_or_the_making_and_breaking_of_a_47_billion_unicorn/</guid></item><item><title>Blurring the lines between shell and editor</title><link>https://xenodium.com/yasnippet-in-emacs-eshell</link><description>&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/yasnippet-in-emacs-eshell/yas-eshell.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I recently &lt;a href="https://twitter.com/xenodium/status/1404746233860837378"&gt;tweeted&lt;/a&gt; that Vivek Haldar's &lt;a href="https://blog.vivekhaldar.com/post/3996068979/the-levels-of-emacs-proficiency"&gt;10-year old post&lt;/a&gt; rings true today just the same. He writes about &lt;a href="https://blog.vivekhaldar.com/post/3996068979/the-levels-of-emacs-proficiency"&gt;the levels of Emacs proficiency&lt;/a&gt;. All 6 levels are insightful in their own right, but for the sake of this post, let's quote an extract from level &lt;em&gt;4. Shell inside Emacs&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;And then, you learned about it: M-x shell.&lt;/p&gt;
&lt;p&gt;It was all just text. Why did you need another application for it? Why should only the shell prompt be editable? Why can’t I move my cursor up a few lines to where the last command spewed out its results? All these problems simply disappear when your shell (or shells) simply becomes another Emacs buffer, upon which all of the text manipulation power of Emacs can be brought to bear.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, we aren't merely removing shell restrictions, but opening up possibilities…&lt;/p&gt;
&lt;p&gt;Take Emacs eshell looping, for example. I use it so infrequently, I could never remember eshell's syntax. I would refer back to EmacsWiki's &lt;a href="https://www.emacswiki.org/emacs/EshellForLoop"&gt;Eshell For Loop&lt;/a&gt; or Mastering Emacs's &lt;a href="https://masteringemacs.org/article/complete-guide-mastering-eshell"&gt;Mastering Eshell&lt;/a&gt; comments for a reminder. It finally dawned on me. I don't need to internalize this eshell syntax. I have &lt;a href="https://github.com/joaotavora/YASnippet"&gt;YASnippet&lt;/a&gt; available like any other buffer. I could just type &amp;quot;for&amp;quot; and let YASnippet do the rest for me.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/yasnippet-in-emacs-eshell/yas-for.gif" /&gt;&lt;/p&gt;
&lt;p&gt;All I need is a tiny YASnippet:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-YASnippet"&gt;#name : Eshell for loop
#key : for
# --
for f in ${1:*} { ${2:echo} &amp;quot;$f&amp;quot;; $3} $0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Want a gentle and succinct YASnippet intro? Check out Jake's &lt;a href="https://www.reddit.com/r/emacs/comments/o282fq/YASnippet_snippetstemplating_introductiontutorial/"&gt;YASnippet introduction video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you're a &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Shell-Mode.html"&gt;shell-mode&lt;/a&gt; user, YASnippet would have you covered in your favorite shell. The expansion snippet can be modified to a Bash equivalent, giving us the same benefit. We type &amp;quot;for&amp;quot; and let YASnippet expand and hop over arguments. Here's a Bash equivalent emphasizing the hopping a little more:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/yasnippet-in-emacs-eshell/yasbash.gif" /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-YASnippet"&gt;#name : bash for loop
#key : for
# --
for f in ${1:*}; do ${2:echo} $f; done $0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ps. Looks like &lt;a href="https://github.com/akermu/emacs-libvterm"&gt;vterm&lt;/a&gt;, &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Term-Mode.html"&gt;term&lt;/a&gt;, or &lt;a href="https://www.emacswiki.org/emacs/AnsiTerm"&gt;ansi-term&lt;/a&gt; work too. See Shane Mulligan's post: &lt;a href="https://mullikine.github.io/posts/use-yasnippets-in-term/"&gt;Use YASnippets in term and vterm in emacs&lt;/a&gt;.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sat, 19 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/yasnippet-in-emacs-eshell</guid></item><item><title>WakatimeClient</title><link>https://hth.is/2021/06/19/wakatimeclient/</link><description>I&amp;rsquo;ve officially released the first version of a library I have been working on for a while now, that carries the very unimaginative title &amp;lsquo;WakatimeClient&amp;rsquo;. For the technical details you can take a look at the library page or go straight to github.
Backstory Link to heading Wakatime is a fantastic coding activity tracker that not only has a fantastic range of plugins for (it seems) almost any IDE on the planet, but it also has a fully functional restful api that can be used for consuming and creating events.</description><author>Hrafn Thorvaldsson</author><pubDate>Sat, 19 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://hth.is/2021/06/19/wakatimeclient/</guid></item><item><title>AWS IAM: Allowing a Role to Assume Another Role</title><link>https://nelson.cloud/aws-iam-allowing-a-role-to-assume-another-role/?ref=rss</link><description>How to allow an IAM Role to assume another Role.</description><author>Nelson Figueroa</author><pubDate>Sat, 19 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nelson.cloud/aws-iam-allowing-a-role-to-assume-another-role/?ref=rss</guid></item><item><title>Migrants test new surveillance schemes for  Europe</title><link>https://stop.zona-m.net/2021/06/migrants-test-new-surveillance-schemes-for-europe/</link><description>&lt;p&gt;And they do it for free! Aren&amp;rsquo;t they great?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 19 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/migrants-test-new-surveillance-schemes-for-europe/</guid></item><item><title>16 things you didn't know about Kube APIs and CRDs</title><link>https://evancordell.com/posts/kube-apis-crds/</link><description>#15 will shock you!
If you're familiar with kubernetes, you're likely familiar with the kubernetes API and the concept of controllers: reconciliation loops that look at the data stored in the api and try to make the state of the cluster match that declared API state.
This is a pretty powerful model that has proven its value over time - but despite the seeming simplicity of the core ideas, there are plenty of details that may be surprising once you scratch the surface.</description><author>Evan Cordell</author><pubDate>Fri, 18 Jun 2021 15:35:45 GMT</pubDate><guid isPermaLink="true">https://evancordell.com/posts/kube-apis-crds/</guid></item><item><title>Meet Hugo, a static page generator</title><link>https://kaszkowiak.org/en/blog/hugo-static-page-generator/</link><description>A convenient workflow for every web developer. Learn more about the Hugo generator, when should you use it and what are its flaws.</description><author>Blog on Maciej Kaszkowiak</author><pubDate>Fri, 18 Jun 2021 15:33:06 GMT</pubDate><guid isPermaLink="true">https://kaszkowiak.org/en/blog/hugo-static-page-generator/</guid></item><item><title>Review: Farewell</title><link>http://evantravers.com/articles/2021/06/18/review-farewell-the-greatest-spy-story-of-the-twentieth-century/</link><description>&lt;p&gt;Farewell is about a soviet double-agent towards the end of the Cold War. He exposed a massive operation of technical theft, contributing to the economic pressure western powers were exerting on the Soviet Union. It is a fascinating portrait: what sort of man betrays his country. It's also interesting as a window behind the iron curtain.&lt;/p&gt;
&lt;p&gt;The authors take their time&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-time" id="fnref-time"&gt;1&lt;/a&gt;&lt;/sup&gt; building the picture of Vetrov as a man… what drove him, his family life, his weaknesses until escalating into double-agent territory and then a dramatic finale.&lt;/p&gt;
&lt;p&gt;After Vetrov's documents made their way to western powers, their sneaky responses were fascinating:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;they abused their knowledge of what targeted by the KGB to &amp;quot;send&amp;quot; components with bugs and viruses to cause damage to soviet technology.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-explosion" id="fnref-explosion"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;the US deliberately misled soviet scientists by pretending to invest in dead end technology.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-misdirection" id="fnref-misdirection"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Honestly, this feels Stephenson-esque or like the Three Body Problem… it's hard to believe that all this was going down around the time I was born. I also don't think that Russia and China didn't pay attention, you can see the same shenanigans repeated these days.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-shenanigans" id="fnref-shenanigans"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Another anecdote that stood out to me: The soviets felt threatened by the US building the space shuttle, so they directed their scientists to build a clone of it called the Buran. They flew it only once… they didn’t know what to do with it!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Buran spacecraft is a revealing illustration of this. Before questioning its utility, simply out of mimicry or out of fear of falling behind in the arms race, the Soviets launched their own space shuttle program, based on designs stolen from Western technology. This shuttle completed only one experimental spaceflight. When asked after the Cold War why they made that spacecraft, a few engineers answered they had no idea why, it was just about copying the Americans. (location: &lt;a href=""&gt;3066&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The success criteria for the Buran seems to be was “build a space shuttle”… they had no greater outcome (like &amp;quot;do science in space.&amp;quot; &lt;a href="http://evantravers.com/articles/2020/07/09/outcomes-over-outputs/"&gt;Outcomes over Outputs&lt;/a&gt;, anyone?&lt;/p&gt;
&lt;p&gt;I enjoyed the book. It was intriguing as a study in history, in human psychology, in learning a little bit about a time and culture that is still relevant today, and a little bit about spy tradecraft. I am still amused that Vetrov got away with it for so long by &lt;em&gt;ignoring&lt;/em&gt; normal tradecraft procedure… because that's what the KGB were looking for. 🤷&lt;/p&gt;
&lt;section class="footnotes"&gt;
&lt;ol&gt;
&lt;li id="fn-time"&gt;
&lt;p&gt;It may be the translation from French to English, or the fact I was reading it slowly before bed. &lt;a class="footnote-backref" href="#fnref-time"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-explosion"&gt;
&lt;blockquote&gt;
&lt;p&gt;Weiss’s plan allowed everybody to agree. He arranged to have software delivered to Line X through a Canadian company. This software was meant to control gas pipeline valves and turbines, and it was delivered with viruses embedded in the code by one of the contractors. The viruses were designed to have a delayed effect; at first the software seemed to work as per the contract specifications. The sudden activation of the viruses in December 1983 led to a huge three-kiloton gas explosion in the Urengoi gas field, precisely in Siberia where, ironically, Vetrov had just started his jail time for a crime of passion. (location: &lt;a href=""&gt;4446&lt;/a&gt;)
More reading&lt;a href="https://www.risidata.com/Database/Detail/cia-trojan-causes-siberian-gas-pipeline-explosion"&gt;CIA Trojan Causes Siberian Gas Pipeline Explosion&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;a class="footnote-backref" href="#fnref-explosion"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-misdirection"&gt;
&lt;blockquote&gt;
&lt;p&gt;Convinced that the Soviets had a serious interest in that particular isotope, secret services of the Western alliance decided to encourage, in a subtle way, the Russians to err further. The American journal Physical Review and the British magazine Nature published a few articles signed by renowned physicists detailing the number and the diversity of osmium-187 energy levels. Then, technical papers on the subject stopped. When this happens in technical and scientific areas, it signals to all intelligence services that this is of strategic importance. Directorate T (the soviet technology espionage group) immediately ordered its operatives to follow that trail, and more specifically in France, where the KGB had attempted to recruit quite a few scientists. At scientific conventions, where intelligence officers spent a lot of time, renowned researchers from all over the world started discussing with credibility osmium properties, describing the metal as a good candidate for use in a possible “graser.” A few labs went as far as publishing posters claiming successful experiments conducted in highly protected research centers. And last, American labs conspicuously bought significant quantities of natural osmium from the Soviet Union, major provider of this metal on world markets. (location: &lt;a href=""&gt;4488&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;a class="footnote-backref" href="#fnref-misdirection"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-shenanigans"&gt;
&lt;p&gt;&lt;a href="https://www.breitbart.com/national-security/2021/02/12/report-china-used-computer-chips-spy-american-pc-systems/"&gt;Report: China Used Computer Chips to Spy on American P.C. Systems&lt;/a&gt; &lt;a class="footnote-backref" href="#fnref-shenanigans"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><author>trv.rs</author><pubDate>Fri, 18 Jun 2021 11:44:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/06/18/review-farewell-the-greatest-spy-story-of-the-twentieth-century/</guid></item><item><title>Robinson Creek</title><link>https://sam.hooke.me/trip/2021/06/robinson-creek/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Fri, 18 Jun 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/06/robinson-creek/</guid></item><item><title>How does GPT-2 Tokenize Text?</title><link>https://blog.lukesalamone.com/posts/gpt2-tokenization/</link><description>&lt;p&gt;Let&amp;rsquo;s explore how GPT-2 tokenizes text.&lt;/p&gt;
&lt;h2 id="what-is-tokenization"&gt;What is tokenization?&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s important to understand that GPT-2 doesn&amp;rsquo;t work with strings directly. Instead, it needs to tokenize the input string, which is essentially a process for converting the string into a list of numbers, or &amp;ldquo;tokens&amp;rdquo;. It is these tokens which are passed into the model during training or for inference. As a concrete example, let&amp;rsquo;s look at a few sample sentences:&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Fri, 18 Jun 2021 03:30:48 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/gpt2-tokenization/</guid></item><item><title>Less e-waste would be good. If it happened in the right way</title><link>https://stop.zona-m.net/2021/06/less-e-waste-would-be-good.-if-it-happened-in-the-right-way/</link><description>&lt;p&gt;Which is exactly what is happening now.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 18 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/less-e-waste-would-be-good.-if-it-happened-in-the-right-way/</guid></item><item><title>The Australian Expedition – Live YouTube Q&amp;amp;A</title><link>https://theroadchoseme.com/the-australian-expedition-live-youtube-qa</link><description>While I&amp;#8217;m stuck in quarantine in Australia I&amp;#8217;ve setup a live Q&amp;#38;A session about my upcoming Australian expedition. Join the chat to ask me anything you&amp;#8217;ve always wondered about my previous expeditions, or this&amp;#46;&amp;#46;&amp;#46;</description><author>The Road Chose Me</author><pubDate>Thu, 17 Jun 2021 12:59:15 GMT</pubDate><guid isPermaLink="true">https://theroadchoseme.com/the-australian-expedition-live-youtube-qa</guid></item><item><title>Pyramid Lake</title><link>https://sam.hooke.me/trip/2021/06/pyramid-lake/</link><description>&lt;figure class="single"&gt;
 &lt;div class="single-inset"&gt;
 










 
 
 
 &lt;a href="https://sam.hooke.me/images/trip/2021/pyramid_lake/w1200/pyramid_lake_01.jpg"&gt;
 
 &lt;img alt="View looking down into Pyramid Lake, which is more of a pond than a lake, sitting inside a small shady crater, with turquoise water, and several dozen logs floating on one side." src="https://sam.hooke.me/images/trip/2021/pyramid_lake/w1200/pyramid_lake_01_hu10998540295599944799.jpg" /&gt;
 
 &lt;/a&gt;
 


 &lt;figcaption style="margin: auto;"&gt;
 
 View looking down into Pyramid Lake.
 
 &lt;/figcaption&gt;
 &lt;/div&gt;
&lt;/figure&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;This scenic hike is named after its destination, Pyramid Lake. While the lake itself is a little on the small side, perhaps edging into &amp;ldquo;pond&amp;rdquo; territory, it is nevertheless beautiful, with some fascinating features. Carniverous sundew plants abound on the floating logs, and pikas hide in the nearby rocks.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Thu, 17 Jun 2021 09:45:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/06/pyramid-lake/</guid></item><item><title>Traderino update #1: Multistrategy testing and improved bot commands</title><link>https://ailef.tech/2021/06/17/traderino-update-1-multistrategy-testing-and-improved-bot-commands/</link><description>It&amp;#8217;s been two weeks since my last blog post and finally a new update for Traderino is ready. As usual, let&amp;#8217;s do a small recap of what happened in the last couple of weeks. In these 9 days of trading we&amp;#8217;ve had some big swings in profits but it seems for now Traderino managed to [&amp;#8230;]</description><author>AILEF</author><pubDate>Thu, 17 Jun 2021 08:14:03 GMT</pubDate><guid isPermaLink="true">https://ailef.tech/2021/06/17/traderino-update-1-multistrategy-testing-and-improved-bot-commands/</guid></item><item><title>Debug woes 2: unexpected array in replacement string</title><link>https://learnbyexample.github.io/mini/debug-woes-2/</link><description>&lt;p&gt;So, I was editing a markdown file in Vim and I wanted to convert some lines to links. The regexp pattern ended up needing non-greedy quantifier, but it didn't work. I thought I got Vim's rather weird &lt;code&gt;\{-}&lt;/code&gt; syntax wrong and switched to using Perl from the command line instead of checking the documentation if I had actually made that mistake.&lt;/p&gt;
&lt;p&gt;Turns out I made other mistakes in the regexp, but I didn't want to switch back to Vim. I was still scratching my head though, since I wasn't getting the expected output. Thankfully, compared to the &lt;a href="https://learnbyexample.github.io/mini/debug-woes-1/"&gt;previous debug misery&lt;/a&gt;, I was able to guess this issue soon enough.&lt;/p&gt;
&lt;p&gt;Here's a simplified issue, how I debugged it and the corrected usage:&lt;/p&gt;
&lt;pre class="language-ruby " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-ruby"&gt;&lt;span style="color: #7f8989;"&gt;# sample input
&lt;/span&gt;&lt;span&gt;$ cat ip.txt 
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;*&lt;/span&gt;&lt;span&gt; blah blah &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;12&lt;/span&gt;&lt;span&gt; xyz &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;34&lt;/span&gt;&lt;span&gt; abcd &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;56&lt;/span&gt;&lt;span&gt; foobaz
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt; blah &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;100&lt;/span&gt;&lt;span&gt; apple &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;200&lt;/span&gt;&lt;span&gt; fig
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# where I got stuck
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# what happened to $1 and $2?
&lt;/span&gt;&lt;span&gt;$ perl &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt;lpe &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'s/^(. )(.*?\d+) (.+)/$1[$2](#$3)/'&lt;/span&gt;&lt;span&gt; ip.txt
&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;#xyz 34 abcd 56 foobaz)
&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;#apple 200 fig)
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# what I did to debug
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# step 1: only $1 in the replacement
&lt;/span&gt;&lt;span&gt;$ perl &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt;lpe &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'s/^(. )(.*?\d+) (.+)/$1/'&lt;/span&gt;&lt;span&gt; ip.txt
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;* 
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;- 
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# step 2: $1 and $2 in the replacement
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# only empty lines as output - bingo!
&lt;/span&gt;&lt;span&gt;$ perl &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt;lpe &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'s/^(. )(.*?\d+) (.+)/$1[$2]/'&lt;/span&gt;&lt;span&gt; ip.txt
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# $1[$2] treated as array syntax in Perl
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# so, need to escape [ since array isn't intended here
&lt;/span&gt;&lt;span&gt;$ perl &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt;lpe &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'s/^(. )(.*?\d+) (.+)/$1\[$2](#$3)/'&lt;/span&gt;&lt;span&gt; ip.txt
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;* &lt;/span&gt;&lt;span&gt;[blah blah &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;12&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;#xyz 34 abcd 56 foobaz)
&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;- &lt;/span&gt;&lt;span&gt;[blah &lt;/span&gt;&lt;span style="color: #b3933a;"&gt;100&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;#apple 200 fig)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><author>learnbyexample</author><pubDate>Thu, 17 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/mini/debug-woes-2/</guid></item><item><title>Languages you can run in the browser, part 1: Python, JavaScript, SQLite</title><link>http://notes.eatonphil.com/languages-you-can-run-in-the-browser.html</link><description>&lt;p&gt;This is an external post of mine. Click
&lt;a href="https://datastation.multiprocess.io/blog/2021-06-16-languages-you-can-run-in-the-browser.html"&gt;here&lt;/a&gt;
if you are not redirected.&lt;/p&gt;</description><author>Notes on software development</author><pubDate>Thu, 17 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/languages-you-can-run-in-the-browser.html</guid></item><item><title>[Computing] How the K-line got its name: history of IRC daemon configuration</title><link>https://www.devever.net/~hl/irclines</link><description>&lt;p&gt;Most people who use IRC know that to K-line someone means to ban them from an
IRC server. Since the origins of this terminology are now rather obscure, I
thought it might be beneficial to explain the history of this terminology and
how it came to be.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Thu, 17 Jun 2021 01:46:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/irclines</guid></item><item><title>Throwing the WRONG kitchen sink at Big Tech</title><link>https://stop.zona-m.net/2021/06/throwing-the-wrong-kitchen-sink-at-big-tech/</link><description>&lt;p&gt;&amp;ldquo;If they make you ask the wrong questions, answers don&amp;rsquo;t matter&amp;rdquo;.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 16 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/throwing-the-wrong-kitchen-sink-at-big-tech/</guid></item><item><title>So Long, and Thanks For All The Pizza</title><link>https://blog.andyglassman.com/2021/06/so-long-and-thanks-for-all-pizza.html</link><description>&lt;p&gt;On a cold Wisconsin day in December 2014, my good friend &lt;a href="https://www.linkedin.com/in/jonathan-kupcho-750a3112/"&gt;Jonathan Kupcho&lt;/a&gt;&amp;nbsp;and I patiently waited next to 2 large Dominos pizzas, and a case of warm Diet Coke.&amp;nbsp; &amp;nbsp;We had 11 RSVPs.&amp;nbsp; Some of them would&amp;nbsp;show up... right?&amp;nbsp; &amp;nbsp;Had we given clear enough directions, and parking instructions?&amp;nbsp; Did people deem it too cold to journey out that night?&amp;nbsp; Our fears subsided as the first attendees started to trickle in.&amp;nbsp; I don't remember the exact number of attendees, but the exact number is not important.&amp;nbsp; People came!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;That night was the first event ever for the &lt;a href="https://www.meetup.com/Greater-Milwaukee-Java-Meetup"&gt;Greater Milwaukee Java Meetup&lt;/a&gt;. As the organizer of this meetup, I planned, emailed, called, coordinated, ordered pizza, ran to the grocery store, and even had time to do a bit of open sourcing!&amp;nbsp; Now, over 50 events later, I am stepping down to let others run the group.&amp;nbsp; Before I fade into obscurity, I want to share the lessons I learned while running this meetup.&amp;nbsp; Hopefully you'll find these lessons useful if you ever want to start your own group, or maybe you'll reserve a bit of empathy for those that organize these types of groups. It's certainly a labor of love!&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 style="text-align: left;"&gt;Lesson 1: One Is Better Than None&lt;/h2&gt;While running the group, I've always kept the mindset that "one is better than none".&amp;nbsp; If one person shows up, and is able to learn or share something, I consider that a win.&amp;nbsp; &amp;nbsp;There were times where we'd have 40 people, and other times where we'd have 4.&amp;nbsp; Both have their pros and cons.&amp;nbsp; The number of attendees is a vanity metric. You should focus on getting to know the people attending your meetup.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While you shouldn't focus on number of attendees as a main indicator of success for your meetup, it is nice to grow your meetup.&amp;nbsp; More attendees does mean more varied backgrounds, levels of experience, and general life experiences.&amp;nbsp; Also, more attendees can be more fun!&amp;nbsp; Another bonus of a larger membership are that you can start to rely on them for content such as lightning talks, or workshops.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Overall, just focus on the quality of the conversations, and content.&amp;nbsp; There is no prize for the biggest meetup!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaC4JYPJCn0lEZ8hFbtbBDbvZleah7gaFD2k9x4O5VDQEHgT7ylOKVIE3s9_C1mSFGuW_yFcpo2yoQQJm1-iO9yo8rg5qsbE97gfjg5XTuqrMFPU-r0C6oUh_fA5xMjwQEIBqEHkpHzA/s1208/Screen+Shot+2021-06-16+at+8.30.47+AM.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaC4JYPJCn0lEZ8hFbtbBDbvZleah7gaFD2k9x4O5VDQEHgT7ylOKVIE3s9_C1mSFGuW_yFcpo2yoQQJm1-iO9yo8rg5qsbE97gfjg5XTuqrMFPU-r0C6oUh_fA5xMjwQEIBqEHkpHzA/s320/Screen+Shot+2021-06-16+at+8.30.47+AM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;A nice crowd at this one!&lt;/div&gt;&lt;h2 style="text-align: left;"&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2 style="text-align: left;"&gt;Lesson 2:&amp;nbsp; Excessively Communicate&lt;/h2&gt;&lt;p style="text-align: left;"&gt;One thing that I focused on from the start was to always be prompt and verbose with communication around events.&amp;nbsp; Sometimes it feels excessive, but remember, you're just one of possibly hundreds of emails, tweets, and calendar invites that your members may encounter throughout the week.&amp;nbsp; Sometimes people need a reminder email, or three!&lt;/p&gt;&lt;p style="text-align: left;"&gt;During an event, take detailed notes on what was discussed.&amp;nbsp; Write down any links, or topics mentioned during the presentation, or Q&amp;amp;A section.&amp;nbsp; Create a detailed summary of the event, include any curated presentations and links the presenter provided, and include your notes.&amp;nbsp; This is one of the top things I always got great feedback on.&amp;nbsp; People love this level of detail. It is also super useful for those that were not able to attend.&amp;nbsp; Send these notes out to the entire group, and also post publicly on the meetup discussion board, or group blog.&amp;nbsp; I also would post links to the recaps on twitter and linked in, tagging and thanking the presenter.&amp;nbsp; This would often times spark other great discussions after the fact!&lt;/p&gt;&lt;p style="text-align: left;"&gt;Communicate early, and often.&amp;nbsp; It may feel unnatural, but members will appreciate it.&lt;/p&gt;&lt;p style="text-align: left;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 style="text-align: left;"&gt;Lesson 3: Plan Early for Low Stress&lt;/h2&gt;&lt;div&gt;Every time I procrastinated on planning an event, it was high stress.&amp;nbsp; Every time I started planning an event over a month in advance, it seemed like a breeze.&amp;nbsp; While this seems obvious, it can be easy to let planning wait until the last minute.&amp;nbsp; Here are some tips for low stress planning.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Start finding presenters for at least 2 or months out.&amp;nbsp; This gives them enough time to prepare content, and for back and forth communication on event details.&amp;nbsp; On Meetup.com, you can start preparing events as a draft, and wait to announce them when all the details are ironed out.&lt;/li&gt;&lt;li&gt;Have multiple events in the works.&amp;nbsp; Don't wait for one event to be done to start planning the next one.&amp;nbsp; At times, I had 3 events in the works for various parts of the year.&lt;/li&gt;&lt;li&gt;If you're providing food, order it a day ahead of time for delivery.&amp;nbsp; Most websites let you do this.&amp;nbsp; If you plan on running to the store, do it the day before.&amp;nbsp; Make sure you have plates, napkins, cups.&lt;/li&gt;&lt;li&gt;Try to raise cash from sponsors rather than having them reimburse expenses.&amp;nbsp; I will create a whole other post on this topic.&amp;nbsp; Cash sponsorship vs reimbursement made things so much easier!&lt;/li&gt;&lt;li&gt;Find other local meetups with similar interests to collaborate with.&amp;nbsp; This is great for both groups.&amp;nbsp; It helps spread the word about other meetups in the community, and helps create quality content for both groups.&amp;nbsp; I did many collaborations not only with other meetups, but also local companies like &lt;a href="https://careers.kohls.com/corporate/c/technology-jobs"&gt;Kohl's&lt;/a&gt;, and Digital Measures (now part of &lt;a href="https://www.watermarkinsights.com/"&gt;Watermark Insights&lt;/a&gt;).&amp;nbsp; Collaborating with companies also helps get people involved in the meetup community who may not have known such events even existed!&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXstfu_fJr1FULZ5ULmAYwsIxzKeGwDl1ElYGmeigdImk9OZREZXgApwHbNJHanFXeIsIC34JMr7vTlW8I_f9zdek-eUsdxyG2ir4aV8fX0tfeosdEl_kwsMwzL68B-efuanrqsi-Q7A/s1211/Screen+Shot+2021-06-16+at+8.32.12+AM.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXstfu_fJr1FULZ5ULmAYwsIxzKeGwDl1ElYGmeigdImk9OZREZXgApwHbNJHanFXeIsIC34JMr7vTlW8I_f9zdek-eUsdxyG2ir4aV8fX0tfeosdEl_kwsMwzL68B-efuanrqsi-Q7A/s320/Screen+Shot+2021-06-16+at+8.32.12+AM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyzk9pQ2-7GTf8LhNm80R9GeenpdJ83O7sZ8s0sxpx4luueVzIU5YVXELtxYm_q0DxeZN9k5lsjcq1rsJWb9bWRrKgT3CM0TxR9wplFoYJOSRgKgSUk3I2ZLDrlFQAX8UtbbuYvjf7zA/s1152/Screen+Shot+2021-06-16+at+8.32.23+AM.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyzk9pQ2-7GTf8LhNm80R9GeenpdJ83O7sZ8s0sxpx4luueVzIU5YVXELtxYm_q0DxeZN9k5lsjcq1rsJWb9bWRrKgT3CM0TxR9wplFoYJOSRgKgSUk3I2ZLDrlFQAX8UtbbuYvjf7zA/s320/Screen+Shot+2021-06-16+at+8.32.23+AM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;A collaboration with &lt;a href="https://www.stack41.com/"&gt;Stack 41&lt;/a&gt; to tour the &lt;a href="https://www.dataholdings.com/"&gt;Data Holdings&lt;/a&gt; Tier III+ Data Center.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h2&gt;Lesson 4: Take the Easy Route&lt;/h2&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;As your group grows, you may start to get members, or even those that just find you online wanting to present to the group.&amp;nbsp; Take them up on this!&amp;nbsp; Any time I could find someone willing to present quality content to the group I took it.&amp;nbsp; This approach really paid off in the long run, as those presenters will refer you to other quality presenters they know if they had an enjoyable time with your group.&amp;nbsp; &amp;nbsp;This approach is how we were able to attract quality, well known speakers like &lt;a href="https://twitter.com/brjavaman"&gt;Bruno Souza&lt;/a&gt;, and &lt;a href="https://twitter.com/starbuxman"&gt;Josh Long&lt;/a&gt;, among many other local top speakers.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTSewIqzZcHMpSBMwIAZ2AX5JQgNdpInNG6XrWbJxby6_zbakhNLMxOY1QgZFgn-kKTm1QbxptWL_KZ70GVyQu0-iaT8DYTztPj3Bojkyuq76OX9qPkrdv4xs3klYbhx1YTuedU0ZdUw/s1070/Screen+Shot+2021-06-16+at+8.30.53+AM.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTSewIqzZcHMpSBMwIAZ2AX5JQgNdpInNG6XrWbJxby6_zbakhNLMxOY1QgZFgn-kKTm1QbxptWL_KZ70GVyQu0-iaT8DYTztPj3Bojkyuq76OX9qPkrdv4xs3klYbhx1YTuedU0ZdUw/s320/Screen+Shot+2021-06-16+at+8.30.53+AM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://twitter.com/starbuxman" style="text-align: left;"&gt;Josh Long&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9gQOW9Gc9uEjwcsZ-7nJpm3W6pAWqodfyph8LAeJDQDQKcy44Ywoi5-nGZfqTcklR1TwC_ZFd61SAdRYzxFWIiwbR1KlmZRIsGUCPpTAe4toZ5N3bCk978aV_ib4UIwfdcm-9hRRbSQ/s1122/Screen+Shot+2021-06-16+at+8.31.46+AM.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9gQOW9Gc9uEjwcsZ-7nJpm3W6pAWqodfyph8LAeJDQDQKcy44Ywoi5-nGZfqTcklR1TwC_ZFd61SAdRYzxFWIiwbR1KlmZRIsGUCPpTAe4toZ5N3bCk978aV_ib4UIwfdcm-9hRRbSQ/s320/Screen+Shot+2021-06-16+at+8.31.46+AM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://twitter.com/brjavaman" style="text-align: left;"&gt;Bruno Souza&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When in doubt, do a lightning talk, or social.&amp;nbsp; There were at least a couple times where I couldn't find a speaker, and didn't have the time/energy to create a presentation myself.&amp;nbsp; Having a different event format like a lightning talk, hack session, or social are great ways to keep the group momentum up without having to.&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Finally, don't be afraid to lean on others for help.&amp;nbsp; Find a co-organizer to help you track down, and communicate with presenters, find sponsors, and other such tasks.&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style="text-align: left;"&gt;Lesson 5:&amp;nbsp; Get To Know Your Members&lt;/h2&gt;&lt;div&gt;One of the best parts of running a meetup is meeting so many diverse and interesting people.&amp;nbsp; Organizing the meetup opened many doors for me over the past 7 years.&amp;nbsp; While it was challenging, and sometimes felt overwhelming, it was well worth it.&amp;nbsp; &amp;nbsp;While I won't be organizing this particular meetup, I'll still be attending many others, and running into those same people over the years.&amp;nbsp; I've also made many connections to people and organizations outside of Milwaukee, which may come in handy some day.&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style="text-align: left;"&gt;The Bottom Line&lt;/h2&gt;&lt;div&gt;Are you considering starting a meetup?&amp;nbsp; I highly suggest you do.&amp;nbsp; I started the meetup because I saw a niche in the community that I thought I could fill.&amp;nbsp; I also wanted to improve my organizational and leadership skills, and meet more people in the community.&amp;nbsp; Running a meetup checked all those boxes for me, and it has been one of the more rewarding experiences in my personal and professional career.&amp;nbsp; If you have any questions, or just need encouragement, &lt;a href="https://twitter.com/a_glassman"&gt;just reach out to me on twitter!&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxMAbu2Gy7z7VhNLZFWs2xvJxS-AKP-bcKLvDJEpM8DHCkVcU-HOPKjVocEZua69OaexfcJ1AqWKQr_VtJHvCKdesTxaWStJrbRXn7s3O_4GWzVGFl8ehooEs5AFl8kvVYlP5LqgtE_A/s1352/Screen+Shot+2021-06-16+at+8.31.32+AM.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxMAbu2Gy7z7VhNLZFWs2xvJxS-AKP-bcKLvDJEpM8DHCkVcU-HOPKjVocEZua69OaexfcJ1AqWKQr_VtJHvCKdesTxaWStJrbRXn7s3O_4GWzVGFl8ehooEs5AFl8kvVYlP5LqgtE_A/s320/Screen+Shot+2021-06-16+at+8.31.32+AM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;</description><author>Milwaukee Maven</author><pubDate>Wed, 16 Jun 2021 16:47:43 GMT</pubDate><guid isPermaLink="true">https://blog.andyglassman.com/2021/06/so-long-and-thanks-for-all-pizza.html</guid></item><item><title>Lessons from Two Years of Fermenting</title><link>https://duncanlock.net/blog/2021/06/16/lessons-from-two-years-of-fermenting/</link><description>&lt;section id="preamble"&gt;&lt;figure class="image-block"&gt;&lt;img alt="Three mason jars, full of fermenting things. Two Bánh Mì(ish) and one eggs." src="https://duncanlock.net/images/posts/lessons-from-two-years-of-fermenting/IMG_20190330_104815-smaller.webp" width="400" /&gt;
&lt;figcaption&gt;Figure 1. Three mason jars, full of fermenting vegetables. Two Bánh Mì(ish) and one eggs.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I&amp;#8217;ve been fermenting vegetables of various kinds (and occasionally other things) at home for a little over two years. It&amp;#8217;s a fun, interesting &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; nutritious hobby. On the whole it&amp;#8217;s pretty easy and low stakes&amp;#8201;&amp;#8211;&amp;#8201;the worst thing that happens is a few cucumbers go&amp;nbsp;mouldy.&lt;/p&gt;
&lt;p&gt;Because it doesn&amp;#8217;t involve any heat&amp;#8201;&amp;#8211;&amp;#8201;but does include pouring, measuring, stirring, spiralizing, grating (and some chopping)&amp;#8201;&amp;#8211;&amp;#8201;it&amp;#8217;s ideal to do with little&amp;nbsp;kids.&lt;/p&gt;
&lt;p&gt;According to my notes (which I didn&amp;#8217;t start initially), I&amp;#8217;ve made ~56 batches of ferments, ranging in size from a single 1l mason jar, to three 2l&amp;nbsp;ones.&lt;/p&gt;
&lt;p&gt;These are my lessons so&amp;nbsp;far:&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_the_difference_between_science_screwing_around_is_writing_it_down"&gt;The difference between science &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; screwing around, is writing it&amp;nbsp;down&lt;/h2&gt;&lt;p&gt;As they say, the difference between science and screwing around is writing it down&amp;#8201;&amp;#8211;&amp;#8201;so keep notes. If you want to perfect recipes &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; techniques, write down what you did and iterate. Change things which didn&amp;#8217;t work out, ideally just changing one thing at a time, so you can see the&amp;nbsp;effect.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_leave_headspace_at_the_top_of_the_jar"&gt;Leave headspace at the top of the&amp;nbsp;jar&lt;/h2&gt;&lt;p&gt;Leave a layer of clear brine, above the ferment &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; weights. For short ferments up to a month, ~1.5&amp;#8221; or ~4 cm. For longer ferments, double it. Only the good lacto-fermenting bacteria can grow in the brine (mostly), so a layer of clear brine at the top will keep anything else from getting to your fermenting vegetables down below. Sometimes things grow on the surface (see below)&amp;#8201;&amp;#8211;&amp;#8201;but if you have enough headspace, you can just skim it off and it won&amp;#8217;t bother the ferments&amp;nbsp;below.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_keep_everything_underneath_the_brine"&gt;Keep everything underneath the&amp;nbsp;brine&lt;/h2&gt;&lt;p&gt;Make sure nothing rises to, or floats on, the surface. No matter what you do, &lt;em&gt;anything&lt;/em&gt; on the surface will go mouldy &lt;em&gt;eventually&lt;/em&gt;. Simple ways to do this are to push a whole cabbage leaf down over the top of everything, or a slice of rutabaga/swede that&amp;#8217;s about the same size as the jar&amp;#8201;&amp;#8211;&amp;#8201;then a weight on top of&amp;nbsp;that.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_what_is_the_white_stuff_on_the_surface_of_the_waterwhat_is_kahm_yeast_do_i_have_it"&gt;What is the white stuff on the surface of the water/What is Kahm Yeast &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; do I have&amp;nbsp;it?&lt;/h2&gt;&lt;p&gt;This is by far the most common question on &lt;a href="https://www.reddit.com/r/fermentation/"&gt;/r/fermentation&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="image-block"&gt;&lt;img alt="looking down at a white patchy layer of Kahm Yeast growing on the surface of the brine inside a wide mouth mason jar." src="https://duncanlock.net/images/posts/lessons-from-two-years-of-fermenting/IMG_20210122_082207.webp" width="300" /&gt;
&lt;figcaption&gt;Figure 2. Kahm Yeast growing on the surface of a ferment. Yes, this is what Kahm Yeast looks like. Yes, you will get it from time to time.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Kahm Yeast looks like a thin, white, mostly flat patchy layer on the surface of the brine. It&amp;#8217;s mostly harmless, but if you leave it too long it&amp;#8217;ll make things taste off&amp;#8201;&amp;#8211;&amp;#8201;skim it off with a spoon or kitchen&amp;nbsp;paper.&lt;/p&gt;
&lt;p&gt;Kahm yeast is white &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; fairly flat&amp;#8201;&amp;#8211;&amp;#8201;anything colourful, furry/hairy or lumpy is mould. If you catch the mould early, skim it off and carry on. If it looks black/pink/angry or has taken over, remove it, maybe try a pickle and see what you think, but maybe compost the lot to be on the safe&amp;nbsp;side.&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_use_wide_mouth_mason_jars_with_easy_fermenter_lids_weights"&gt;Use wide mouth mason jars, with Easy Fermenter lids &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;weights&lt;/h2&gt;&lt;p&gt;The ferment will produce some CO₂, causing jars/lids to burst. Either &amp;#8220;burp&amp;#8221; it by unscrewing the lid a little or don&amp;#8217;t fully tighten the&amp;nbsp;lid.&lt;/p&gt;
&lt;p&gt;Actually forget that, just use wide-mouth mason jars &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Easy Fermenter lids&amp;#8201;&amp;#8211;&amp;#8201;these are &lt;em&gt;excellent&lt;/em&gt;, reducing mould and increasing ease &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; reliability quite a bit: &lt;a href="https://amzn.to/3q2qMt5"&gt;Easy Fermenter Wide Mouth Lid&amp;nbsp;Kit&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Wed, 16 Jun 2021 16:43:05 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/16/lessons-from-two-years-of-fermenting/</guid></item><item><title>Algorithms for the IT Matura exam</title><link>https://kaszkowiak.org/en/blog/algorytmy-matura-z-informatyki/</link><description>I&amp;rsquo;ve put together, described (in Polish) and implemented (in Python) every required algorithm for the IT Matura exam.</description><author>Blog on Maciej Kaszkowiak</author><pubDate>Wed, 16 Jun 2021 15:33:06 GMT</pubDate><guid isPermaLink="true">https://kaszkowiak.org/en/blog/algorytmy-matura-z-informatyki/</guid></item><item><title>Lost River</title><link>https://sam.hooke.me/trip/2021/06/lost-river/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Wed, 16 Jun 2021 12:45:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/06/lost-river/</guid></item><item><title>Triumph Bonneville Dolomites Ride 2021</title><link>https://nicolaiarocci.com/triumph-bonneville-dolomites-ride-2021/</link><description>&lt;p&gt;So the motorcycle-touring season has started, albeit a little late this year
because of the COVID situation, and I&amp;rsquo;ve been avidly pursuing it. On Sunday,
I embarked on a 700 km trip from my place to the Dolomites and back.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been a glorious day.  We (&lt;a href="https://nicolaiarocci.com/musings-on-an-unexpected-motorcycle-trip/"&gt;Antonio&lt;/a&gt; and I) visited many renowned
Mountain Passes, enjoyed stunning views, ate great food and chatted like
there&amp;rsquo;s no tomorrow.&lt;/p&gt;
&lt;p&gt;I took my GoPro along with me. I wanted to record some scenes to inspire my
motorcycle club&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; comrades. However, I admit I was dubious that it would be
worth the hassle. Filming while driving would be difficult; editing and
assembling the cuts later would be tedious. Moreover, I have almost no
experience with this stuff.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Wed, 16 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/triumph-bonneville-dolomites-ride-2021/</guid></item><item><title>Magic typing with shortcuts on Linux</title><link>https://purpleidea.com/blog/2021/06/15/magic-typing-with-shortcuts-on-linux/</link><description>&lt;p&gt;Ever find yourself typing more characters than is necessary when using some
annoying application or website? Here&amp;rsquo;s a quick hack around that.&lt;/p&gt;


&lt;br /&gt;&lt;span style="text-decoration: underline; font-weight: bold;"&gt;Intro&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;

&lt;p&gt;There&amp;rsquo;s a magic program called &lt;code&gt;xdotool&lt;/code&gt; written by &lt;a href="https://twitter.com/jordansissel/"&gt;Jordan Sissel&lt;/a&gt;
that solves this problem. I&amp;rsquo;ve known about his excellent work for years, but
only today did I learn he also wrote &lt;code&gt;xdotool&lt;/code&gt;. It&amp;rsquo;s named as such because it
tells the &lt;code&gt;X&lt;/code&gt; server to do something for you.&lt;/p&gt;</description><author>The Technical Blog of James on purpleidea.com</author><pubDate>Wed, 16 Jun 2021 07:40:00 GMT</pubDate><guid isPermaLink="true">https://purpleidea.com/blog/2021/06/15/magic-typing-with-shortcuts-on-linux/</guid></item><item><title>What Are Attention Masks?</title><link>https://blog.lukesalamone.com/posts/what-are-attention-masks/</link><description>&lt;p&gt;TLDR: Attention masks allow us to send a batch into the transformer even when the examples in the batch have varying lengths. We do this by padding all sequences to the same length, then using the &amp;ldquo;attention_mask&amp;rdquo; tensor to identify which tokens are padding.&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Here we use a batch with three samples padded from the left since we want to predict the next token on the right. (Padding on the right would probably predict another pad.)" src="./img/attention_mask.png" /&gt;&lt;figcaption&gt;
			&lt;p&gt;Here we use a batch with three samples padded from the left since we want to predict the next token on the right. (Padding on the right would probably predict another pad.)&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Wed, 16 Jun 2021 03:09:36 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/what-are-attention-masks/</guid></item><item><title>xcodebuild's SPM support (Xcode 11)</title><link>https://xenodium.com/xcodebuilds-spm-support-xcode-11</link><description>&lt;p&gt;Had been a while since I looked into generating Xcode projects from a Swift package. On my latest use of the &lt;em&gt;generate-xcodeproj&lt;/em&gt; subcommand, I was greeted by a nice &lt;s&gt;warning&lt;/s&gt; surprise…&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;swift package generate-xcodeproj
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Xcode can handle Swift packages directly. Similarly, xcodebuild can handle them too. This isn't new. It's likely been available since Xcode 11. I just totally missed it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: I've yet to dig into Xcode 13 beta, as Swift packages may already support the build/test features I was after in xcodebuild (like &lt;a href="https://developer.apple.com/documentation/swift_packages/supportedplatform/3788290-maccatalyst"&gt;build/test on Catalyst&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In any case, on to xcodebuild… but let's first create a brand new Swift package.&lt;/p&gt;
&lt;h2&gt;Creating a Swift package library&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;mkdir FooBar &amp;amp;&amp;amp; cd FooBar
swift package init --type library
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;List package schemes&lt;/h2&gt;
&lt;p&gt;We can use xcodebuild to list the available schemes.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;xcodebuild -list
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Show supported platform, architecture, etc&lt;/h2&gt;
&lt;p&gt;Similarly, we can list destinations supported for the schemes.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;xcodebuild -showdestinations -scheme FooBar
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;macOS builds&lt;/h2&gt;
&lt;p&gt;Let's build for macOS, though let's first import UIKit into FooBar.swift. This ensures we get an expected failure when building for macOS.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import UIKit

struct FooBar {
  var text = &amp;quot;Hello, World!&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's attempt to build it…&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;xcodebuild build -quiet -scheme FooBar -destination 'platform=macOS'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The failure expected as UIKit isn't available on your typical macOS builds.&lt;/p&gt;
&lt;h2&gt;macOS Catalyst builds&lt;/h2&gt;
&lt;p&gt;We do, however, have Catalyst available, so we can use its variant to build for macOS with UIKit support, and.. voilà!&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;xcodebuild build -quiet -scheme FooBar -destination 'platform=macOS,variant=Mac Catalyst' &amp;amp;&amp;amp; echo \\o/
&lt;/code&gt;&lt;/pre&gt;</description><author>xenodium.com @alvaro</author><pubDate>Wed, 16 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/xcodebuilds-spm-support-xcode-11</guid></item><item><title>Building a Newborn Simulator</title><link>https://snevsky.com/blog/newborn-simulator</link><description>I built a browser game over paternity leave that simulates caring for a newborn: feed, change, rest, and manage your own stamina. Built with React.</description><author>Serge Nevsky</author><pubDate>Wed, 16 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://snevsky.com/blog/newborn-simulator</guid></item><item><title>Checking the Weather with Phillips Hue Light Bulbs</title><link>https://snevsky.com/blog/weather-on-phillips-hue-light-bulb</link><description>I wired Phillips Hue bulbs to the OpenWeatherMap API so a painting of Paris shows the current weather: Python, Docker, and two hours of coding.</description><author>Serge Nevsky</author><pubDate>Wed, 16 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://snevsky.com/blog/weather-on-phillips-hue-light-bulb</guid></item><item><title>Italian farms may benefit a lot from digitization...</title><link>https://stop.zona-m.net/2021/06/italian-farms-may-benefit-a-lot-from-digitization.../</link><description>&lt;p&gt;If they do not transform themselves in offices, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 16 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/italian-farms-may-benefit-a-lot-from-digitization.../</guid></item><item><title>2021–06–16: Pinephone keyboard input daemon released</title><link>https://xnux.eu/log/#040</link><author>megi's PinePhone Development Log</author><pubDate>Wed, 16 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#040</guid></item><item><title>Good, simple, Bash slugify function</title><link>https://duncanlock.net/blog/2021/06/15/good-simple-bash-slugify-function/</link><description>&lt;section id="preamble"&gt;&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;# Slugify&lt;/span&gt;
  &lt;span class="c"&gt;# Transliterate everything to ASCII&lt;/span&gt;
  &lt;span class="c"&gt;# Strip out apostrophes&lt;/span&gt;
  &lt;span class="c"&gt;# Anything that's not a letter or number to a dash&lt;/span&gt;
  &lt;span class="c"&gt;# Strip leading &amp;amp; trailing dashes&lt;/span&gt;
  &lt;span class="c"&gt;# Everything to lowercase&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;slugify&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  iconv &lt;span class="nt"&gt;-t&lt;/span&gt; ascii//TRANSLIT &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"'"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/[^a-zA-Z0-9]+/-/g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/^-+|-+$//g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s2"&gt;"[:upper:]"&lt;/span&gt; &lt;span class="s2"&gt;"[:lower:]"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is &lt;a href="https://www.oilshell.org/blog/2017/01/15.html"&gt;point-free style&lt;/a&gt;&amp;#8201;&amp;#8211;&amp;#8201;so, once you&amp;#8217;ve declared it, you can use it in a pipe, like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"This is my long Title, with some Punctuation!?"&lt;/span&gt;
&lt;span class="nv"&gt;title_slug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | slugify&lt;span class="si"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"This is my long Title, with some Punctuation!?"&lt;/span&gt; | slugify
&lt;span class="go"&gt;this-is-my-long-title-with-some-punctuation&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;Mostly modified from here: &lt;a class="bare" href="https://gist.github.com/oneohthree/f528c7ae1e701ad990e6"&gt;https://gist.github.com/oneohthree/f528c7ae1e701ad990e6&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/19335215/what-is-a-slug"&gt;What is a&amp;nbsp;Slug?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 15 Jun 2021 22:24:56 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/15/good-simple-bash-slugify-function/</guid></item><item><title>How to Rebuild Static Sites at Fixed Intervals with Netlify and GitHub Actions</title><link>https://rafaelquintanilha.com/how-to-rebuild-static-sites-at-fixed-intervals-with-netlify-and-github-actions/</link><description>Learn how to schedule redeployments at regular intervals by building a simple application that fetches stock market data.</description><author>Software Engineering - Rafael Quintanilha</author><pubDate>Tue, 15 Jun 2021 21:23:00 GMT</pubDate><guid isPermaLink="true">https://rafaelquintanilha.com/how-to-rebuild-static-sites-at-fixed-intervals-with-netlify-and-github-actions/</guid></item><item><title>Promise.allSettled in JavaScript</title><link>https://duncanlock.net/blog/2021/06/15/promise-allsettled-in-javascript/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;Sometimes I want to resolve several promises at once, then do something when they&amp;#8217;re all done. For example, make several &lt;span class="caps"&gt;API&lt;/span&gt; calls, then do something with all the&amp;nbsp;results.&lt;/p&gt;
&lt;p&gt;I was doing something like this&amp;nbsp;before:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Build your promises array&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://blah.com/static/file.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://blah.com/api/thing/one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://blah.com/apr/thing/two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thing1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thing2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A simpler, neater &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; easier to read version uses &lt;code&gt;Promise.allSettled(promises)&lt;/code&gt; to do the same&amp;nbsp;thing:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Build your promises array&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://blah.com/static/file.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://blah.com/api/thing/one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://blah.com/apr/thing/two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;// Resolve all the promises&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allSettled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thing1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thing2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Each promise in the original array is now a response object:&lt;/span&gt;
    &lt;span class="c1"&gt;// For each outcome object, a status string is present. If the status is fulfilled, then a value is present.&lt;/span&gt;
    &lt;span class="c1"&gt;// If the status is rejected, then a reason is present.&lt;/span&gt;
    &lt;span class="c1"&gt;// The value (or reason) reflects what value each promise was fulfilled (or rejected) with.&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thing2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a class="bare" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled"&gt;https://developer.mozilla.org/en-&lt;span class="caps"&gt;US&lt;/span&gt;/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="bare" href="https://javascript.info/promise-api"&gt;https://javascript.info/promise-api&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Tue, 15 Jun 2021 20:34:00 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/15/promise-allsettled-in-javascript/</guid></item><item><title>Towards a ToDoMVC Backend in Objective-S</title><link>https://blog.metaobject.com/2021/06/towards-todomvc-backend-in-objective-s.html</link><description>A couple of weeks ago, I showed a little http &lt;a href="https://blog.metaobject.com/2021/05/a-far-too-simple-harcoded-tasks-backend.html"&gt;backend&lt;/a&gt;.  Well, &lt;em&gt;tiny&lt;/em&gt; is probably a more apt description, and also aptly describes its functionality,
which is almost non-existent.  All it does is define a simplistic &lt;code&gt;Task&lt;/code&gt; class, create an array with two 
sample instances and then serves that array of tasks over http. And it serves the &lt;code&gt;-description&lt;/code&gt; of those tasks
rather than anything usefuk like a JSON encoding.&lt;p&gt;

For reference, this is the original code, hacked up in maybe 15 minutes:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
framework:ObjectiveHTTPD load.

class Task {
   var &amp;lt;bool&amp;gt; done.
   var title.
   -description { "Task: {this:title} done: {this:done}". }
}

taskList ← #( #Task{ #title: 'Clean my room', #done: false }, #Task{ #title: 'Check twitter feed', #done: true } ).

scheme todo {
   var taskList.
   /tasks { 
      |= { 
         this:taskList.
      }
   }
}.

todo := #todo{ #taskList: taskList }.
server := #MPWSchemeHttpServer{ #scheme: todo, #port: 8082 }.
server start.
shell runInteractiveLoop.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

What would it take to make this borderline useful?  First, we would probably need to encode the result as JSON, rather than
serving a description.  This is where Storage Combinators come in.  We (now) have a &lt;code&gt;MPWJSONConverterStore&lt;/code&gt; that's
a mapping store, it passes its "REST" requests through while performing certain transformations on the data and/or the
references.  In this case the transformation is serializing or deserialzing objects from/to JSON, depending on which 
way the request is going and which way the converter is pointing.&lt;p&gt;

In this case, the converter is pointing "up", that is it serializes objects read from its source to JSON and deserializes
data written to its source from JSON to objects.  We also tell it that it is dealing with &lt;code&gt;Task&lt;/code&gt; objects.
When we have the converter we connect it to our &lt;code&gt;todo&lt;/code&gt; scheme and tell the HTTP server to talk to the json
converter (which talks to our todo scheme):

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
todo := #todo{ #taskList: taskList, #store: persistence }.
json := #MPWJSONConverterStore{  #up: true, #class: class:Task }.
json → todo.
server := #MPWSchemeHttpServer{ #scheme: json, #port: 8082 }.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

Second, we also want to be to interact with individual tasks.  No problem, just add a &lt;code&gt;/task/:id&lt;/code&gt; proprerty path to our store/scheme handler,
along with GET ("|=") and PUT ("=|") handlers.  I am not fully sold yet on the "|=" syntax for this, but I would like to avoid names for this sort of
structural component.  Maybe arrows?

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
	/task/:id {
		|= {
			this:taskDict at:id .
		}
		=| {
			this:taskDict at:id put:newValue.
		}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

In order to facilitate this, the &lt;code&gt;taskList&lt;/code&gt; was changed to a dictionary.  Once we make changes to our data, we probably also want to persist it.
One easy way to do this is to store the tasks as JSON on disk.  This allows us to reuse the JSON converter from above, but this time pointing "down".  We
connect this converter to the filesystem at the directory &lt;code&gt;/tmp/tasks&lt;/code&gt; and to the store:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
json → todo → #MPWJSONConverterStore{  #class: class:Task } → ref:file:/tmp/tasks/ asScheme.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

In addition, we need to trigger saving in the PUT handler:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
		=| {
			this:taskDict at:id put:newValue.
			self persist.
		}
	-persist {
		source:tasks := this:taskDict allValues.
	}
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

This will (synchronously) write the entire task list on every PUT.

The full code is here:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
framework:ObjectiveHTTPD load.

class Task {
	var id.
	var  done.
	var title.
	-description { "Task: {this:title} done: {this:done} id: {this:id}". }
	-writeOnJSONStream:aStream {
		aStream writeDictionaryLikeObject:self withContentBlock:{ :writer |
			writer writeInteger: this:id forKey:'id'.
			writer writeString: this:title forKey:'title'.
			writer writeInteger: this:done forKey:'done'.
		}.
	}
}

taskList ← #( #Task{ #id: '1', #title: 'Clean Room', #done: false }, #Task{ #id: '2', #title: 'Check Twitter', #done: true } ).

scheme todo : MPWMappingStore {
	var taskDict.
	-setTaskList:aList {
		this:taskDict := NSMutableDictionary dictionaryWithObjects: aList forKeys: aList collect id.
	}
	/tasks { 
		|= { 
			this:taskDict allValues.
		}
	}
	/task/:id {
		|= {
			this:taskDict at:id .
		}
		=| {
			this:taskDict at:id put:newValue.
			self persist.
		}
	}
	-persist {
		source:tasks := this:taskDict allValues.
	}
}.

todo := #todo{ #taskList: taskList }.
json := #MPWJSONConverterStore{  #up: true, #class: class:Task }.
json → todo → #MPWJSONConverterStore{  #class: class:Task } → ref:file:/tmp/tasks/ asScheme.
server := #MPWSchemeHttpServer{ #scheme: json, #port: 8082 }.
server start.
shell runInteractiveLoop.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

The &lt;code&gt;writeOnJSONStream:&lt;/code&gt; method is currently still needed by the serializer to encode the task object as JSON.  The parser doesn't need
any support, it can figure things out by itself for simple mappings.  Yes, this makes no sense, as serializing is easier than parsing, but I
haven't gotten around to the automation for serializing yet.&lt;p&gt;

&lt;h3&gt;Analysis&lt;/h3&gt;

So there you have it, an almost functional Todo backend, in refreshingly little code, and with refreshingly little magic.
What I find particularly pleasing is that this conciseness can be achieved while keeping the architecture fully visible
and maintaining a hexagonal/ports-and-adapters style.&lt;p&gt;

What is the architecture of this app?  It says so right at the end:  the server is parametrized by its scheme, and that scheme
is a JSON serializer hooked up to my todo scheme handler, hooked up to another JSON serializer hooked up to the directory &lt;code&gt;/tmp/tasks&lt;/code&gt;.&lt;p&gt;

Although a Rails &lt;a href="https://github.com/doerfli/todo-backend-rails5-api"&gt;app&lt;/a&gt; contains comparably little code, this code is scattered over different classes and is only comprehensible as a
plugin to Rails.  All the architecture is hidden inside Rails, it is not at all visible in the code and simply cannot be
divined from looking at the code.  Although there are many reasons for this, one fundamental one is that Ruby is a call/return
language, and Rails does its best to translate from the REST architectural style to something that is more natural in the 
call/return style. And it does an admirable job at it.&lt;p&gt;

I do think that this example gives us a little glimpse into what I believe to be the power of Architecture Oriented Programming:  the
power and succinctness of frameworks, but with the simplicity, straightforwardness and reusability of more library-oriented styles.  


&lt;h3&gt;Performance&lt;/h3&gt;

I obviously couldn't resist benchmarking this, and to my great joy found that &lt;a href="https://github.com/wg/wrk"&gt;wrk&lt;/a&gt; now
works on the M1.  Since the interpreter isn't thread safe, I had to restrict it to a single connection and thread.  My
expectations were that it requests/s would be in the double to low triple digits, my fear was that it would be single
digits.  (The reason for that fear is the &lt;code&gt;writeOnJSONStream:&lt;/code&gt; method that is called for every object 
serialized and is in interpreted Objective-S, probably one of the slowest language implementations currently in existence).

To say I was surprised is an understatement.  Stunned is more like it:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
wrk -c 1 -t 1 http://localhost:8082/task/1 
Running 10s test @ http://localhost:8082/task/1
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   133.62us   14.45us   0.97ms   98.52%
    Req/Sec     7.50k   311.09     7.62k    99.01%
  75326 requests in 10.10s, 12.28MB read
Requests/sec:   7458.60
Transfer/sec:      1.22MBTransfer/sec:      1.97MB
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

More than 7K requests per second!  Those M1 Macs really are fast.  I wonder what it will be once I remove the need for the manually
written &lt;code&gt;writeOnJSONStream:&lt;/code&gt; method.&lt;p&gt;

(NOTE: previous version said &gt;12K requests/s, which is even more insane, but was with an incorrect URL that had the server returning 404s)</description><author>metablog</author><pubDate>Tue, 15 Jun 2021 15:55:58 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/06/towards-todomvc-backend-in-objective-s.html</guid></item><item><title>if let it be</title><link>https://blog.metaobject.com/2021/06/if-let-it-be.html</link><description>One of funkier aspects of Swift syntax is the &lt;code&gt;if let&lt;/code&gt; statement.  As far as I can tell, it exists pretty
much exclusively 
to check that an optional variable actually does contain a value and if it does, work with a no-longer-optional version
of that variable.&lt;p&gt;

Swift packages this functionality in a combination &lt;code&gt;if&lt;/code&gt; statement and &lt;code&gt;let&lt;/code&gt; declaration:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
if let value = value {
   print("value is \(value)")
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

This has a bunch of problems that are explained nicely in a &lt;a href="https://forums.swift.org/t/lets-fix-if-let-syntax/48188/25"&gt;Swift Evolution thread&lt;/a&gt; (via &lt;a href="https://mjtsai.com/blog/2021/05/12/fixing-swifts-if-let-syntax/"&gt;Michael Tsai&lt;/a&gt;) together with some proposals to fix it.  One of the issues is the idiomatic
repitition of the variable name, because typically you do want the same variable, just with less optionality.  Alas,
code-completion apparently doesn't handle this well, so the temptation is to pick a non-descriptive variable name.&lt;p&gt;

In my previous post (&lt;a href="https://blog.metaobject.com/2021/06/asynchronous-sequences-and-polymorphic.html"&gt;Asynchronous Sequences and Polymorphic Streams&lt;/a&gt;) I noted how the fact that iteration in Smalltalk and Objecive-S is done via 
messages and blocks means that there is no separate concept of a "loop-variable", that is just an argument to the
block.&lt;p&gt;

Conditionals are handled the same way, with blocks and messages, but normally don't pass arguments to their 
argument blocks, because in normal conditionals those arguments would always be just the constants
&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.  Not very interesting.&lt;p&gt;

When I added &lt;code&gt;ifNotNil:&lt;/code&gt; some time ago, I used the same logic, but it turns out the object
is now actually potentially interesting.  So &lt;code&gt;ifNotNil:&lt;/code&gt; now passes the now-known-to-be-non-nil
value to the block and can be used as follows:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
value ifNotNil:{ :value |
    stdout println:value.
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

This doesn't eliminate the duplication, but does avoid the issue of having the newly
introduced variable name precede the original variable.  Well, that and the whole
weird &lt;code&gt;if let&lt;/code&gt; in the first place.&lt;p&gt;

With anonymous block arguments, we actually don't have to name the parameter at all:
&lt;p&gt;
&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
value ifNotNil:{ stdout println:$0. }
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

Alternatively, we can just take advantage of some conveniensces and use a HOM 
instead:
&lt;p&gt;
&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
value ifNotNil printOn:stdout.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

Of course, Objective-S currently doesn't care about optionality, and with the current 
nil-eating behavior, the &lt;code&gt;ifNotNil&lt;/code&gt; is 
not strictly necessary, you could just write it as follow:
&lt;p&gt;
&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
value printOn:stdout.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;


I haven't really done much thinking about
it, but the whole idea of optionality shouldn't really be handled in the
space of values, but in the space of references.  Which are first class
objects in Objective-S.&lt;p&gt;

So you don't ask a value if it is nil or not, you ask the variable if 
it contains a value:

&lt;p&gt;
&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
ref:value ifBound:{ :value | ... }
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

To me that makes a lot more sense than having every type be accompanied
by an optional type.&lt;p&gt;

So if we were to care about optionality so in the future, we have the
tools to create a sensible solution. And we can let &lt;code&gt;if let&lt;/code&gt; 
just be.&lt;p&gt;</description><author>metablog</author><pubDate>Tue, 15 Jun 2021 13:37:52 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/06/if-let-it-be.html</guid></item><item><title>Asynchronous Sequences and Polymorphic Streams</title><link>https://blog.metaobject.com/2021/06/asynchronous-sequences-and-polymorphic.html</link><description>Browsing the WWDC '21 session videos, I came across the &lt;a href="https://developer.apple.com/wwdc21/10058"&gt;session on Asynchronous Sequences&lt;/a&gt;. 
The preview image showcased some code for asynchronously fetching and massaging current earthquake data from the U.S. Geological Survey:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
@main
struct QuakesTool {
   static func main() async throws {
      let endpointURL = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv")!

      for try await event in endpointURL.lines.dropFirst() {
         let values = event.split(separator: ",")
         let time = values[0]
         let latitude = values[1]
         let longitude = values[2]
         let magnitude = values[4]
         print("Magnitude \(magnitude) on \(time) at \(latitude) \(longitude)")
      }
   }
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;
This is nice, clean code, and it certainly looks like it serves as a good showcase for the benefits of
asynchronous coding with async/await and asynchronous sequences built on top of async/await.&lt;p&gt;

Or does it?&lt;p&gt;

Here is the equivalent code in &lt;a href="http://objective.st"&gt;Objective-S&lt;/a&gt;:
&lt;p&gt;
&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
stream ← ref:https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv linesAfter:1.

stream do: { :theLine |
   values ← theLine componentsSeparatedByString:','.
   time ← values at:0.
   latitude ← values at:1.
   longitude ← values at:2.
   magnitude ← values at:4.
   stdout println:"Quake: magnitude {magnitude} on {time} at {latitude} {longitude}".
}. 
stream awaitResultForSeconds:20.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;
&lt;p&gt;

Objective-S does not (and will not) have async/await, but it can nevertheless provide the equivalent functionality easily and elegantly.  How?  Two features:

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://conf.researchr.org/details/dls-2019/dls-2019/7/Standard-Object-Out-Streaming-Objects-with-Polymorphic-Write-Streams"&gt;Polymorphic Write Streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Messaging&lt;/li&gt;
&lt;/ol&gt;

Let's see how these two conspire to make adding something equivalent to &lt;code&gt;for try await&lt;/code&gt; trivial.

&lt;h3&gt;Polymorphic Write Streams&lt;/h3&gt;



In the Objective-S implementation, &lt;code&gt;https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv&lt;/code&gt; is not a string,
but an actual identifier, a Polymorphic Identifier, adding the &lt;code&gt;ref:&lt;/code&gt; prefix turns it into a binding, a first class variable.
You can ask a binding for its &lt;code&gt;value&lt;/code&gt;, but for bindings that can also be regarded as collections of some kind, you can also
ask them for a &lt;code&gt;stream&lt;/code&gt; of their values, in this particular case a &lt;code&gt;&lt;a href="https://github.com/mpw/MPWFoundation/blob/master/Streams.subproj/MPWURLStreamingStream.m"&gt;MPWURLStreamingStream&lt;/a&gt;&lt;/code&gt;.  This stream is a &lt;a href="https://github.com/mpw/MPWFoundation/blob/master/Documentation/Streams.md"&gt;Polymorphic Write Stream&lt;/a&gt; that can be easily composed with other filters to create pipelines.  The &lt;code&gt;linesAfter:&lt;/code&gt;
method is a convenience method that does just that:  it composes the URL fetcher with a filter that converts from bytes to lines of
text and another filter that drops the first &lt;em&gt;n&lt;/em&gt; items.&lt;p&gt;

Objective-S actually has convenient syntax for creating these compositions without having to do it via convenience methods, but I wanted
to keep differences in the surrounding scaffolding small for this example, which is about the &lt;code&gt;for try away&lt;/code&gt; and &lt;code&gt;do:&lt;/code&gt;.&lt;p&gt;

When I encountered the example, Polymorphic Write Streams actually did not have a &lt;code&gt;do:&lt;/code&gt; for iteration, but it was trivial to add:&lt;p&gt;

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
-(void)do:aBlock
{
    [self setFinalTarget:[MPWBlockTargetStream streamWithBlock:aBlock]];
    [self run];
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

(This code lives in MPWFoundation, so it is in Objective-C, not Objective-S).&lt;p&gt;

Those 5 lines were all that was needed.  I did not have to make substantive changes to the language or its implementation.  One reason
for this is that Polymorphic Write Streams are &lt;em&gt;asynchrony-agnostic&lt;/em&gt;:  although they are mostly implemented as straightforward
synchronous code, they work just as well if parts of the pipeline they are in are asynchronous.  It just doesn't make a difference,
because the semantics are in the data flow, not in the control flow.&lt;p&gt;



&lt;h3&gt;Messaging&lt;/h3&gt;

The other big reason an asynchronous &lt;code&gt;do:&lt;/code&gt; was easy to add is &lt;em&gt;&lt;a href="http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html"&gt;messaging&lt;/a&gt;&lt;/em&gt;. 

&lt;blockquote&gt;
If you focus on just messaging -- and realize that a good metasystem can
late bind the various 2nd level architectures used in objects -- then much
of the language-, UI-, and OS based discussions on this thread are really
quite moot.
&lt;/blockquote&gt;

One of the many really, really neat ideas in Smalltalk is how control structures, which in most other languages
are special language features, are just plain old messages and implemented in the library, not in the language.&lt;p&gt;

So the &lt;code&gt;for ... in&lt;/code&gt; loop in Swift is just the &lt;code&gt;do:&lt;/code&gt; message sent to a collection, and the
keyword syntax makes this natural:


&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
for event in lines {
...
}
...
lines do: { :event |
...
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;
Note how making loops regular like this also makes the special concept of "loop variable"
disappear.  The "loop variable" is just the block argument.  And I just realized the same
would go for a not-nil result of a nil test.&lt;p&gt;

Anyway, if "loops" are just messages, it's easy to add a method implementing iteration to some other
entity, for example a stream, the way that I did.  (Smalltalk streams also support the 
iteration messages).&lt;p&gt;

And when you can easily make stream processing, which can handle asynchrony naturally and 
easily, just as convenient as imperative programming,
you don't need async/await, which tries to make asynchronous programming &lt;em&gt;look&lt;/em&gt; like
imperative programming in order to make it convenient.&lt;p&gt;</description><author>metablog</author><pubDate>Tue, 15 Jun 2021 12:07:52 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/06/asynchronous-sequences-and-polymorphic.html</guid></item><item><title>[Computing] Freenode commits suicide, is no longer a serious IRC network</title><link>https://www.devever.net/~hl/freenode_suicide</link><description>&lt;p&gt;Since this happened after I was &lt;a href="freenode_kline"&gt;banned from Freenode&lt;/a&gt;, I
didn't get to observe this first hand, but this is the situation as I now
understand it.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Tue, 15 Jun 2021 06:39:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/freenode_suicide</guid></item><item><title>Writing fast async HTTP requests in Python</title><link>https://jonlu.ca/posts/async-python-http</link><description>Controlling concurrency with asyncio and semaphores to make large batches of Python HTTP requests fast.</description><author>JonLuca's Blog</author><pubDate>Tue, 15 Jun 2021 06:02:29 GMT</pubDate><guid isPermaLink="true">https://jonlu.ca/posts/async-python-http</guid></item><item><title>How Does Convolution Work?</title><link>https://blog.lukesalamone.com/posts/how-does-convolution-work/</link><description>&lt;p&gt;Convolutional neural networks have had breakthrough success in image recognition, natural language processing, and even board games like Chess and Go. But what&amp;rsquo;s really going on during convolution? Well, I think the easiest way to explain is with an interactive demo. Feel free to play around with the parameters below to see for yourself!&lt;/p&gt;



&lt;div id="input-output"&gt;
  &lt;div id="input-grid"&gt;&lt;/div&gt;
  &lt;div id="output-grid"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id="controls"&gt;
  &lt;table&gt;
    &lt;tr id="number"&gt;
      &lt;td&gt;number:&lt;/td&gt;
      &lt;td&gt;
        &lt;select&gt;
          &lt;option value="four"&gt;four&lt;/option&gt;
          &lt;option value="three"&gt;three&lt;/option&gt;
          &lt;option value="eight"&gt;eight&lt;/option&gt;
        &lt;/select&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr id="padding"&gt;
      &lt;td&gt;padding: &lt;span class="val"&gt;&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input max="2" min="0" type="range" value="0" /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr id="kernelsize"&gt;
      &lt;td&gt;kernel size: &lt;span class="val"&gt;&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input max="4" min="1" type="range" value="2" /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr id="stride"&gt;
      &lt;td&gt;stride: &lt;span class="val"&gt;&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input max="3" min="1" type="range" value="1" /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr id="speed"&gt;
      &lt;td&gt;speed: &lt;span class="val"&gt;&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;&lt;input max="5" min="1" type="range" value="3" /&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr id="errors" style="display: none;"&gt;
      &lt;td colspan="2"&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;You can use the settings above to control the hyperparameters of the convolutional layer.&lt;/p&gt;</description><author>Luke Salamone's Blog</author><pubDate>Tue, 15 Jun 2021 05:05:06 GMT</pubDate><guid isPermaLink="true">https://blog.lukesalamone.com/posts/how-does-convolution-work/</guid></item><item><title>Android Privacy Guide</title><link>https://cookie.engineer/weblog/articles/android-privacy-guide.html</link><description>Best practices for Android and recommended Apps that respect your Privacy.</description><author>Cookie Engineer's Web Log</author><pubDate>Tue, 15 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://cookie.engineer/weblog/articles/android-privacy-guide.html</guid></item><item><title>Lamps are too smart if they can do this</title><link>https://stop.zona-m.net/2021/06/lamps-are-too-smart-if-they-can-do-this/</link><description>&lt;p&gt;Seriously. Why can&amp;rsquo;t they be just lamps?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 15 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/lamps-are-too-smart-if-they-can-do-this/</guid></item><item><title>IPFS misconceptions</title><link>https://voussoir.net/writing/ipfs_misconceptions</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;IPFS misconceptions&lt;/h1&gt;&lt;ol id="table_of_contents"&gt;Table of contents&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#ipfs_is_not_a_place_you_upload_to_its_a_network_youre_a_part_of"&gt;IPFS is not a place you upload to, it's a network you're a part of&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#ipfs_does_not_force_others_to_host_your_files"&gt;IPFS does not force others to host your files&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#ipfs_is_not_anonymous_or_dark_web"&gt;IPFS is not anonymous or "dark web"&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#ipfss_copywriting_is_bad"&gt;IPFS's copywriting is bad&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;a class="header_anchor_link" href="#introduction"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am interested in &lt;a href="https://ipfs.io/"&gt;IPFS&lt;/a&gt;. I don't run a node, but I look forward to seeing the technology grow. I like peer-to-peer networks and hope they make a strong resurgence.&lt;/p&gt;
&lt;p&gt;However, IPFS has marketing problems. I am subscribed to &lt;a href="https://old.reddit.com/r/ipfs"&gt;/r/ipfs&lt;/a&gt; which is inundated daily with posts by people who have clearly gotten the wrong idea about what IPFS is and isn't, and the IPFS homepage hasn't exactly stepped up to clarify the misunderstandings.&lt;/p&gt;
&lt;h2 id="ipfs_is_not_a_place_you_upload_to_its_a_network_youre_a_part_of"&gt;IPFS is not a place you upload to, it's a network you're a part of&lt;a class="header_anchor_link" href="#ipfs_is_not_a_place_you_upload_to_its_a_network_youre_a_part_of"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All of the quotes in this article are real posts from &lt;a href="https://old.reddit.com/r/ipfs"&gt;/r/ipfs&lt;/a&gt; or other forums:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Won't there be hard drive capacity isssues if everyone used IPFS?&lt;/p&gt;
&lt;p&gt;The way I understand it is everyone who opts in to be an IPFS server will download all the content on the web that I browse to, and host it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Would it be a good idea to make an ipfs mobile app&lt;/p&gt;
&lt;p&gt;They have made a web browser plug-in but I'm wondering if it would be a good idea to somehow make an app for your mobile device that works with ipfs maybe something you could use to sync your contacts but then again to run into the same problem you would need somebody to run the gateway node for you I mean I have 64 gigabytes of storage on my phone but I don't think it is enough to run and ipfs Gateway on there lol I had a computer with a 2 terabyte hard drive and it managed to fill that thing up in less than a week I Can Only Imagine how fast it could fill up 64GB&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Any way to get an mp4 to loop infinitely when uploading to IPFS?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;After uploading content on the IPFS network and having its relative CID address available, that content and address will never be more removable or editable, correct?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;IPFS Soundcloud. How would one upload an audio file to IPFS through the browser?&lt;/p&gt;
&lt;p&gt;I am new to IPFS and javascript programming in general. I am trying to build the barebones beginnings of a "Soundcloud" clone that can live and stream entirely from IPFS.&lt;/p&gt;
&lt;p&gt;I was able to get the streaming working but even after reading documentation, I'm having trouble figuring out how to implement uploading. I know there's a method to ipfs-add a file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;I thought when you upload something on IPFS it is stored across all nodes on the network. What makes it different from centralized services? How to know who are using IPFS that is global and won't get down?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Is it a good idea to store avatars for my project on IPFS?&lt;/p&gt;
&lt;p&gt;I want to use the least amount of money for my project as a challenge so I was wondering if it was possible and is a good idea to store avatars (around 2-8mb) on IPFS and use an API to access them later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;How does ipfs avoid spam and bad actors?&lt;/p&gt;
&lt;p&gt;What if I would try to upload several petabytes of data, what would prevent me doing that to overload the current network?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;I heard that any node will host also a bit of other random ones, what can have illegal things. Am I totally safe for using IPFS?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Hey guys I decided to spin up own IPFS node.&lt;/p&gt;
&lt;p&gt;Works perfect in docker 24/7.&lt;/p&gt;
&lt;p&gt;Some questions:&lt;/p&gt;
&lt;p&gt;How permanent are IPFS uploads?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The number one problem with IPFS's public perception is that a huge, huge number of people think that IPFS is a place you just "upload" to, and it'll hold your files forever. People see the word "decentralized" or "distributed" and assume that IPFS distributes your files for you, for free, everywhere.&lt;/p&gt;
&lt;p&gt;IPFS is a peer-to-peer system, comparable to &lt;a href="https://en.wikipedia.org/wiki/BitTorrent"&gt;bittorrent&lt;/a&gt;. You are a peer, and you host files so that other peers can download them from you. When you use the "import file" button in the IPFS client GUI, or the &lt;code&gt;ipfs add&lt;/code&gt; command on the CLI, you are simply telling the rest of the world that you have a file you'd like to share. It doesn't actually get uploaded anywhere until another person asks you for it. That's because IPFS is not a place you upload to, it's a network you're a part of.&lt;/p&gt;
&lt;p&gt;Most end users are not accustomed to being an equal, active participant in the great glowing mass we call the internet. We feel that 'the internet' is this big thing that is separate from ourselves, managed by far away entities with billions of dollars. We are accustomed to interactions between service providers: Google, Apple, Facebook; and service users: us. We upload photos to Facebook and then leave. We upload videos to YouTube and then leave. The internet just runs 24/7 and I don't have to do anything about it.&lt;/p&gt;
&lt;p&gt;Proportionally, the number of people who have ever hosted a web server or game server for their friends, or transferred files between two devices in their home via LAN IP is dwindling all the time. Instead we rely on web hosting companies or "official" game servers and we forget that this power was ever within our grasp. Recently I explained to someone that a modem connects your house to the internet and your router connects your devices to each other — and they asked me why you'd want to do that. The word "internet" means "between networks", and necessarily postdates the smaller institutional- and home-level networks we started with.&lt;/p&gt;
&lt;p&gt;You can be a part of the World Wide Web even if you don't own a domain name: just run an http server program, open your ports, and give your friends your IP. You're live. Welcome to the internet. It's made by people, and you're a people too. You can even run a website &lt;a href="/writing/ipfs_misconceptions/phone_server.png"&gt;off of a cell phone&lt;/a&gt;, except that Google and Apple are making this harder all the time in order to groom your Stockholm syndrome.&lt;/p&gt;
&lt;p&gt;To be a part of IPFS, you need to download and run the client software, either on your home PC or on a rented server, and allow other people to connect to you. Then you need to &lt;em&gt;leave it running&lt;/em&gt;, because if you turn it off then of course nobody can get your files. What did you expect.&lt;/p&gt;
&lt;p&gt;The selling point of IPFS — the nature of its decentralization — is that anybody who downloads your files can seed them to anyone else using the exact same link. The &lt;code&gt;ipfs://&lt;/code&gt; links contain the hash of the file rather than the address of your computer specifically.&lt;/p&gt;
&lt;p&gt;This is the difference between IPFS and the regular HTTP web: if a video gets deleted from youtube.com, it's &lt;em&gt;possible&lt;/em&gt; someone else in the world made a copy, but how are you going to find that person? If you get lucky, someone will generously reupload it, but the link will be different and hard to find. And if that link gets deleted, you need to find another one. Over and over again. With IPFS, the original link works as long as a single person in the solar system is willing to keep it alive.&lt;/p&gt;
&lt;p&gt;There are companies or entities known as &lt;a href="https://docs.ipfs.io/how-to/work-with-pinning-services/"&gt;pinning services&lt;/a&gt;, who will host your files on IPFS for you if you pay them. However, even this is not "uploading to IPFS" any more than uploading a file to Amazon S3 is "uploading to The Internet". The agreement to host the file in exchange for money is between you and the service provider. IPFS the protocol and the IPFS community at large aren't responsible if that host goes out of business and stops hosting your file.&lt;/p&gt;
&lt;h2 id="ipfs_does_not_force_others_to_host_your_files"&gt;IPFS does not force others to host your files&lt;a class="header_anchor_link" href="#ipfs_does_not_force_others_to_host_your_files"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;Who pays for the storage on IPFS?&lt;/p&gt;
&lt;p&gt;I keep coming back to the same question, who pays for the storage space. Like I can create a local node and upload my draft-version2-history report but why on earth would that be getting replicated across the network. It's great for me because now I can globally access it, but who would want to store that file solely for me who didn't pay anything for that replication?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What stops ipfs from becoming a tragedy of the commons?&lt;/p&gt;
&lt;p&gt;I look at my own hard drive and half the contents - perhaps most - is ephemeral, intermediate, or junk. Sometimes I'm forced to sift through Everest-sized piles of digital rubbish to reclaim disk space, though in the process I inevitably lose some of the baby along with the bathwater.&lt;/p&gt;
&lt;p&gt;Now imagine all that cruft on a planetary scale, petabytes of obsolete memes, shopping lists, programs in the process of being developed, temporary files — and billions of idiots who pin them all someplace else as a free cloud backup.&lt;/p&gt;
&lt;p&gt;If I run a node on my home server, how many terabytes do I reserve to replicate an infinitely expanding pin list, assuming I only have to host the content I offer or have previously downloaded?&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What makes you think you can just "pin them all some place else as a free cloud backup"? Most pinning services charge you per GB*month (or equivalent) for the things you pin. If you stop paying, they stop pinning it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I just want to be sure I understand the objective — is it fundamentally a gimmick to part rubes from their crypto, or a genuine innovation in distributed storage?&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;ipfs has nothing to do with bitcoin. Its just an alternative to http. its a way to share data that is not inherently centralized.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I expect most or all cluster providers will require or at least allow payment by means of their preferred wankcoin, don't you?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What's the difference between storing files on AWS cloud and IPFS?&lt;/p&gt;
&lt;p&gt;From this subreddit, I learned that IPFS does not store your files permanently even if I pinned them. If so, why should I store on it? So that my files can't be hacked? So that in case Amazon goes bankrupt I still have my files? But if I use a pinned service like Pinata.cloud, they might go bust too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;If I host my own node, how to ensure that the file I uploaded goes into my node?&lt;/p&gt;
&lt;p&gt;I learned that files are not forever on ipfs and the best way to make them permanent is by hosting my own node. So, if I do host one, do I just redirect my files into my node or will ALL nodes have ALL files from around the world from everyone?&lt;/p&gt;
&lt;p&gt;Cos otherwise, if all my files are only in my node then what's the point of decentralization.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What's the point of IPFS? Why wouldn't I just host the files myself?&lt;/p&gt;
&lt;p&gt;From what I understand, IPFS isn't private, your IP is public, and nodes only host the files they want to host.&lt;/p&gt;
&lt;p&gt;Since most people only care about their own stuff, most files will be hosted from a single machine with a public IP.&lt;/p&gt;
&lt;p&gt;Why wouldn't I just host the files from a single machine with a public IP?&lt;/p&gt;
&lt;p&gt;What is IPFS actually providing?&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I am grappling with a similar question, how can we incentivize fellow nodes to pin our data? Is there any quid pro quo proposal or protocol in place?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Okay so if no one pinned anything and just hosted what they host then it would be just like the internet without IPFS. So what incentivises people to pin?&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Incentive seems to be one of the fundamentally unsolved problems for decentralized storage. Not only does hosting content need to be profitable, but there needs to be systems in place that make sure that users aren't just hosting the same popular content that gets the most traffic and dumping less popular content because it isn't generating the same amount of revenue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;This misconception often stems from the first, that IPFS is a giant magical hard drive in the sky where other people will hold on to your stuff for you forever, so naturally there must be some big incentive for them to do that. "What's in it for me?" is the only language we understand. That was the idea behind &lt;a href="https://filecoin.io/"&gt;Filecoin&lt;/a&gt; — it gives members a financial incentive to share their storage space with the network. As it happens, Filecoin operates on a &lt;a href="https://github.com/filecoin-project/specs/issues/1191#issuecomment-704065968"&gt;completely separate peer network&lt;/a&gt;, unreachable by the regular IPFS client, since they need to keep their accounts payable in order. Yet, people conflate Filecoin with IPFS itself, and assume that all IPFS involves the use of Filecoin or other payment, because they can't fathom what sort of incentives exist besides financial ones.&lt;/p&gt;
&lt;p&gt;There is no "push" mechanism in IPFS. There is no forcing people to take a copy of your stuff if they didn't specifically ask for it. The IPFS homepage says "each network node stores only content it is interested in".&lt;/p&gt;
&lt;p&gt;That's right, idiots. Nobody's going to help you replicate your history book report for the same reason that they won't let you put your lawnmower in their garage after your garage gets full. They're not interested. This shouldn't be surprising.&lt;/p&gt;
&lt;p&gt;This is related to the previous idea, that people are only familiar with online services backed by faceless corporations. It's difficult to imagine that the system is made of actual people and you have to make it worth their while to download what you're sharing. That's the magical effect of money, of course: people &lt;strong&gt;will&lt;/strong&gt; suddenly become interested in your homework assignments and lawnmowers if you pay them to be. But in the absence of the financial incentive, you are left with the fact that nobody else is interested in your random junk files.&lt;/p&gt;
&lt;p&gt;For many of these people, the line of reasoning stops there. If the network won't host my stuff for me for free, it's pointless. Ok, bye. Meanwhile, the people who generate valuable data that is interesting and desirable to others will see their files propagate. If NASA started advertising CIDs for original moon landing footage, there would be plenty of volunteers replicating it.&lt;/p&gt;
&lt;p&gt;Many of these comments assume that all other IPFS nodes are strangers, but why? IPFS can very well be deployed in an organization where no one is a stranger to each other, and they don't need financial incentives to host each others' files. A website operator could serve their javascript or image assets over IPFS, and put lots of IPFS nodes around the world so that visitors can connect to the nearest one. If random strangers want to pin the assets, they can do that too, but the website owner would always know that keeping their files alive is their responsibility. They would never &lt;em&gt;rely&lt;/em&gt; on strangers' pins when the functionality of their website is at stake.&lt;/p&gt;
&lt;p&gt;Here is another example: imagine that a team of researchers in Antarctica (or on Mars) are accessing a website over IPFS. The first researcher will have to download the page and its assets over a slow satellite connection that leads back to the original node, but then they can seed the files to the other researchers over a faster local network. These peers are incentivized to help each other because it leads to a better experience for the whole group, not because they're getting paid.&lt;/p&gt;
&lt;p&gt;The point of IPFS is not "strangers keep my stuff", it's "people sharing files" for whatever reasons they need. You just have to look past your own nose.&lt;/p&gt;
&lt;h2 id="ipfs_is_not_anonymous_or_dark_web"&gt;IPFS is not anonymous or "dark web"&lt;a class="header_anchor_link" href="#ipfs_is_not_anonymous_or_dark_web"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;Why do we need decentralised/censorship resistant communication platforms when they already exist on darknet?&lt;/p&gt;
&lt;p&gt;they probably aren't decentralised but they do the job&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Can my uploaded IPFS files be traced to get my IP address?&lt;/p&gt;
&lt;p&gt;I'd like to use IPFS to share my files with others on Discord, however I'm concerned someone might grab my IP. Is this possible, and if it is, how do I protect myself from being attacked?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;I love the idea of decentralized file storage, but how would IPFS prevent something like the distribution of child porn or hate speech?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What is stopping someone from creating a child porn IPFS website?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What are the ways in which ipfs prevents piracy?&lt;/p&gt;
&lt;p&gt;I was thinking that all the successful decentralized networks today are highly utilised for piracy which harms the reputation of the technology. So what are the ways in which ipfs prevents that and what are the disadvantages of those implementations?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What Prevents IPFS from becoming the next uTorrent?&lt;/p&gt;
&lt;p&gt;I believe this a problem that IPFS will face as in any peer to peer network, piracy is a big issue.&lt;/p&gt;
&lt;p&gt;I propose that we make a "hash blacklist" which will have a list of hashes which are illegal. Though we would have to build a proper system around it and make sure that not any or every hash is added to it, kinda like a dmca takedown.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;I want to join the IPFS, but I'm concerned about ending up with illegal content on my computer somehow. I tried to find information about this on the web but haven't been successful, so I thought I'd ask here.&lt;/p&gt;
&lt;p&gt;I don't want illegal movies, music, etc., but I'm especially concerned about illegal sexual content like child porn. Having even traces of that stuff on your system could be disastrous. So, if I get involved in the decentralized web, and particularly with IPFS, how do I protect myself from illegal content?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;What stops people from distributing illegal copies of different types of media?&lt;/p&gt;
&lt;p&gt;(1) If I were a bad person and started uploading pirated copies of movies or games, what stops someone from doing this?&lt;/p&gt;
&lt;p&gt;(2) If I have the legal rights to a movie and I found an illegal copy on IPFS for free. How could I prevent this from spreading; or stopping this illegal activity from happening? I guess this wouldn't be possible as the whole point is to spread data around so people can download it even if the original uploader is offline.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can find lots of posts from people who are worried about illegal or unsavory things happening through IPFS, and much of the messaging surrounding IPFS does describe it as "unstoppable" or "censorship-resistant" or "can't be taken down". There are a couple of angles we can get at here depending on your concern.&lt;/p&gt;
&lt;p&gt;Some of these people believe that IPFS is a place you upload to, or that all nodes assist with the hosting of all files, which leads them to believe that by running an IPFS node they will wind up with illegal files on their computer. There is precedence for p2p networks where all peers help host all files, like &lt;a href="https://en.wikipedia.org/wiki/GNUnet"&gt;GNUnet&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Freenet"&gt;Freenet&lt;/a&gt;, but this is not the case with IPFS. You only host what you visit and pin.&lt;/p&gt;
&lt;p&gt;On the other hand, some people might just feel generally uneasy knowing that child molesters can use new tools like IPFS to share their material, and they wish the system had some kind of innate block against that. But how do you expect the IPFS software to know whether a file is being blocked for a "good" reason, like preventing the spread of child pornography, versus a "bad" reason, like preventing the spread of Tiananmen Square photos? Both of these situations are basically the same: it's material that people are not allowed to look at. It just depends who's doing the allowing and disallowing in your jurisdiction. Should there be a human-curated ban list built into the default client? Curated by people from which country? The client is open source anyway, so criminals could compile the program without the blocklist. It just doesn't work. Using technical solutions to address social problems rarely does.&lt;/p&gt;
&lt;p&gt;You'll also notice that knives do not have special handles that prevent you from committing murder, beer bottles do not prevent you from driving drunk, cash does not prevent you from buying drugs, pens do not prevent you from forging signatures. The tool is agnostic and the crime is up to you.&lt;/p&gt;
&lt;p&gt;What stops people from distributing illegal files over IPFS? Nothing. IPFS is just a protocol. It is a language that two computers speak when they talk to each other. There is no sequence of words in the English language that will instantly alert the police when I say them, no matter how bad the words are. Likewise, there is no kind of file that will cause IPFS to alert the police.&lt;/p&gt;
&lt;p&gt;However, (here comes the point of this section), IPFS is not anonymous. When you run an IPFS node and people connect to you, they're connecting to your public IP address assigned to you by your internet service provider. If the police catch wind of any CIDs for illegal files, they'll just check out which IP addresses are sharing those files and try to pin down the owners of the IP addresses.&lt;/p&gt;
&lt;p&gt;People have begun to conflate decentralization with anonymity, which is not correct. As I described earlier, companies decentralize their CDN servers because it improves regional latency, but they are certainly not anonymous. Networks that aim for anonymity like &lt;a href="https://en.wikipedia.org/wiki/Tor_(network)"&gt;Tor&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/I2P"&gt;I2P&lt;/a&gt; do so by routing your connection through several intermediate nodes so that the sender and receiver never talk directly to each other.&lt;/p&gt;
&lt;p&gt;If a journalist in China tries to publicize the Tiananmen Square incident, the Chinese government might discover it and try to shut that person down. But, because a single &lt;code&gt;ipfs://&lt;/code&gt; link can work anywhere in the world, that journalist only needs a single friend outside of the country to also pin the file and make it available globally, then the journalist can turn off their node. Other countries like USA will not prosecute a person for hosting the photos. (Realistically, the journalist should transfer the file to their friend through a separate, encrypted channel, not IPFS, in the first place.)&lt;/p&gt;
&lt;p&gt;As with other technologies, like bittorrent or even just http, you could gain anonymity by running the program on a far away server which you rent or by diligently routing your PC traffic through a VPN. In both of those cases, you still need to make sure that the server owner or VPN service won't hand you over to the police and that your payment method doesn't lead back to your real name. The person who wants to download illegal files should prepare for the possibility of connecting to a honeypot source, and they probably should remove the file from their IPFS node afterward so it doesn't seed back to anyone else. This is already standard procedure.&lt;/p&gt;
&lt;p&gt;From what I've seen so far, the censorship-resistance claims of IPFS are predicated on the idea that you're first able to get your files out of the jurisdiction that wants to censor them and into a jurisdiction where they're permitted. IPFS does not provide any secrecy, and if the whole world is after you, IPFS isn't a magic shield. The fact of the matter is that the regular HTTP web already carries hate speech, copyrighted files, and illegal material, but new protocols are subjected to higher scrutiny and criticism than the incumbents as a matter of course.&lt;/p&gt;
&lt;p&gt;Now, one of those comments I quoted made a great point, which is that the reputation of the technology as a whole can be ruined if it's known for piracy or criminal material. Bittorrent is known for little other than piracy — when people give a demonstration of how to use a bittorrent client, they always show how to download a Linux release or Big Buck Bunny, because that's the only thing they can think of that isn't a copyright violation. Many people think that torrents are inherently illegal. So far, IPFS hasn't become a popular piracy tool to my knowledge, and all of their messaging centers around webpages.&lt;/p&gt;
&lt;h2 id="ipfss_copywriting_is_bad"&gt;IPFS's copywriting is bad&lt;a class="header_anchor_link" href="#ipfss_copywriting_is_bad"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Am I doing a good job so far? Let's compare what I've written with what appears on the IPFS homepage to see how poorly they communicate their project to newcomers. All of the statements they make are technically correct, but they only make sense if you already know how IPFS works. If you don't, it's problematic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: since this article was originally published, some phrasing on their homepage has changed. &lt;a href="/writing/ipfs_misconceptions/ipfs_homepage_20210613.html"&gt;This&lt;/a&gt; is the revision against which I wrote these criticisms. I will keep this article as-is until I take the time to review the new claims.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Today's web can't preserve humanity's history&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The average lifespan of a web page is 100 days before it's gone forever. It's not good enough for the primary medium of our era to be this fragile. IPFS keeps every version of your files and makes it simple to set up resilient networks for mirroring data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To the layperson, this implies that 'the network' stores your files forever once you put them in. It does absolutely nothing to address the mindset that most internet users have, which is that service providers and service users are clearly delineated.&lt;/p&gt;
&lt;p&gt;Have you heard the phrase "guns don't kill people, people kill people"? Well, the network doesn't store anything, it's the people that store things, and IPFS is the network by which you can reach them. But people can get bored of holding on to a particular file and delete it to make space for something else.&lt;/p&gt;
&lt;p&gt;Are you still holding on to copies of all the webpages you read 100 days ago? No? Why not? Don't you want to contribute to the permanence of all those pages? I clutch my pearls in your general direction. See, it's easy to sit around fawning over IPFS for being 'the permanent web' until you realize that permanence isn't actually a guarantee of the protocol. Permanence is a &lt;em&gt;possibility&lt;/em&gt;, only to be realized by the collective choice of individuals to host the material they believe is important. And unless you're doing your part to preserve and host the files you think are important, you don't have much ground to stand on when you find that other people aren't keeping the stuff you want either.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Archivists&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;IPFS provides deduplication, high performance, and clustered persistence — empowering you to store the world's information for future generations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Similar to the previous one, the word "provides" here gives the wrong impression to people who approach this page with the mindset that IPFS is a web storage service like DropBox. I'm not saying that's the only possible interpretation, but it's a very common one and IPFS should write more clearly to disambiguate.&lt;/p&gt;
&lt;p&gt;"IPFS provides me with high performance storage? Sure, I'll take it! What? No, I don't want to actually &lt;em&gt;run&lt;/em&gt; anything..."&lt;/p&gt;
&lt;p&gt;I will give them credit for "empowering you to store", giving agency to the reader. Just make sure you use the power once you've got it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Blockchains&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With IPFS, you can address large amounts of data and put immutable, permanent links in transactions — timestamping and securing content without having to put the data itself on-chain.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This doesn't mention the fact that the link is only good as long as someone keeps the file. When everyone assumes "oh, the network's got it, I can delete mine", you'll suddenly find there's no copies left. By omission, this implies that IPFS just holds things forever.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Here's how IPFS works&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Take a look at what happens when you add a file to IPFS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wording here could be changed to "when you add a file to your IPFS node" to remind that you're processing your own files; this isn't happening remotely on some kind of IPFS service.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Your file, and all of the blocks within it, is given a unique fingerprint called a cryptographic hash.&lt;/p&gt;
&lt;p&gt;IPFS removes duplications across the network.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This statement isn't specific enough. I get it, because two identical files will produce matching hashes, so you don't get "duplicate" links. But removing duplications sounds like the opposite of what the reader wants — high availability and low latency are achieved only after many duplicates of the file are shared by many peers around the world. By contrast, it is the centralized file hosts like Google Drive who benefit most from file deduplication: if two people upload the same file to GD, Google can just store it once and let both people download it, saving themselves disk space. IPFS's statement almost lends itself to the interpretation that files will be removed from your node if they already exist on someone else's, which isn't the case.&lt;/p&gt;
&lt;p&gt;Furthermore, this statement uses third-person voice for both IPFS and "the network", which is a double-whammy against the agency of the reader. It sounds like a remote service.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Each network node stores only content it is interested in, plus some indexing information that helps figure out which node is storing what.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Could be reworded to "You only store content you're interested in" to remind that IPFS is not a remote service and give agency to the reader.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;When you look up a file to view or download, you're asking the network to find the nodes that are storing the content behind that file's hash.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is weird. "Asking the network to find the nodes"? That's like asking the wave to find the water. What is the network if not nodes? And the sentence structure reminds me of the dog that killed the cat that caught the mouse that ate the cheese.&lt;/p&gt;
&lt;p&gt;Should be reworded to "you're asking your peers to find who has a copy of that file".&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Content creators&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;IPFS brings the freedom and independent spirit of the web in full force — and can help you deliver your content at a much lower cost.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The IPFS copywriters should be aware that approximately 100% of today's content creators are accustomed to uploading their videos, music, photos, and blog posts to online ad-supported services without having to pay to do so. You probably should let them know that hosting their creations on IPFS requires them to run a node or pay a pinning service, and that the decentralization only happens after they have fans who are willing to store and pin their works.&lt;/p&gt;
&lt;p&gt;IPFS's homepage is bad. I wonder if they are intentionally toeing the line with their messaging, since the implications are always more grandiose and fabulous than the reality. I've said it once and I'll say it again: sqlite.org has a front-page link to &lt;a href="https://sqlite.org/whentouse.html"&gt;When to use SQLite&lt;/a&gt; which also plainly describes when &lt;strong&gt;not&lt;/strong&gt; to use it. IPFS doesn't do this because they're &lt;a href="/writing/sqlite_what_a_hunk"&gt;not as chad as SQLite&lt;/a&gt;. Can't fault them for that. No one is.&lt;/p&gt;
&lt;p&gt;Clearly, IPFS's messaging, and the messaging from crypto nuts who advocate IPFS without really knowing what it is either, has led people to believe that IPFS is a giant decentralized cloud storage system that may or may not be free to use. In my opinion, the copywriters need to turn this ship around and make it clear that IPFS is not a magic dumping ground for your random personal junk to be replicated globally. It's a peer-to-peer network that you need to actually participate in if you ever want to get anything done.&lt;/p&gt;
&lt;p&gt;Welcome to the internet.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/ipfs_misconceptions/ipfs_misconceptions.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/16a4bfafc6fc486b5a257177a847a677b5e53835"&gt;&lt;time datetime="2022-01-29T14:30:59-08:00"&gt;2022-01-29&lt;/time&gt; Improve IPFS misconceptions.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/c1bc7ead18841bfc66c9d5421524edab9b6c7a6c"&gt;&lt;time datetime="2021-12-07T23:11:55-08:00"&gt;2021-12-07&lt;/time&gt; Add a comma between these clauses.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/a01011230d1387b1ec60bf707c34e4fd592405e0"&gt;&lt;time datetime="2021-10-03T00:11:35-07:00"&gt;2021-10-03&lt;/time&gt; Reduce arrogance.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/fc809226b3f6a3399be70575fcd6a7d027e38dc1"&gt;&lt;time datetime="2021-06-14T16:07:47-07:00"&gt;2021-06-14&lt;/time&gt; Add ipfs_misconceptions.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Tue, 15 Jun 2021 02:07:47 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/ipfs_misconceptions</guid></item><item><title>How to make an RSS feed in SvelteKit</title><link>https://www.davidwparker.com/posts/how-to-make-an-rss-feed-in-sveltekit</link><description>RSS feeds help make consuming any website a million times better. In this post, I show how to make an RSS feed in SvelteKit with a simple endpoint.</description><author>David W Parker</author><pubDate>Tue, 15 Jun 2021 00:31:57 GMT</pubDate><guid isPermaLink="true">https://www.davidwparker.com/posts/how-to-make-an-rss-feed-in-sveltekit</guid></item><item><title>What Should You Do with Your Life? Directions and Advice</title><link>https://heidenstedt.org/links/what-should-you-do-with-your-life-daa/</link><description>&lt;p&gt;
      &lt;em&gt;Best viewed on the &lt;a href="https://heidenstedt.org/links/what-should-you-do-with-your-life-daa/"&gt;original page&lt;/a&gt;, where extended functionality like the
    footnote helper is available.&lt;/em&gt;
    &lt;/p&gt;&lt;p&gt;&lt;a href="https://guzey.com/personal/what-should-you-do-with-your-life/"&gt;What Should You Do with Your Life? Directions and Advice&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;TLDR:&lt;/p&gt;
&lt;p&gt;The article &amp;ldquo;What Should You Do with Your Life? Directions and Advice&amp;rdquo; by Alexey Guzey provides guidance for people who are unsure about their life plans. Here&amp;rsquo;s a summary:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What to work on&lt;/strong&gt;: Guzey suggests several resources for finding interesting problems to work on, including Y Combinator’s Requests for Startups, OpenAI’s Requests for Research, and various lists of research ideas and project ideas.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to work on the problem you like&lt;/strong&gt;: Guzey advises writing down a shortlist of exciting problems, selecting the most exciting and tractable ones, and then learning everything you need to start working on the problem. This could involve taking MOOCs, auditing classes at a local university, or finding a tutor. Once you&amp;rsquo;ve gained sufficient knowledge, start working on a project related to the problem, produce a minimal public impressive result, and share it with people working in your area.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cold emails and Twitter&lt;/strong&gt;: Guzey emphasizes the effectiveness of cold emails and Twitter for connecting with people who can help you. He shares examples of his own successful cold emails and provides tips for writing them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Finding funding&lt;/strong&gt;: For projects that require funding, Guzey lists several resources, including Nadia Eghbal’s list of microgrants, the Thiel Fellowship, and Emergent Ventures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;General advice&lt;/strong&gt;: Guzey shares advice from various sources, such as Paul Graham&amp;rsquo;s essay &amp;ldquo;How to Do What You Love&amp;rdquo;, Patrick Collison&amp;rsquo;s advice for ages 10-20, and Patrick McKenzie&amp;rsquo;s &amp;ldquo;Don’t End The Week With Nothing&amp;rdquo;. The common theme is to be proactive, produce tangible work, and take advantage of opportunities.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;More projects to work on&lt;/strong&gt;: Guzey ends the article by suggesting more projects to work on, like Jacob Trefethen’s Vigilante accountability project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description><author>Mia Heidenstedt</author><pubDate>Mon, 14 Jun 2021 21:41:31 GMT</pubDate><guid isPermaLink="true">https://heidenstedt.org/links/what-should-you-do-with-your-life-daa/</guid></item><item><title>How the French Think: An Affectionate Portrait of an Intellectual People</title><link>https://macleodsawyer.com/books/how-the-french-think/</link><description>&lt;blockquote&gt;
&lt;p&gt;In How the French Think, the internationally-renowned historian Sudhir Hazareesingh tells the epic and tumultuous story of French intellectual thought from Descartes, Rousseau, and Auguste Comte to Sartre, Claude LÃ©-Strauss, and Derrida. He shows how French thinking has shaped fundamental Westerns ideas about freedom, rationality, and justice, and how the French mind-set is intimately connected to their own way of life-in particular to the French tendency towards individualism, their passion for nature, their celebration of their historical heritage, and their fascination with death. Hazareesingh explores the French veneration of dissent and skepticism, from Voltaire to the Dreyfus Affair and beyond; the obsession with the protection of French language and culture; the rhetorical flair embodied by the philosophes, which today’s intellectuals still try to recapture; the astonishing influence of French postmodern thinkers, including Foucault and Barthes, on postwar American education and life, and also the growing French anxiety about a globalized world order under American hegemony.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While I enjoyed the book (up until the conclusion), this was an excellent primer on the celebrity philosophers and their direct (or indirect) effects on the french social scene and overall social truths. If this book had been titled &lt;em&gt;anything&lt;/em&gt; else, then this would be an excellent book - but as the core concept on ‘how the french think’ it runs a bit flat. This is contemporary view of how the social elite thought throughout history but touched very little on the every day citizens or world that directly affected them.&lt;/p&gt;
&lt;p&gt;This is a history of French philosphy, but any attempt to strive off this path does not fare well and will cause the reader to feel less emotionally involved as one would want to be. The writing is exceptionally well done, but until the conclusion you would feel hopeful. The author uses the last chapter to usher and pigeon hole a concept that ‘society is failing’ and that everything is worse than it ever has been without giving any specific reasons. He does try to find a tone in a paragraph where he is optimistic but the chapter itself betrays this pitiful attempt. The author seems to live in a world-view that ‘philosophy is dead, long live the old!’, if you ignore the last two chapters and view this as a history of the French bourgeoisie then this is a book that I would recommend, otherise you should temper your expectations.&lt;/p&gt;</description><author>Macleod Sawyer | Web Portal Feed</author><pubDate>Mon, 14 Jun 2021 15:06:36 GMT</pubDate><guid isPermaLink="true">https://macleodsawyer.com/books/how-the-french-think/</guid></item><item><title>Linus Torvalds addresses an anti-vaxxer</title><link>https://nicolaiarocci.com/linus-torvalds-addresses-an-anti-vaxxer/</link><description>&lt;p&gt;Linus Torvalds&amp;rsquo; &lt;a href="https://lore.kernel.org/ksummit/CAHk-=wiB6FJknDC5PMfpkg4gZrbSuC3d391VyReM4Wb0+JYXXA@mail.gmail.com/"&gt;reply to an anti-vaxxer&lt;/a&gt; on the Linux kernel list is
a must-read. Pre-2018, Linus would have destroyed the poor chump. He&amp;rsquo;s
discouraging further discussion (Kernel list is not the place for that) while
providing crystal clear and detailed mRNA vaccine information, all without
renouncing to an opening salvo of his good-ole, grumpy style.&lt;/p&gt;
&lt;p&gt;As John Gruber &lt;a href="https://daringfireball.net/linked/2021/06/10/linus-torvalds"&gt;affirms&lt;/a&gt;, this is one rant we can all get behind.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Mon, 14 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/linus-torvalds-addresses-an-anti-vaxxer/</guid></item><item><title>Simple Notes app using React, TypeScript, Node.js, GraphQL and MongoDB</title><link>https://www.rasikjain.com/posts/simple-notes-app-using-react-typescript-nodejs-graphql-mongodb/</link><description>In the series of the blog post, I am going to walk through step by step process of building a Simple Notes App using React, TypeScript, Node.js, GraphQL , and MongoDB. During the process of our development, we are going to use some useful npm packages like Express, Apollo-Server, Typegoose, Mongoose, TypeGraphQL , and Bootstrap.
Here is the Github Source Code to run the application.
Table of Content
 Dev Environment Setup GraphQL Server Backend Folder Structure Creating Node.</description><author>Rasik Jain - Senior Architect | Generative AI &amp;amp; Full-Stack Engineering</author><pubDate>Mon, 14 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.rasikjain.com/posts/simple-notes-app-using-react-typescript-nodejs-graphql-mongodb/</guid></item><item><title>2021–06–14: Hints on debugging HDMI output on Pinephone</title><link>https://xnux.eu/log/#039</link><author>megi's PinePhone Development Log</author><pubDate>Mon, 14 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#039</guid></item><item><title>2021–06–14: First pinephone keyboard typings with my FOSS firmware</title><link>https://xnux.eu/log/#038</link><author>megi's PinePhone Development Log</author><pubDate>Mon, 14 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#038</guid></item><item><title>2021–06–14: Pinephone keyboard firmware</title><link>https://xnux.eu/log/#037</link><author>megi's PinePhone Development Log</author><pubDate>Mon, 14 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#037</guid></item><item><title>Why IaaS beat PaaS</title><link>https://www.swyx.io/why-iaas-beat-paas</link><description>The conclusions are far more nuanced when you ask the people involved at the time.</description><author>swyx's site RSS Feed</author><pubDate>Sun, 13 Jun 2021 21:34:19 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/why-iaas-beat-paas</guid></item><item><title>Vito's Guide to Tandon CS "Placement"</title><link>https://blog.vito.nyc/posts/tandon-cs-guide/</link><description>&lt;p&gt;Tandon&amp;rsquo;s CS program, unlike CAS and the majority of other schools with CS programs, does not have any sort of CS placement. Officially, the only course an incoming CS/CE/ECE major can skip is the very first Intro course with an AP CS exam score of 4 or 5. Unofficially, you can skip all of the requirements of any of the majors so long as you get enough credits in the required course work by graduation. This guide is intended to be helpful to underclassmen attempting to take an unorthodox approach to these majors.&lt;/p&gt;</description><author>vito.nyc</author><pubDate>Sun, 13 Jun 2021 17:47:32 GMT</pubDate><guid isPermaLink="true">https://blog.vito.nyc/posts/tandon-cs-guide/</guid></item><item><title>Glue: the Dark Matter of Software</title><link>https://blog.metaobject.com/2021/06/glue-dark-matter-of-software.html</link><description>"Software seems 'large' and 'complicated' for what it does".  I keep coming back to this &lt;a href="https://youtu.be/ubaX1Smg6pY?t=113"&gt;quote&lt;/a&gt; by Alan Kay.&lt;p&gt;


The same feeling has been nagging me pretty me much ever since I started writing software.  On the one hand, there is the magic, almost literally:
we write some text (spells) and the machine does things in the real world.  On the other hand, it seems just way too much work to make the machine 
do anything more complex than:
&lt;br /&gt;&lt;blockquote&gt; &lt;code&gt;10 PRINT "Hello"&lt;br /&gt;20 GOTO 10&lt;/code&gt;&lt;/blockquote&gt;
Almost like threading a needle with boxing gloves.  And that's even if we are careful, if we avoid unnecessary complexity.&lt;p&gt;

And the numbers appear to back that up, Alan Kay mentions Microsoft office at several hundred million lines of code.  From my personal experience,
the Wunderlist iOS client was not quite 200 KLOC.  For the latter, I can attest to the attention given by the team to &lt;em&gt;not&lt;/em&gt; introduce
unnecessary bloat, and even to actively reduce it.  (For example, we cut our core code by around 30KLOC thanks to some of the architectural
mechanisms such as &lt;a href="https://2019.splashcon.org/details/splash-2019-Onward-papers/7/Storage-Combinators"&gt;Storage Combinators&lt;/a&gt;).
I am fairly sure I am not the only one with this experience.&lt;p&gt;

&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;hexagonal architecture has enabled me to extract the business logic in the product i’m building and currently it’s less than 5% of all code&lt;/p&gt;&amp;mdash; 3. life out of balance (@infinitary) &lt;a href="https://twitter.com/infinitary/status/934321320338313216?ref_src=twsrc%5Etfw"&gt;November 25, 2017&lt;/a&gt;&lt;/blockquote&gt; 

So why so much code?  After all Wunderlist was just a To Do List, albeit a really nice one.  I can't really say much about Office, I don't
think anyone can, because 400 MLOC is just way too much code to comprehend.   I think the answer is:&lt;p&gt;

Glue Code.&lt;p&gt;

It's the unglamorous, invisible code that connects two pieces of software, makes sure that data that's in location A reaches location B
unscathed (from the datbase to the UI, from the UI to the model, from the model to the backend and so on...).  And like Dark Matter, it
is invisible and &lt;em&gt;massive&lt;/em&gt;.&lt;p&gt;

Why do I say it is "invisible"?  After all, the code is right there, isn't it?  As far as I can tell, there are several related reasons:

&lt;ol&gt;
&lt;li&gt;Glue code is deemed not important.  It's just a couple of lines here, and another couple of lines over there ... and soon enough you're talking 
real MLOCs!&lt;/li&gt;
&lt;li&gt;We cannot directly express glue code.  Most of our languages are what I call "DSLs for Algorithms" (See &lt;a href="https://en.wikipedia.org/wiki/ALGOL"&gt;ALGOL, the ALGOrithmic Language&lt;/a&gt;), so glue can not be expressed intentionally, but only by describing algorithms for implementing the glue.&lt;/li&gt;
&lt;/ol&gt;

That's why it is invisible, and also partly why it is massive:  not being able to express it directly means we cannot abstract and encapsulate it, we 
keep repeating slight variations of that glue.  There is another reason why it's massive:

&lt;ol start="3"&gt;
&lt;li&gt;Glue is quadratic.  If you have N features that interact with each other, you have O(N&amp;sup2;) pieces of glue to get them to talk to each other.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;

This last point was illustrated quite nicely by Kevin Greer in a video comparing Multics and Unix development, with the crucial insight
being that you need to "program the perimeter, not the area":&lt;p&gt;


&lt;p&gt;

For him, the key difference is that Unix had the pipe, and I would agree.  The pipe is one-character glue:  "|".  This is absolutely crucial.&lt;p&gt;

If you have to write even a little custom code every time you connect two modules, you will be in quadratic complexity, meaning that
as your features grow your glue code &lt;em&gt;will&lt;/em&gt; overwhelm the core functionality.  And you will only notice this when it's 
far too late to do anything about it, because the initial growth rate will be low.&lt;p&gt;

So what can we do about it?  I think we need to make glue first class so we can actually write down the glue itself, and not the algorithms that implement
the glue.  Once we have that, we can and hopefully will create better kinds of glue, ones like the Unix pipe in that they can connect components generically, 
without requiring custom glue per component pair.&lt;p&gt;

UPDATE&lt;p&gt;

There were some questions as to what to do about this.  Well, I am working on it, with &lt;a href="http://objective.st"&gt;Objective-S&lt;/a&gt;, and I write
fairly frequently on this blog (and occasionally submit my &lt;a href="http://objective.st/Publications/"&gt;writing&lt;/a&gt; to scientific conferences), one post that would be immediately relevant is:  &lt;a href="https://blog.metaobject.com/2019/02/why-architecture-oriented-programming.html"&gt;Why Architecture Oriented Programming Matters&lt;/a&gt;.&lt;p&gt;

I also don't see Unix Pipes and Filters as &lt;em&gt;The Answer&lt;/em&gt;™, they just demonstrate the concept of minimized
and constant glue.  Expanding on this, and as I wrote in &lt;a href="https://blog.metaobject.com/2019/02/why-architecture-oriented-programming.html"&gt;Why Architecture Oriented Programming Matters&lt;/a&gt;, I also don't see any one single connector
as "the" solution.  We need different kinds of connectors, and we need to write them down, to abstract over them and
use them natively.  Not simulate everything by calling procedures, methods or functions. See also &lt;a href="https://longnow.org/seminars/02007/jan/26/why-foxes-are-better-forecasters-than-hedgehogs/"&gt;Foxes vs. Hedgehogs&lt;/a&gt;.&lt;p&gt;</description><author>metablog</author><pubDate>Sun, 13 Jun 2021 13:46:26 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/06/glue-dark-matter-of-software.html</guid></item><item><title>[Computing] I have now been banned from Freenode</title><link>https://www.devever.net/~hl/freenode_kline</link><description>&lt;p&gt;Timeline:&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Sun, 13 Jun 2021 05:36:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/freenode_kline</guid></item><item><title>This Too Shall Pass</title><link>https://lambdaland.org/posts/2021-06-13_this_shall_pass/</link><description>&lt;p&gt;It’s cliché at this point to say that 2020 was a rough year. I’m grateful in that I and my wife were relatively unscathed by the pandemic. I had some personal health issues however that by themselves made 2020 a bit of a struggle. I learned some important lessons.&lt;/p&gt;</description><author>Lambda Land</author><pubDate>Sun, 13 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://lambdaland.org/posts/2021-06-13_this_shall_pass/</guid></item><item><title>[Computing] Why the GDPR is a threat to a free internet and should be ignored by those outside the EU</title><link>https://www.devever.net/~hl/ignoregdpr</link><description>&lt;p&gt;Much has already been said about the EU's General Data Protection Regulation
(GDPR). One of the commonly noted aspects of the GDPR is that it attempts to
exert extraterritorial jurisdiction on any entity processing the data of an EEA
resident, even if the entity itself has no assets in the EEA or other relation
to it.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Sun, 13 Jun 2021 00:35:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/ignoregdpr</guid></item><item><title>Google FLoc is just another bag of cookies</title><link>https://stop.zona-m.net/2021/06/google-floc-is-just-another-bag-of-cookies/</link><description>&lt;p&gt;Toldya&amp;hellip;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 12 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/google-floc-is-just-another-bag-of-cookies/</guid></item><item><title>Nature</title><link>https://one.mikro2nd.net/pages/nature/</link><description>We talk of &amp;ldquo;Nature&amp;rdquo; as though it exists, but it doesn&amp;rsquo;t. It&amp;rsquo;s a fiction brought into being by human conversation, a figment of the human collective consciousness. There is no &amp;ldquo;Nature&amp;rdquo;, only the totality, the cosmos. What happens on Ganymede or on Mercury or on some anonymous and unidentified lump of ice out in the Oort Cloud or on a White Dwarf in some distant galaxy is every bit as natural as what happens in the Amazon Jungle or as Schipol Airport.</description><author>one mikro2nd</author><pubDate>Sat, 12 Jun 2021 16:24:18 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/nature/</guid></item><item><title>Dynamic website vs. static website - pros, cons and definition</title><link>https://kaszkowiak.org/en/blog/dynamic-vs-static-website/</link><description>Learn the differences between the two types of sites: dynamic and static. A practical list of the most important pros and cons.</description><author>Blog on Maciej Kaszkowiak</author><pubDate>Sat, 12 Jun 2021 14:33:06 GMT</pubDate><guid isPermaLink="true">https://kaszkowiak.org/en/blog/dynamic-vs-static-website/</guid></item><item><title>Bo Burnham: Inside</title><link>https://olshansky.info/movie/bo_burnham_inside/</link><description>Olshansky's review of Bo Burnham: Inside</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 12 Jun 2021 11:08:36 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/bo_burnham_inside/</guid></item><item><title>The Master and Margarita</title><link>https://apurva-shukla.me/bookshelf/the-master-and-margarita/</link><description>⭐ ⭐ ⭐ ⭐ Bulgakov’s The Master and Margarita is a wonderful story weaving together the oppresive, homogenous era under the early years of…</description><author>Apurva Shukla's RSS Feed</author><pubDate>Sat, 12 Jun 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://apurva-shukla.me/bookshelf/the-master-and-margarita/</guid></item><item><title>Bringing Colour and Life to Black and White Photos</title><link>https://blog.skz.dev/bringing-colour-and-life-to-black-and-white-photos</link><description>&lt;p class="description"&gt;
    Machine learning allows us to colourise black and white photos with ease, but at what risk?
&lt;/p&gt;

&lt;h2 id="a-different-universe"&gt;A Different Universe&lt;/h2&gt;

&lt;p&gt;Although colourised photos have technically existed for a century or more (&lt;a href="https://www.loc.gov/pictures/related/?fi=name&amp;amp;q=Prokudin-Gorski%20%2C%20Serge%20%20Mikha%20lovich%2C%201863-1944"&gt;Sergey Prokudin-Gorsky and Piotr Vedenisov, pioneers of colour photography, traveled around Imperial Russia documenting it in full colour&lt;/a&gt;), the majority of the 20th century was documented in black and white. Even later in the century, much of the world did not have the luxury of colour film- my father was developing his own black and white photos into the 1980s-90s in Soviet Ukraine, and while Americans recorded full colour footage of the Vietnam War, the North’s perspective seems to be relegated to black and white photography.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Young boy near the Sim River" src="/assets/img/colourising-black-and-white-photos/boy-sim-river.jpeg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Young boy near the Sim River, a very early colour photograph from Imperial Russia. This was achieved by taking multiple pictures of the same scene with different colour filters. Courtesy of the Library of Congress.
&lt;/p&gt;

&lt;p&gt;Some will argue that black and white footage has its own special quality, and this may be true. But for me black and white footage disconnects me from the scene. It is clearly distant in time. We all associate black and white footage with the past, as the trope of black and white footage in cinematagrophy to refer to a memory shows. And as someone who experiences reality in full colour, black and white footage is difficult to experience. I struggle to put myself into the scene or the characters’ shoes, because in my mind it exists in a different, black and white universe. And yet when that footage is colourized, all of sudden everything appears more real, more believable, more tangible. It is then clear that whatever is depicted in the footage is of the same Earth and of the same people, people like you or me.&lt;/p&gt;

&lt;p&gt;And so when my father recently began digitizing our collection of family photos - as physical photos deteriorate, or are otherwise at risk of being lost forever, along with the histories, memories, and legacies preserved by them - I felt an urge to colourise them, too - so that I could experience them to a fuller extent, and so that my family members could reminisce about their loved ones who are no longer around or remember the memories of their past and their youth.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="colorizing-photos-with-ai"&gt;Colorizing Photos With AI&lt;/h2&gt;

&lt;p&gt;Color restoration can be done manually and laboriously. Machine learned models offer a quick and inexpensive way to colourize photos, but they have their limitations, as we will discuss later in this post. I did not train my own model for this purpose. Others have already invested thousands of hours into developing and training models to do exactly that. I found an existing model that does a very good job.&lt;/p&gt;

&lt;p&gt;I used the &lt;a href="https://deepai.org/machine-learning-model/colorizer"&gt;Image Colorization API from DeepAi&lt;/a&gt;. The production model deployed by DeepAi appears to be a form of the DeOldify model, which can be found at &lt;a href="https://github.com/jantic/DeOldify"&gt;this github repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don’t think the DeepAi API allows you to adjust the model parameters, but you can if you run it yourself from the repo above or &lt;a href="https://colab.research.google.com/github/jantic/DeOldify/blob/master/ImageColorizerColabStable.ipynb"&gt;a colab instance&lt;/a&gt;. &lt;a href="https://www.myheritage.com/incolor"&gt;MyHeritage&lt;/a&gt; also offers this API as a service with tunable parameters.&lt;/p&gt;

&lt;p&gt;What I did was create a quick script that enables bulk colorization of directories. It iterates over all the photos in a directory, sends them off to Image Colorization API from DeepAi, and downloads the result into a separate folder (ignoring images that have already been colourised). If that would be useful to you, just set your own DeepAi API key as an environment variable and run &lt;a href="https://github.com/skzv/colorize-photos"&gt;this script&lt;/a&gt;:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DL_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-deep-ai-key&amp;gt;
python3 main.py /path/to/images
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="my-family-photos"&gt;My Family Photos&lt;/h2&gt;
&lt;p&gt;Here are some examples of the results on our family photos.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Mama with Vova" src="/assets/img/colourising-black-and-white-photos/mama-and-vova.jpg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
My mother holding my brother in early '90s Kherson, Ukraine. Courtesy of the Kuznetsov family.
&lt;/p&gt;

&lt;p&gt;&lt;img alt="Mama with Vova" src="/assets/img/colourising-black-and-white-photos/mama-and-vova-colorized.jpg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Now in colour.
&lt;/p&gt;

&lt;p&gt;&lt;img alt="Babushka Allarkadina" src="/assets/img/colourising-black-and-white-photos/my-grandmother-allarkadina.jpg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
My grandmother, Allarkadina, likely sometime in the '50s, on her way to university. She graduated from multiple institutes, specializing in electrical engineering, and had a successful career as a marine electrical engineer.
&lt;/p&gt;

&lt;p&gt;&lt;img alt="Babushka Allarkadina" src="/assets/img/colourising-black-and-white-photos/my-grandmother-alla-colourised.jpeg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
In colour.
&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Looks so nice and alive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those words from my father upon seeing the newly coloured photos made it all worth it.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="photos-from-the-vietnam-war"&gt;Photos From the Vietnam War&lt;/h2&gt;
&lt;p&gt;A couple weeks after preparing a bulk colourising script for my family photos, a post titled &lt;a href="https://news.ycombinator.com/item?id=27283624"&gt;“The Vietnam War from the North Vietnamese Side”&lt;/a&gt; made it to the top of Hacker News. It seemed like a perfect opportunity to apply the script, and offer a new perspective on historical footage. Just as &lt;a href="https://www.imdb.com/title/tt7905466/"&gt;“They Shall Not Grow Old”&lt;/a&gt; made the horrors of WW1 living and real, so did I aim to make the characters and scenes from the perspective of North Vietnam.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Mama with Vova" src="/assets/img/colourising-black-and-white-photos/vietnam-agent-orange.jpeg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Destruction of the jungle wrought by Agent Orange.
&lt;/p&gt;

&lt;p&gt;&lt;img alt="Mama with Vova" src="/assets/img/colourising-black-and-white-photos/vietnam-agent-orange-colourised.jpg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
In colour.
&lt;/p&gt;

&lt;p&gt;&lt;img alt="Mama with Vova" src="/assets/img/colourising-black-and-white-photos/vietnam-downed-aircraft.jpeg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
Locals inspecting a downed American aircraft piloted by Lt. Stephen Owen Musselman. The pilot did not survive- war is hell.
&lt;/p&gt;

&lt;p&gt;&lt;img alt="Mama with Vova" src="/assets/img/colourising-black-and-white-photos/vietnam-downed-aircraft-colourised.jpg" /&gt;&lt;/p&gt;

&lt;p class="caption"&gt;
In colour.
&lt;/p&gt;

&lt;p&gt;All the original black and white images can be found &lt;a href="https://rarehistoricalphotos.com/vietnam-war-images-from-vietnamese-photographers/"&gt;here&lt;/a&gt;, with the colourised versions &lt;a href="https://imgur.com/a/aJbpMjf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="response-on-hacker-news"&gt;Response on Hacker News&lt;/h2&gt;

&lt;p&gt;My efforts were met with both praise&lt;/p&gt;

&lt;p&gt;&lt;img alt="Praise" src="/assets/img/colourising-black-and-white-photos/praise.png" /&gt;&lt;/p&gt;

&lt;p&gt;and contempt&lt;/p&gt;

&lt;p&gt;&lt;img alt="Contempt" src="/assets/img/colourising-black-and-white-photos/contempt.png" /&gt;&lt;/p&gt;
&lt;p class="caption"&gt;
Too bad about the [flagged] comment, because it raised an interesting point. The comment read something like 'Please don't do this.'
&lt;/p&gt;

&lt;p&gt;but most importantly, they generated a lot of very interesting discussions.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Discussion" src="/assets/img/colourising-black-and-white-photos/discussion.png" /&gt;&lt;/p&gt;

&lt;p&gt;The full thread spurred by the colourised photos can be found &lt;a href="https://news.ycombinator.com/item?id=27284795"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="limitations"&gt;Limitations&lt;/h2&gt;

&lt;p&gt;There were two aspects of the discussion that stood out to me. First, was the reminder of the limitations of AI.&lt;/p&gt;

&lt;p&gt;For example, the model invoked on an old image of the Golden Gate under construction produces a white bridge. It’s not wholly inaccurate, to be fair- the bridge was indeed white early in its construction, but by the time the picture was taken, the bridge had already been covered in red primer (according to the source at the &lt;a href="https://github.com/jantic/DeOldify"&gt;DeOldify repository&lt;/a&gt;). So while the training data probably did contain the Golden Gate under construction, and the model could identify the same bridge under construction here, it could not discern the date and know that the bridge had already been primed with red at this point.&lt;/p&gt;

&lt;p&gt;&lt;img alt="A White Golden Gate?" src="/assets/img/colourising-black-and-white-photos/golden-gate.jpeg" /&gt;&lt;/p&gt;
&lt;p class="caption"&gt;
A white Golden Gate? It was white- but not when this picture was taken.
&lt;/p&gt;

&lt;p&gt;Some scenes are ambiguous. For example, based on the features in the following, the model cannot differentiate between the ground being field or asphalt, and incorrectly colours the asphalt green, where a human colouriser may get it right.&lt;/p&gt;

&lt;p&gt;&lt;img alt="A celebration" src="/assets/img/colourising-black-and-white-photos/a-celebration-colourised.jpg" /&gt;&lt;/p&gt;
&lt;p class="caption"&gt;
The model can't tell that the ground is asphalt and not grass.
&lt;/p&gt;

&lt;p&gt;Another interesting observation was that clothes seemed to often lack distinctive hues, textures, features, or context that made them accurately colourisable. The model tended to colour clothes purple - unless they had distinctive features or context. This is probably because when the model cannot make a good inference about the colour of the clothes, purple is a conservative bet, somewhere in the middle of the ambiguous colour space, that minimizes the training loss.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Vietnamese divers - all purple?" src="/assets/img/colourising-black-and-white-photos/vietnam-divers-colourised.jpg" /&gt;&lt;/p&gt;
&lt;p class="caption"&gt;
I don't think clothes in the past were as often as purple as this model makes it out to be.
&lt;/p&gt;

&lt;p&gt;Indeed, my father requested to correct the colour of some of the articles in the photos. Unfortunately I did not pursue this request, nor is it immediately obvious to me how I could. But as one commenter remarked, even when the colours are wrong, they still bring the images to life.&lt;/p&gt;

&lt;div class="divider"&gt;&lt;/div&gt;

&lt;h2 id="ethical-considerations"&gt;Ethical Considerations&lt;/h2&gt;

&lt;p&gt;Now that some of the limitations are clear, we are in a good position to consider the ethical ramifications of a colourising AI. This is was the second aspect of discussion that stood out to me. Ethics did not occur to me when I undertook the project, but some of the feedback I received spurred me to reflect, and for that I am thankful.&lt;/p&gt;

&lt;p&gt;The main ethical consideration is truth. Similar to the controversy around deepfakes confusing us to what is true, artificial coloration can alter our experience of history. The AI makes choices about the colours of the past, which are not necessarily accurate. For example, if we keep and disseminate the white version of the Golden Gate, produced by such an AI, then the version of the bridge that is experienced by viewers is not historically accurate. Of course, neither is the black and white version. Perhaps the trouble is that viewers of the black and white image know that’s not what that past looked like, but viewers of the coloured image are easily tricked into believing that is history as it was.&lt;/p&gt;

&lt;p&gt;Some may go as far as to say such retouching is rewriting of history, and indeed, sometimes it appears so. Take this recent example:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Cambodia has called on US media group Vice to withdraw an article that featured newly-colourised photographs of victims of the Khmer Rouge, saying the images are an insult to the dead because some had been altered to add smiles.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p class="caption"&gt;&lt;a href="https://www.aljazeera.com/news/2021/4/11/cambodia-condemns-vice-for-altered-khmer-rouge-images"&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which is a very valid complaint. It appears the model was over-fit, likely trained on mostly smiling faces.  It thus altered the faces of the victims to more closely resemble the training data- by smiling. It is a good example of the pitfalls of AI, especially where applied carelessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.myheritage.com/incolor"&gt;MyHeritage&lt;/a&gt;, which offers a color restoration service based on the same DeOldify model I have used, attempts to make a compromise by watermarking all their retouched images. I think they make a compelling point:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We differentiate colorized photos from those photographed originally in color using a special embossed palette symbol in the bottom left corner of colorized photos. While highly realistic, colorized photos have colors that are simulated by automatic algorithms and these colors may not be identical to the real-life colors of the original image. The palette icon appears on all colorized photos so that users can tell them apart from photos that have real colors. We hope that this responsible practice will be adopted by others who use photo colorization technology. Photos that have undergone color restoration do not have an icon because those colors are authentic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my case, my family knew that I had artificially colourised our photos, and they could tell the colours were not wholly accurate. But even with the wrong colours, the photos, and people and scenes within them, were brought back to life. Just as when I was a high-school volunteer in a retirement home, and I was able to transport a senior resident to his childhood home in Dresden via Google Streetview, which he had not visited in 50 years, I was able to use AI to teleport our family  to good times decades in the past. Technology is a tool, and I’m thankful I live in an age where magic like this is possible.&lt;/p&gt;</description><author>Sasha Kuznetsov's Blog</author><pubDate>Sat, 12 Jun 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.skz.dev/bringing-colour-and-life-to-black-and-white-photos</guid></item><item><title>First Post</title><link>https://savraj.co/2021/06/12/first-post.html</link><description>Hey guys, welcome to my brand new blog. First post! Thanks Jekyll and the world of open source.</description><pubDate>Sat, 12 Jun 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://savraj.co/2021/06/12/first-post.html</guid></item><item><title>Digital Santería in Cuba</title><link>https://stop.zona-m.net/2021/06/digital-santer%C3%ADa-in-cuba/</link><description>&lt;p&gt;And what we may learn from it.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 12 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/digital-santer%C3%ADa-in-cuba/</guid></item><item><title>Misinfo about Permissions Policy and FLoC</title><link>https://seirdy.one/posts/2021/04/16/permissions-policy-floc-misinfo/</link><description>Recently, people have been telling webmasters to add a Permissions-Policy header to their sites to opt out of FLoC. The reality of the situation isn't so simple.</description><author>All content on Seirdy’s Home</author><pubDate>Sat, 12 Jun 2021 01:08:29 GMT</pubDate><guid isPermaLink="true">https://seirdy.one/posts/2021/04/16/permissions-policy-floc-misinfo/</guid></item><item><title>Squeezebox Keyboard v2105</title><link>https://peterlyons.com/problog/2021/06/squeezebox-keyboard-v2105/</link><description>&lt;p&gt;I've made a second prototype of the Squeezebox Keyboard. It's a huge improvement over the first working prototype.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://peterlyons.com/problog/2021/06/squeezebox-keyboard-v2105/&amp;#x2F;&amp;#x2F;photos.peterlyons.com&amp;#x2F;2021&amp;#x2F;2021-06-13-01F82HZ3J0ECCRPM37A1TSH528.2048.jpg" /&gt;
  &lt;figcaption&gt;Detail of assembled right hand side&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="improvements-included-in-this-version"&gt;Improvements included in this version&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Key switches are hot swappable courtesy of Kailh hot-swap sockets
&lt;ul&gt;
&lt;li&gt;This required a major level-up in my FreeCAD skills as there's a LOT of precise geometry&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Per-finger splay angle, adjustable for tailor fit&lt;/li&gt;
&lt;li&gt;Each keywell column is a separate independently-operable 3-key macropad
&lt;ul&gt;
&lt;li&gt;This means if one breaks or fails you only need to print a single part and handwire 3 switch/diode pairs&lt;/li&gt;
&lt;li&gt;This is also extremely useful for the DIY process because each small session of handwiring work can be positively confirmed working and correct without the psychological crush of spending one or more full days wiring and soldering only to connect USB and have precisely nada happen when you press a key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Angle between rows A and B increased to solve problem of some fingers scraping it on the fingernail side&lt;/li&gt;
&lt;li&gt;Smaller individual 3D printed components avoids frustration of multi-hour print job failing and delivering no usable parts&lt;/li&gt;
&lt;li&gt;reset button is actually mounted into a housing instead of floating in the air at the end of some wires&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wiring-and-electronics"&gt;Wiring and Electronics&lt;/h2&gt;
&lt;p&gt;Here's a video explaining the wiring system which I believe is fairly novel and strikes me as the "classic Jeep" approach of DIY handwired keyboards. As you'll see in the video, the electronics are made really clear and easy to build, understand, disassemble, reassemble.&lt;/p&gt;
&lt;div class="youtube-video-container"&gt;

&lt;/div&gt;
&lt;p&gt;Key points from the video for the TL;DW crowd&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each finger column can be connected to the microcontroller with dupont wires and work as a 3-key macropad&lt;/li&gt;
&lt;li&gt;The fingers daisy chain together with dupont wires to link up the columns into the full half of a split keyboard&lt;/li&gt;
&lt;li&gt;Thumb cluster works the same way&lt;/li&gt;
&lt;li&gt;Don't like the layout of the thumb cluster? You can disconnect it, build a different one, and connect that up and Bob's your father's brother.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mechanics-assembly-and-adjustment"&gt;Mechanics, Assembly, and Adjustment&lt;/h2&gt;
&lt;p&gt;This video demonstrates how the individual parts are assembled into a system and tailor fit for the user's hand.&lt;/p&gt;
&lt;div class="youtube-video-container"&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Key points&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Heat-set threaded inserts and M3 bolts continue to be heavily used&lt;/li&gt;
&lt;li&gt;This version incorporates magnets holding the base pillars to a metallic bottom plate covered in gaffer tape&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ergonomics-and-tenting"&gt;Ergonomics and Tenting&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src="https://peterlyons.com/problog/2021/06/squeezebox-keyboard-v2105/&amp;#x2F;&amp;#x2F;photos.peterlyons.com&amp;#x2F;2021&amp;#x2F;2021-06-13-01F82HVAF0SEVPWT96JSS4T7D1.2048.jpg" /&gt;
  &lt;figcaption&gt;Squeezebox Keyboard v2105 split and tented for standing use&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There's been some dramatic back and forth on this R&amp;amp;D effort regarding tenting. My original vision was a truly vertical orientation (like a joystick with your hand in "tamborine position" not "bongo position" - thus the name "squeezebox" which is held in that position). Test typing on my first prototype, my fingers felt pretty good but my thumb began quickly to experience fatigue and shaking due to floating unsupported hovering over its home key on the thumb cluster. That would probably be addressable if instead of floating I was really gripping something joystick-shaped or roughly soda can shape but I didn't want to add yet another discipline like modeling clay to my curriculum and my 3D CAD skills were definitely not ready to make some organic curvy thing, so I yielded to pragmatism and changed my design to target using flat on a desk with no tenting like a standard keyboard. So the first working prototype from back in April was used flat with tall wrist rests and had a thumb cluster on the same basic plane of orientation as the main keywells.&lt;/p&gt;
&lt;p&gt;This version was completed with that same basic approach and I was either going to try to use it on my desk top or maybe mount it to the arms of my chair. But once I had the working version with the diamond-pattern metal base plate in hand, I had a eureka moment realizing I could easily and securly mount this to the camera stands I bought initially to tent my Keyboardio Model 01. That keyboard comes with a threaded insert in the base sized to match up with camera mounting gear which is absolutely brilliant. All keyboard designers take note, please. Every split keyboard should have that.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src="https://peterlyons.com/problog/2021/06/squeezebox-keyboard-v2105/&amp;#x2F;&amp;#x2F;photos.peterlyons.com&amp;#x2F;2021&amp;#x2F;2021-06-13-01F82TGMFR39DFHRKR9XCMY1A9.2048.jpg" /&gt;
  &lt;figcaption&gt;Standing desk posture with floating arms&amp;#x2f;hands&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;So I was able to mount this just by cutting a hole in the gaffer tape and running the bolt through the existing hole in the diamond plate pattern and tightening it down with a bolt and it worked great. I have nice fancy adjustable mounts (these are not at all DIY - video folks don't mess around when it comes to gear) and I can tent them anywhere from 0 degrees to full 90 if I want. Here's a video talking a little bit about this.&lt;/p&gt;
&lt;div class="youtube-video-container"&gt;

&lt;/div&gt;
&lt;p&gt;And one random ergonomic note is that the separate fingers and splay angle combined with the white key switch stems makes this thing look a lot like a skeleton hand, which I take as a pretty good indication of the bio-mimickry going on in the ergonomics of it.&lt;/p&gt;
&lt;h2 id="so-you-type-real-fast-now-right"&gt;So You Type Real Fast Now, Right?&lt;/h2&gt;
&lt;p&gt;Ha ha, no. This is a one-of-a-kind prototype. I can sort of barely use it but I am very very slow and very very inaccurate. But at the moment I've truly only spent a few minutes even trying. This is mostly a passion project and adopting it as my daily driver would be great, but I'm many dozens of hours of practice away from seriously considering that. That said, my keymap has undergone a fairly thorough overhaul as a result of the explorations involved with this project, so even on my daily driver ergodox, my layout is better now and I'm gradually reducing pinky usage and other straining movements. Also even on my ergodox which I've been using primarily since 2013 I'm just not very fast or accurate or consistent. 70-90 WPM ballpark. Keyboard ergonomics for me is an accommodation to reduce RSI of which I've had either mild or severe on and off again for years. Look for impressive WPM stats elsewhere.&lt;/p&gt;
&lt;div class="youtube-video-container"&gt;

&lt;/div&gt;
&lt;h2 id="diy-keyboard-kit"&gt;DIY Keyboard Kit&lt;/h2&gt;
&lt;p&gt;The files for this are posted on &lt;a href="https://www.prusaprinters.org/prints/69209-squeezebox-keyboard-v2105"&gt;prusaprinters.org&lt;/a&gt; if you are ambitious enough to attempt this. It is not at all to the level of documentation to expect not-me people to be able to successfully build one, but have at it if you are inspired.&lt;/p&gt;
&lt;h2 id="plans-for-next-prototype"&gt;Plans for next prototype&lt;/h2&gt;
&lt;p&gt;Circumstances in my personal life are likely to no longer afford time to work as much on this project going forward, but if by some means I am able to work on a third prototype, here's my todo list&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the extreme tenting and floating arms/hands seems long-term viable, I will need to design and print a different connector for the thumb cluster so it can have its main plane be more horizontal when the keyboard is tented up to like 80 degrees&lt;/li&gt;
&lt;li&gt;Thumb cluster key layout itself may want to tweak slightly when tenting&lt;/li&gt;
&lt;li&gt;The PCB mount needs a redesign in light of metal base plate and magnets. I'll probably make a very simple rectangular box held to the plate with a magnet and that will help with a tidier wire layout and more stability when connecting/disconnecting the USB cable.
&lt;ul&gt;
&lt;li&gt;might even get really fancy and try to have a top so it's not just a hood-open bouquet of wires :-)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I'd like to make a chair arm rest mount. Possibly magnetic. This would also benefit from a long non-coiled RJ-9 cable that could go around the back of the chair instead of across the front where it is totally in the way.&lt;/li&gt;
&lt;li&gt;I have already found and bought some much nicer connectors for daisy chaining the columns. The plastic connector is more robust and snaps into place more solidly, and all the colors will match up nicely. It is more bulky than the dupont connectors though so it'll maybe need a bit more space to live under the keywells.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="thanks"&gt;Thanks&lt;/h2&gt;
&lt;p&gt;I continue to be very grateful for people across the Internet helping me with this project: answering questions, providing feedback and encouragement, etc. I've posted to many slacks, subreddits, discords, github repos, etc to get help on this project. Especially check out &lt;a href="https://www.youtube.com/user/ModOfLow"&gt;Jan Lunge's Youtube Channel&lt;/a&gt;. You'll find a link to his discord there too.&lt;/p&gt;</description><author>Pete's Points</author><pubDate>Sat, 12 Jun 2021 00:39:11 GMT</pubDate><guid isPermaLink="true">https://peterlyons.com/problog/2021/06/squeezebox-keyboard-v2105/</guid></item><item><title>Develop and deploy your first Ethereum Smart Contract with Python  Learn how to write a basic smart contract in Solidity and then integrate it with Python app by using Web3.py</title><link>https://blog.adnansiddiqi.me/develop-and-deploy-your-first-ethereum-smart-contract-with-python/</link><description>&lt;p&gt;This post going to be a bit longer as I am going to cover multiple concepts. I will be covering the following things: Smart Contracts and how do they work in Ethereum blockchain. The basics of Solidity Programming language and how to use online and existing IDEs to write and test them. Using Truffle and Ganache for Ethereum development environment setup. Web3.py helps to integrate Smart Contract with Python applications. What is a Smart Contract According to Investopedia: A smart contract is a self-executing contract with the terms of the agreement between buyer and seller being directly written into lines of code. The code and the agreements contained therein exist across a distributed, decentralized blockchain network. The code controls the execution, and transactions are trackable and irreversible. As per Ethereum: A &amp;#8220;smart contract&amp;#8221; is simply a program that runs on the Ethereum blockchain. It&amp;#8217;s a collection of code (its functions) and data (its state) that resides at a specific address on the Ethereum blockchain. In simple words, Smart Contract is a piece of code that runs within the blockchain. The term coined by Nick Szabo back in the 90s who actually wanted software that works as a middleman between multiple parties. Ethereum uses this term to run any piece of code within the Ethereum blockchain. Solidity is the language used to write Ethereum smart contracts. It took inspiration from C++, Javascript, and Python. Vyper is a recent addition to the Ethereum ecosystem which is highly inspired by Python. Since Solidity is quite an established language and loads of resources are available, I&amp;#8217;d recommend learning it instead of Vyper to get the basics of smart contract programming. Once you learn it, learning Vyper will get easier. I will try to cover Vyper in the coming months. Remix is a very popular IDE to develop, run and test smart contracts. You may connect local Ethereum network or use the one it already provides, it does not matter. What makes it good that you can quickly test your code. Solidity Primer Solidity is the original programming language introduced by Ethereum to write smart contracts. Almost all famous coins and tokens running on the Ethereum platform are using Solidity. I am heading to Remix web-based IDE to write our first contract. When you visit Remix you will be seeing something like the below: As you can it is quite an advanced IDE. The screen you are seeing on left is a side-pane related to file browsing. It has different pre-defined folders available for writing and testing your code. The next is for Solidity Compiler, you may select different options, I&amp;#8217;d say to leave it for the default. After that is for Deployment and Transaction for compiling and running smart contracts. Under the contracts folder, I created a new file FirstContract.sol. All solidity files have .sol extension so whether you run it locally or on Remix you will have to use .sol extension for your smart contract files. The very first line is actually telling which language and compiler version is compatible. The language solidity is being used to write code. The next thing is about versioning. If you are familiar with RegEx then it should not sound strange to you. It is telling that this code will not work for compilers earlier than 0.5.11. It will also not work for compilers like 0.6.x or later and you know it is all due to ^ sign. You have various ways to make your code compatible with different versions. For instance, pragma solidity &amp;#62;=0.4.22 &amp;#60;0.6.0; will make sure that your code is only working for compilers greater than 0.4.22 and less than 0.6.0. The next code block should be very familiar for you if you have done OOP. The keyword contract is actually like class and starting a code block that talks about certain features of the software. I define a private variable value of type uint.  The default accessibility of these variables is private. The variable value is actually a State Variable that means the content of the variable is permanently stored in contract storage. When I say this variable is private, it means it is not accessible by another contract as well as you can&amp;#8217;t call it directly from another program like a Python app. You must know that the information which is being stored in a blockchain is still PUBLIC. So do not confuse that by making a variable private you can hide something confidential. Nothing is confidential in the blockchain world. Next, we define a function getValue() that returns an unsigned int value . Next, you find external keyword. It makes this function available for other contracts that import this contract file. The opposite of it that is, internal can only be called inside of the contract. When you add public in it you make it function available outside of the environment and can be called from out of the system, for instance in your Python app via web3.js or web3.py. The opposite of public is private. if a variable or function is private it can&amp;#8217;t be accessed out of it. The returns  keyword actually tells that this function is returning a value of the type given in brackets which is uint in this case. The logic of the getter/setter functions is self-explanatory. It is time to compile the code. Once the contract is compiled, it is now time to deploy and run the contract on the blockchain. You will notice a few things, the ETH wallet address will be used to deduct the amount for the gas purpose. The environment is JS by default. Leave everything as it is and make sure you pick the right smart contract for the deployment. When you hit the Deploy button, it will deploy it on the blockchain. You see that the status says that the Transaction is mined by miners across the globe. They are paid for their service. Everything which changes the state of the Ethereum blockchain actually considered a transaction. It means you will have to pay for it. If you scroll it down a bit you will find both the transaction and execution cost in the form of gas. Who will pay for it? well, your wallet. Every time a transaction happens the amount will be deducted from your wallet. Once it is deployed, you will see your contract under Deployed Contracts section and its functions. As you can the functions we wrote are visible here. We will set the value first. I enter 5 and hit the setValue button. When it executed, you will see that the Debug window has a few new entries. Scroll down and again you will find gas values for the transaction because the value 5 is now stored in the blockchain. Now hit the getValue button to execute the function. Notice the [call] tag at the beginning instead of [VM]. It means that is a simple function call instead of a transaction hence no gas cost is involved here because it is not changing the state of the blockchain thus no mining is involved which means no gas deduction. These are very important concepts that you must know because every line of your code will cost money to your employer or your client. It is not like you just used an array because you love it. You use Remix or any other IDE or environment, the concepts will be the same. Before I move onto the next section, what if I want to know programmatically who is paying for the execution, msg.sender is the answer. Here I added a new variable and a function. The sender variable is of type address. Though you do not create a function just for the heck of it. It is just for explaining purposes. Like before, you will compile and deploy the contract. Every time you deploy a contract it will create a new copy of it. So make sure you pick the right version. Pick setValue function from the newly deployed contract, enter any value and then run getMsgSender. As expected it will print the first address, 0x5B3.. here. The reason you executed setValue first because I am setting msg.Sender variable here.  Solidity provides some special variables to find a wallet and transaction-related details. You can learn more about it here. The Remix IDE is good to test things online but we should have some local setup to code, test,  and deploy smart contracts. I will be introducing two tools here: Truffle which provides a development environment for blockchain apps and Ganache which provides a local Ethereum blockchain for testing purposes. Setting up Truffle and Ganache In order to install Truffle, we will be using npm as truffle is actually a node module: npm install -g truffle In order to check whether it installed correctly, run truffle command in the console: Once installed, I am going to create a new folder where I will initialize the truffle project. As you can see it creates a few folders and a config file. I am using VS Code IDE with the Solidity Plugin by Juan Blanco. Assuming you have Ganache Desktop GUI is installed, you will be creating a new workspace in it. You select Ethereum as the project type and then set the directory where truffle-config.js file exists. Once it&amp;#8217;s all done you can see the screen like below: You can notice a few things besides the list of ether addresses available for you like gas limit, network ID, and the RPC Server, etc. Under the contracts tab, I created a new file FirstContract.sol and just copied the contract we had developed in Remix IDE. In the migrations folder, you will add a file 2_deploy_contracts.js and the following content in it. For the web developers who worked on Frameworks like RoR, Django, and Laravel, the term migration is not something new. The first line tells where the desired contract file exists. Then the contract instance variable is set to the deploy method. You do not need to worry about it as the Truffle will take care of this. Just make sure you set the correct contract path. All set, now it is time to compile our project. If you notice it created a new build folder that contains two files: FirstContract.json and Migrations.json. The former file contains all info related to the contract you want to deploy like its original source, the compiled bytecode, etc. "metadata": "{\"compiler\":{\"version\":\"0.5.16+commit.9c3226ce\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"getMsgSender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"setValue\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/Users/AdnanAhmad/Data/Development/PetProjects/LearningSolidity/SolidityPythonTutorial/contracts/FirstContract.sol\":\"FirstContract\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"/Users/AdnanAhmad/Data/Development/PetProjects/LearningSolidity/SolidityPythonTutorial/contracts/FirstContract.sol\":{\"keccak256\":\"0x8f927552ec9dc73acee0143c170c276f19ee1e1a85959b49979ef7f524923ff3\",\"urls\":[\"bzz-raw://ba69d6310ad02e5c74228ea971e184bc5debdfb3e3af280fe43a867b136c83cd\",\"dweb:/ipfs/QmT4HoJ6bSDHqcK2jyciU5CaRqyzZfgFbeTGKfJwWwjPU3\"]}},\"version\":1}", "bytecode": "0x608060405234801561001057600080fd5b5061018f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063209652551461004657806355241077146100645780637a6ce2e114610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e5565b005b61009a610130565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008054905090565b33600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060008190555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690509056fea265627a7a72315820ea770f34255d72ae19ebe6d36b62a54850a9602e70b039c54b29b65dc7cb7f2364736f6c63430005100032", "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063209652551461004657806355241077146100645780637a6ce2e114610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e5565b005b61009a610130565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008054905090565b33600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060008190555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690509056fea265627a7a72315820ea770f34255d72ae19ebe6d36b62a54850a9602e70b039c54b29b65dc7cb7f2364736f6c63430005100032", "sourceMap": "26:367:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;26:367:0;;;;;;;", "deployedSourceMap": "26:367:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;26:367:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;97:78;;;:::i;:::-;;;;;;;;;;;;;;;;;;;185:100;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;185:100:0;;;;;;;;;;;;;;;;;:::i;:::-;;295:86;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;97:78;139:4;163:5;;156:12;;97:78;:::o;185:100::-;244:10;235:6;;:19;;;;;;;;;;;;;;;;;;272:6;264:5;:14;;;;185:100;:::o;295:86::-;341:7;368:6;;;;;;;;;;;361:13;;295:86;:::o", "source": "pragma solidity ^0.5.11;\n\ncontract FirstContract {\n uint value;\n address sender; \n \n function getValue() external view returns(uint) { \n return value;\n }\n \n function setValue(uint _value) external {\n sender = msg.sender;\n value = _value;\n }\n \n function getMsgSender() external view returns(address) { \n return sender;\n }\n \n \n}", "sourcePath": "/Users/AdnanAhmad/Data/Development/PetProjects/LearningSolidity/SolidityPythonTutorial/contracts/FirstContract.sol", "ast": { "absolutePath": "/Users/AdnanAhmad/Data/Development/PetProjects/LearningSolidity/SolidityPythonTutorial/contracts/FirstContract.sol", "exportedSymbols": { "FirstContract": [ 37 ] }, Alright. Now it is all set to deploy our first contract on the local Ethereum blockchain. There are few things to notice here. Well, there are many things to be noticed. You see a transaction hash. It is a transaction because you changed the state of the blockchain. Then you find the contract address. The account which actually deployed the contract. The eth consumed for this operation and the total cost. The contract address is very important as we will be using it to interact while writing our Python app.  You can see the deployment and execution of calls in Ganache in the below picture. Whatever you saw above you can find it on the Ganache interface as well. Smart Contract and Python Integration Our smart contract is deployed. In order to make it useful, we need some way to interact with apps that will not be running in the blockchain. Luckily Web3 libraries for Javascript and Python could help here. In order to connect our Python apps with Ethereum based smart contract, we are going to use the Web3 library. pip install web3 The web3 library is installed, it is time to...&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/develop-and-deploy-your-first-ethereum-smart-contract-with-python/"&gt;Develop and deploy your first Ethereum Smart Contract with Python &lt;br /&gt; &lt;span style="color: grey; font-size: 25px; font-weight: 300;"&gt;Learn how to write a basic smart contract in Solidity and then integrate it with Python app by using Web3.py&lt;/span&gt;&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Fri, 11 Jun 2021 15:51:55 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/develop-and-deploy-your-first-ethereum-smart-contract-with-python/</guid></item><item><title>Pico Ada Libraries Release 0.4.0</title><link>https://synack.me/ada/pico/2021/06/10/rp2040-hal-release-0-4-0.html</link><description>I’ve tagged release 0.4.0 of the rp2040_hal, pico_bsp, and pico_examples libraries for the Raspberry Pi Pico. Lots of testing and bug fixes on SPI and I2C in this release and both interfaces should be much more stable now. DMA to PIO works well and opens up a lot of possibilities for high speed signaling. Examples have been added for a rigorous SPI loopback test and a custom BSP implementation for the Adafruit Feather RP2040, which should serve as a template for running on any board that uses the RP2040. The Pimoroni Audio Pack example has been updated to use the ROM floating point library to generate a sampled sine output.</description><author>Jeremy Grosser</author><pubDate>Fri, 11 Jun 2021 09:35:00 GMT</pubDate><guid isPermaLink="true">https://synack.me/ada/pico/2021/06/10/rp2040-hal-release-0-4-0.html</guid></item><item><title>Custom default values for not existing dictionary items (and a lesson learned)</title><link>https://nicolaiarocci.com/custom-default-values-for-not-existing-dictionary-items-and-a-lesson-learned/</link><description>&lt;p&gt;When dealing with dictionaries, a typical problem is when an operation attempts
to retrieve an element using a key that does not exist in the dictionary. In
.NET, a &lt;code&gt;KeyNotFoundException&lt;/code&gt; is raised, and that&amp;rsquo;s the desired behaviour in
most circumstances. Sometimes, however, you know that your program will
frequently try to retrieve keys that do not exist. In such cases, it is more
efficient to use the &lt;code&gt;TryGetValue&lt;/code&gt; method:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This method returns the value associated with the specified key, if the key
is found; otherwise, the default value for the type of the value parameter is
returned (&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetvalue?view=net-5.0"&gt;source&lt;/a&gt;)&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 11 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/custom-default-values-for-not-existing-dictionary-items-and-a-lesson-learned/</guid></item><item><title>Us Again</title><link>https://blog.yiningkarlli.com/2021/06/us-again.html</link><description>&lt;div style="display: none;"&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;

&lt;div class="tableofcontents"&gt;
    &lt;div class="tableofcontents-row"&gt;
        &lt;div class="tableofcontents-column3"&gt;
            &lt;div class="tableofcontents-content"&gt;
                1. &lt;a href="/2021/06/us-again.html#2021-06-11-notes"&gt;Making Us Again&lt;/a&gt;&lt;br /&gt;
                2. &lt;a href="/2021/06/us-again.html#2021-06-11-frames"&gt;Frames from the Short&lt;/a&gt;&lt;br /&gt;
                3. &lt;a href="/2021/06/us-again.html#2021-06-11-references"&gt;References&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column3"&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column3"&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="2021-06-11-notes"&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="/2021/03/raya-and-the-last-dragon.html"&gt;Raya and the Last Dragon&lt;/a&gt;&lt;/em&gt;’s release earlier this spring was accompanied &lt;em&gt;Us Again&lt;/em&gt;, Disney Animation’s newest short film.
&lt;em&gt;Us Again&lt;/em&gt; is also included on the Blu-ray for &lt;em&gt;Raya and the Last Dragon&lt;/em&gt;, and now that the Blu-ray is out, here are some brief notes about interesting rendering details from &lt;em&gt;Us Again&lt;/em&gt;.
&lt;em&gt;Us Again&lt;/em&gt; is, of course, rendered entirely using Disney’s Hyperion Renderer.
I only played a small role on &lt;em&gt;Us Again&lt;/em&gt;, pretty much entirely just support, but I thought I’d post a bit about the short because it’s such a spectacular showcase of what Hyperion is capable of when placed in Disney Animation artists’ hands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_022.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_022.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Us Again&lt;/em&gt; is a stunningly beautiful film; I think it’s one of the most beautiful shorts that Disney Animation has ever made.
The short follows elderly couple Art and Dot re-finding the spark of youth in their relationship through dance.
The short is set in a bustling city and most of the short takes place in a rain storm, with raindrops flying, wet surfaces on everything, and tons of complex crowds and neon city lighting reflecting everywhere.
From a rendering perspective, this can be a preposterously difficult setting to render; there are tons of lights stressing any kind of light sampling strategy, there are tons of specular surfaces and reflections generating difficult to sample light transport, the city setting means the geometric complexity is through the roof, the face paced dance sequences and rainfall means there is high motion blur everywhere, and so on and so forth.
A little under ten years ago, I was an intern at Pixar during the production of &lt;em&gt;The Blue Umbrella&lt;/em&gt;, which also has a rainy urban setting, and I got a close up view of how that short was at the time one of the hardest things Pixar had ever rendered.
&lt;em&gt;Us Again&lt;/em&gt; has even more complex challenges on top of what was in &lt;em&gt;The Blue Umbrella&lt;/em&gt;, so I was really worried that we would run into major difficulties rendering &lt;em&gt;Us Again&lt;/em&gt;.
I was really pleasantly surprised when… lighting and rendering &lt;em&gt;Us Again&lt;/em&gt; went really smoothly and basically no major problems or difficulties emerged at all!
Rendering technology generally has advanced enormously in the past ten years, and by fortunate coincidence Hyperion just happens to have a lot of specific capabilities that are particularly well suited to the rendering challenges in &lt;em&gt;Us Again&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Us Again&lt;/em&gt; used essentially the same version of Hyperion that was used for &lt;em&gt;&lt;a href="/2019/11/froz2.html"&gt;Frozen 2&lt;/a&gt;&lt;/em&gt;, with one notable modification.
Motion blur on &lt;em&gt;Us Again&lt;/em&gt; took some special care to handle because of all of the rain; fast-falling raindrops with a wider shutter angle blurs out into the familiar long streaks that are usually seen in “movie rain”.
Because the motion blurred streaks for rain were so long in screen space, the streaks would tend to bend whenever the camera was under high motion too, sort of like how fast-spinning propellor blades end up looking weird on rolling shutter cameras.
Hyperion already allows for different objects in the same scene and same render to effectively be rendered with different shutter angles; to fix the look of the rain streaks on &lt;em&gt;Us Again&lt;/em&gt;, all the team had to add was the ability to decouple &lt;em&gt;camera&lt;/em&gt; motion blur from &lt;em&gt;object&lt;/em&gt; motion blur.&lt;/p&gt;

&lt;p&gt;For pretty much everything else on &lt;em&gt;Us Again&lt;/em&gt;, our existing solutions in Hyperion just worked.
&lt;em&gt;Us Again&lt;/em&gt;’s urban setting represents a worst-case complexity scenario for light selection and light sampling.
Because &lt;em&gt;Us Again&lt;/em&gt; is set in a city, every shot in the short has a gazillion lights present spread across a huge region of space, and the finale of the short takes place on a boardwalk amusement park pier&lt;sup id="2021-06-11-us-again-footnote-1-backlink"&gt;&lt;a href="#2021-06-11-us-again-footnote-1"&gt;1&lt;/a&gt;&lt;/sup&gt; with a ferris wheel and other rides covered in animated light bulbs.
These types of lighting scenarios through the sheer number of lights present combined with the complex occlusion can sometimes be enough to overwhelm light-BVH based many-lights sampling strategies &lt;a href="https://doi.org/10.1145/3233305"&gt;[Estevez and Kulla 2018]&lt;/a&gt;, and without a many-lights sampling strategy that can handle these cases well, in the worse case falling back to laborious manual grouping and culling of lights may be the only option &lt;a href="https://doi.org/10.1145/3306307.3328161"&gt;[Vavilala 2019]&lt;/a&gt;.
However, Hyperion’s “cache points” many-lights sampling system was developed from the very beginning to handle millions of lights with complex occlusion in vast cityscapes, because this was the exact type of setting that was found throughout &lt;em&gt;Big Hero 6&lt;/em&gt; &lt;a href="https://doi.org/10.1145/3182159"&gt;[Burley et al. 2018]&lt;/a&gt;.
For &lt;em&gt;Us Again&lt;/em&gt;, Hyperion’s cache points system handled all of the highest lighting complexity scenes (such as the ferris wheel on the pier) with millions of light sources with no problem at all; our lighters simply opened the scenes, lit like usual, hit render, and everything just worked efficiently with zero additional optimization work required &lt;a href="https://doi.org/10.1145/3665320.3670993"&gt;[Li et al. 2024]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a similar vein, because &lt;em&gt;Us Again&lt;/em&gt; is set in a rain storm, every asset needed to have both wet and dry variants with custom blending between the two, which would be challenging to author if wet and dry looks had to be authored separately.
However, thanks to Disney Animation’s layer stack shading model with sparse overrides &lt;a href="https://doi.org/10.1145/2343483.2343493"&gt;[Burley 2012&lt;/a&gt;, &lt;a href="https://doi.org/10.1145/2776880.2787670"&gt;Burley 2018]&lt;/a&gt;, adding a wet look to every shader was as simple as just sticking on an additional wet layer, driven with whatever masks artists wanted.&lt;/p&gt;

&lt;p&gt;Finally, one of the things I love the most visually about &lt;em&gt;Us Again&lt;/em&gt; is just the sheer amount of detail and richness there is in everything.
When watching the short, pay attention to things like the fuzz on Dot’s sweater, or the little splashes happening everywhere as rain drops land, or the streaks of water running down Art and Dot’s faces and clothes.
I think ten years ago this short would have been extraordinarily difficult (possibly borderline impossible) for the studio to make, but thanks to a decade of continuous relentless improvement in our filmmaking technology and craft, making &lt;em&gt;Us Again&lt;/em&gt; went off without any major hitches and turned out beautifully!&lt;/p&gt;

&lt;p&gt;Here are some frames from &lt;em&gt;Us Again&lt;/em&gt; from the Blu-ray, presented in random order as usual.
I really recommend seeing &lt;em&gt;Us Again&lt;/em&gt; in motion though; the stills here really don’t do the amazing choreography and animation justice.
Get &lt;em&gt;Us Again&lt;/em&gt; with a copy of &lt;em&gt;Raya and the Last Dragon&lt;/em&gt; or watch it on &lt;a href="https://www.disneyplus.com/browse/entity-fd7c2ab8-7e53-4e05-a0f8-65b42a5efe01"&gt;on Disney+&lt;/a&gt;; either way, go watch it on the biggest screen you can find!&lt;/p&gt;

&lt;div id="2021-06-11-frames"&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_018.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_018.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_004.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_004.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_020.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_020.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_001.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_001.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_012.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_012.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_002.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_002.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_003.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_003.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_006.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_006.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_010.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_010.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_011.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_011.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_007.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_007.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_005.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_005.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_014.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_014.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_016.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_016.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_009.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_009.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_017.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_017.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_019.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_019.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_021.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_021.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_008.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_008.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_015.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_015.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_013.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_013.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_023.jpg"&gt;&lt;img alt="" src="https://blog.yiningkarlli.com/content/images/2021/Jun/us-again/USAGAIN_023.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All images in this post are courtesy of and the property of Walt Disney Animation Studios.&lt;/p&gt;

&lt;div id="2021-06-11-references"&gt;&lt;/div&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;

&lt;p&gt;Brent Burley. 2012. &lt;a href="https://doi.org/10.1145/2343483.2343493"&gt;Physically Based Shading at Disney&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2012 Course Notes: &lt;a href="https://blog.selfshadow.com/publications/s2012-shading-course/"&gt;Practical Physically-Based Shading in Film and Game Production&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Brent Burley. 2015. &lt;a href="https://doi.org/10.1145/2776880.2787670"&gt;Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2015 Course Notes: &lt;a href="https://blog.selfshadow.com/publications/s2015-shading-course"&gt;Physically Based Shading in Theory and Practice&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Brent Burley, David Adler, Matt Jen-Yuan Chiang, Hank Driskill, Ralf Habel, Patrick Kelly, Peter Kutz, Yining Karl Li, and Daniel Teece. 2018. &lt;a href="https://doi.org/10.1145/3182159"&gt;The Design and Evolution of Disney’s Hyperion Renderer&lt;/a&gt;. &lt;em&gt;ACM Transactions on Graphics&lt;/em&gt; 37, 3 (Jul. 2018), Article 33.&lt;/p&gt;

&lt;p&gt;Brian Boyd. 2013. &lt;a href="https://doi.org/10.1145/2504459.2504525"&gt;Lighting “The Blue Umbrella”&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2013 Talks&lt;/em&gt;. Article 53.&lt;/p&gt;

&lt;p&gt;Alejandro Conty Estevez and Christopher Kulla. 2018. &lt;a href="https://doi.org/10.1145/3233305"&gt;Importance Sampling of Many Lights with Adaptive Tree Splitting&lt;/a&gt;. &lt;em&gt;Proc. of the ACM on Computer Graphics and Interactive Techniques (Proc. of High Performance Graphics)&lt;/em&gt; 1, 2 (Aug. 2018), Article 25.&lt;/p&gt;

&lt;p&gt;Yining Karl Li, Charlotte Zhu, Gregory Nichols, Peter Kutz, Wei-Feng Wayne Huang, David Adler, Brent Burley, and Daniel Teece. 2024. &lt;a href="https://doi.org/10.1145/3665320.3670993"&gt;Cache Points for Production-Scale Occlusion-Aware Many-Lights Sampling and Volumetric Scattering&lt;/a&gt;. In &lt;em&gt;Proc. of Digital Production Symposium (DigiPro 2024)&lt;/em&gt;. Article 6.&lt;/p&gt;

&lt;p&gt;Vaibhav Vavilala. 2019. &lt;a href="https://doi.org/10.1145/3306307.3328161"&gt;Lighting Pruning on Toy Story 4&lt;/a&gt;. In &lt;em&gt;ACM SIGGRAPH 2019 Talks&lt;/em&gt;. Article 44.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;

&lt;p&gt;&lt;sup id="2021-06-11-us-again-footnote-1"&gt;1&lt;/sup&gt; If you pay close attention, you may notice that the pier amusement park in &lt;em&gt;Us Again&lt;/em&gt; is a nod to the old Paradise Pier area at Disney’s California Adventure, complete with the same entrance sign!
&lt;a href="#2021-06-11-us-again-footnote-1-backlink"&gt;&lt;span class="material-symbols-outlined"&gt;keyboard_return&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Fri, 11 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/06/us-again.html</guid></item><item><title>2021–06–11: PinePhone keyboard – HW testing</title><link>https://xnux.eu/log/#036</link><author>megi's PinePhone Development Log</author><pubDate>Fri, 11 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#036</guid></item><item><title>I Have No Group, and I Must Scream</title><link>https://bastian.rieck.me/blog/2021/groups/</link><description>&lt;p&gt;One of the central tenets of software development, science, and life in
general is &amp;lsquo;Be humble.&amp;rsquo; In software development, this means that you
should always assume that your code is wrong. It&amp;rsquo;s almost &lt;em&gt;never&lt;/em&gt; an
issue with the compiler, the frameworks, or something else. Most of the
time, it is &lt;em&gt;you&lt;/em&gt;. This is one of those rarer cases where it is &lt;em&gt;not&lt;/em&gt;
you.&lt;/p&gt;
&lt;h1 id="prelude"&gt;Prelude&lt;/h1&gt;
&lt;p&gt;Like many research groups, we like to use compute clusters in order to
execute complex computations in parallel. Our university provides, next
to some additional resources, a &lt;a href="https://scicomp.ethz.ch/wiki/Euler"&gt;cluster&lt;/a&gt; that uses
&lt;a href="https://en.wikipedia.org/wiki/Platform_LSF"&gt;IBM Spectrum LSF&lt;/a&gt;.
Employing a variety of specialised commands, this framework permits you to
interact with a cluster. For instance, you can submit a new job that is
to be executed on some compute node with a certain set of resource
requirements. Personal preferences notwithstanding, the framework is
actually pretty great&amp;mdash;after a brief tutorial session, anyone can turn
their Python scripts or other commands into jobs executed on the
cluster. As a simple example, here is how to submit the command &lt;code&gt;ls&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;bsub ls
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s how easy it is in many cases. More advanced calls can be made, of
course, if additional resources such as memory or CPU cores are required.
Depending on the configuration of the cluster itself, it is also
possible to request special nodes for, say, GPU-based computations. But
the main idea is that &lt;code&gt;bsub&lt;/code&gt; and LSF should enable you to run
&amp;lsquo;fire-and-forget&amp;rsquo; jobs that will be executed at &lt;em&gt;some point&lt;/em&gt; by &lt;em&gt;some
node&lt;/em&gt;. The exact choice of node does not matter&amp;mdash;or so I thought!&lt;/p&gt;
&lt;h1 id="failure"&gt;Failure&lt;/h1&gt;
&lt;p&gt;As we were working towards a deadline, I noticed intermittent job
failures but I did not think too much of it. Sometimes, compute nodes go
down because of hardware issues, so there is always the probability that
not all jobs can be executed properly. But a few days before some
important results were due, more than 90% of all jobs started to fail.
The error message was preceded by one of my own warnings (I
redacted the filename for clarity):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;UserWarning: File $FILE not found. This will cause an error.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A quick check showed that the file was there; I was also able to read it
from one of the login nodes, so I ruled out any issues with file
permissions for now. Adding some additional tracing into the code,
I found that it indeed always failed when it tried to open a certain
file. The file in question was not even very large&amp;mdash;just a few
kilobytes at best&amp;mdash;but loading it failed in &lt;em&gt;almost all circumstances&lt;/em&gt;.
This was getting spooky. Did I have a &lt;a href="https://en.wikipedia.org/wiki/Heisenbug"&gt;Heisenbug&lt;/a&gt;
on my hands, given that I was unable to reproduce it under controlled
conditions?&lt;/p&gt;
&lt;h1 id="the-light"&gt;The Light!&lt;/h1&gt;
&lt;p&gt;After much wailing, weeping, and gnashing of teeth, I decided to just
read more about the function that created the error. Partially, this was
to distract me from impending doom, but I was also curious as to what
was going on here. I was using &lt;code&gt;os.path.exists()&lt;/code&gt; to query the existence of
the file, so I thought that consulting its manual might be a smart idea.
I found the following instructions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Return &lt;code&gt;True&lt;/code&gt; if path refers to an existing path or an open file
descriptor. Returns &lt;code&gt;False&lt;/code&gt; for broken symbolic links. On some platforms,
this function may return &lt;code&gt;False&lt;/code&gt; if permission is not granted to execute
&lt;code&gt;os.stat()&lt;/code&gt; on the requested file, even if the path physically exists.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This seemed to imply that the function &lt;em&gt;might&lt;/em&gt; have returned &lt;code&gt;False&lt;/code&gt; in
order to indicate that the file cannot be accessed. I had ruled out
access issues before, but now they were suddenly back on the menu. Could
it be that permissions changed intermittently? Was I being fooled into
thinking that the permissions on the login node were the same as on the
compute node? I certainly did not change permissions in any way, but as
a last-ditch effort I decided to write a script that checks the
permissions by evaluating the groups my user is a part of. The script
looked something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env bash

for INDEX in $(seq 0 99); do
  bsub -W 00:05 -o "debug_%J.out" -R "rusage[mem=128]" groups
done
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice how I tried to submit a lot of jobs in order to gain a clearer
picture of the status of the compute nodes. I was hoping that the jobs
would be nicely distributed among the cluster, and I anxiously waited
for them to finish running. With the last job finally being completed,
I collected my results:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;tail -qn 1 debug_*.out | sort  | uniq -c
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A good output would have been something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;100 users foo bar baz 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But instead, I received an absolute &lt;em&gt;hotchpotch&lt;/em&gt; of different groups; in
some cases, my user was part of almost no &lt;em&gt;groups&lt;/em&gt; at all. The cluster,
much like my will a few minutes before, was broken. These news, grim as
they might be for the administrative staff, cheered me up quite
considerably because for once, everything was right in the universe
again&amp;mdash;there was an explanation! Much like the characters in &lt;a href="https://en.wikipedia.org/wiki/I_Have_No_Mouth,_and_I_Must_Scream"&gt;Harlan
Ellison&amp;rsquo;s story whose title I butchered for this
post&lt;/a&gt;,
I had been duped and misled by the computer. I have no group, and I must
scream.&lt;/p&gt;
&lt;h1 id="epilogue"&gt;Epilogue&lt;/h1&gt;
&lt;p&gt;Unlike Ellison&amp;rsquo;s story, mine had a good ending: the debug script was
appreciated by the support staff, and the issue was rectified within
a few days&amp;mdash;until it cropped up again, causing considerably less
confusion (the debug script was already in place), and was fixed
again. This time for real.&lt;/p&gt;
&lt;p&gt;My personal lesson from all of this: I need to study my functions more
carefully. While I could argue that &lt;code&gt;os.path.exists&lt;/code&gt; should have
a different behaviour&amp;mdash;the function is called &lt;code&gt;exists&lt;/code&gt;, after all&amp;mdash;it
was nevertheless an assumption of my part when I thought that this would
&lt;em&gt;always&lt;/em&gt; work as expected. Know thyself and know thy functions, even if
they appear to be pretty innocuous at first glance.&lt;/p&gt;
&lt;p&gt;Hope you will always be in the right group&amp;mdash;until next time!&lt;/p&gt;</description><author>Ecce Homology on Bastian Grossenbacher Rieck's personal homepage</author><pubDate>Thu, 10 Jun 2021 22:00:00 GMT</pubDate><guid isPermaLink="true">https://bastian.rieck.me/blog/2021/groups/</guid></item><item><title>COVID exposes incompetence of digital natives</title><link>https://stop.zona-m.net/2021/06/covid-exposes-incompetence-of-digital-natives/</link><description>&lt;p&gt;Exposes? &amp;ldquo;Reminds&amp;rdquo;, more like.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 10 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/covid-exposes-incompetence-of-digital-natives/</guid></item><item><title>Unbreakable Builds on Container Schedulers without Containers</title><link>https://blog.tjll.net/container-scheduling-without-containers/</link><description>&lt;p&gt;
If you&amp;rsquo;re like me, you: have to look up &lt;code&gt;Dockerfile&lt;/code&gt; quirks each time you write one, never know whether a container you &lt;code&gt;exec&lt;/code&gt; into will have &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;sh&lt;/code&gt;, or whatever other shell, and are never sure which container &lt;code&gt;init&lt;/code&gt; is currently the best practice.
I definitely still don&amp;rsquo;t know what the hell &lt;a href="https://mobyproject.org/"&gt;Moby&lt;/a&gt; is.
&lt;/p&gt;

&lt;p&gt;
Overall, packaging applications into Docker container images isn&amp;rsquo;t very ergonomic, and the &amp;ldquo;repeatable&amp;rdquo; part isn&amp;rsquo;t all that reliable (have you ever run into outdated distribution repositories? It&amp;rsquo;s annoying).
That said, container primitives are cool and useful - resource constraints like cgroups are nice, and the fundamental principle of shipping your &lt;i&gt;entire&lt;/i&gt; runtime is definitely good for deployment consistency.
Let&amp;rsquo;s mess around with other ways to build executable artifacts to toss into a container orchestration system or workload scheduler.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
Also: this title is clickbait.
I know you can break pretty much any build.
This is just a way to make the process a little better.
&lt;/p&gt;
&lt;div class="outline-4" id="outline-container-the-runtime"&gt;
&lt;h4 id="the-runtime"&gt;The Runtime&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org044d6c6"&gt;
&lt;p&gt;
I use &lt;a href="https://www.nomadproject.io/"&gt;Hashicorp Nomad&lt;/a&gt; in my homelab to schedule workloads because it&amp;rsquo;s uncomplicated and flexible.
Nomad actually supports &lt;a href="https://www.nomadproject.io/docs/drivers"&gt;numerous types&lt;/a&gt; of task drivers aside from Docker - in addition to other containerized drivers like podman, Nomad can also natively run things like Java jars or qemu virtual machines.
&lt;/p&gt;

&lt;p&gt;
Nomad can also sandbox simple executables with the &lt;a href="https://www.nomadproject.io/docs/drivers/exec"&gt;&lt;code&gt;exec&lt;/code&gt;&lt;/a&gt; driver.
Coupled with the ability to fetch and extract &lt;a href="https://www.nomadproject.io/docs/job-specification/artifact"&gt;artifacts&lt;/a&gt;, I like to pop programs into a minio instance somewhere and let Nomad clients retrieve their workloads, and it works pretty well.
&lt;/p&gt;

&lt;p&gt;
Nomad supports all the typical bells and whistles you&amp;rsquo;d expect from an &amp;ldquo;orchestrator&amp;rdquo; like secrets management, environment variable injection, and more.
Moving on.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="outline-4" id="outline-container-the-executable"&gt;
&lt;h4 id="the-executable"&gt;The Executable&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org5b73763"&gt;
&lt;p&gt;
You could probably come up with some way to get a &lt;i&gt;portable&lt;/i&gt; executable across to your Nomad node, but one of the biggest boons to packaging an application into a container image is that everything comes with it.
Shared libraries, dependent executables, all of it.
Of course, you do end up with abominations like this, and you know in your gut that every piece of this line feels like an annoying anti-pattern.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;Dockerfile&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-legend"&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="org-keyword"&gt;Font used to highlight keywords.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-dockerfile"&gt;&lt;code&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;RUN&lt;/span&gt; apt-get update &amp;amp;&amp;amp; &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    apt-get -y --no-install-recommends install curl &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;        ca-certificates &amp;amp;&amp;amp; &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &amp;lt;curl something&amp;gt;&lt;/code&gt;
&lt;code&gt;    apt-get purge -y curl &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;        ca-certificates &amp;amp;&amp;amp; &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    apt-get autoremove -y &amp;amp;&amp;amp; &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    apt-get clean &amp;amp;&amp;amp; &lt;span class="org-sh-escaped-newline"&gt;\&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    rm -rf /var/lib/apt/lists/*&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The container primitives are awesome, but if I&amp;rsquo;m being honest, I really &lt;i&gt;do not care&lt;/i&gt; about whatever wrapping it all in Docker gets me.
I&amp;rsquo;m sure there are some capabilities a Docker runtime gets you, but a totally standalone executable that leans on basic container primitives to put some safety bars around it is what I actually need.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="outline-4" id="outline-container-the-builder"&gt;
&lt;h4 id="the-builder"&gt;The Builder&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-orgec7aedc"&gt;
&lt;p&gt;
You might stop reading here, and that&amp;rsquo;s okay, but give it a chance: my answer is &lt;a href="https://nixos.org/"&gt;nix&lt;/a&gt;.
Nix has a reputation for being opaque and inscrutable, and that&amp;rsquo;s not wrong.
But people still love to use it, and there&amp;rsquo;s a reason why people push through the pain.
&lt;/p&gt;

&lt;p&gt;
This is broken into three parts: &lt;b&gt;wrapping&lt;/b&gt;, my application, &lt;b&gt;building&lt;/b&gt; my application, and &lt;b&gt;packaging&lt;/b&gt; my application.
We&amp;rsquo;ll use a dumb python webserver as an example.
Assumptions: &lt;a href="https://nixos.org/"&gt;nix&lt;/a&gt; is working, and you have &lt;a href="https://github.com/nmattia/niv"&gt;niv&lt;/a&gt; as well.
&lt;/p&gt;
&lt;/div&gt;
&lt;div class="outline-5" id="outline-container-nix-gonna-nix"&gt;
&lt;h5 id="nix-gonna-nix"&gt;Nix Gonna Nix&lt;/h5&gt;
&lt;div class="outline-text-5" id="text-org95b412c"&gt;
&lt;p&gt;
We initialize a new project with &lt;code&gt;niv&lt;/code&gt;.
This pins our forthcoming &lt;code&gt;nix&lt;/code&gt; operations to a specific revision of the &lt;code&gt;nixpkgs&lt;/code&gt; package repository.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;mkdir demo&lt;/code&gt;
&lt;code&gt;&lt;span class="org-builtin"&gt;cd&lt;/span&gt; demo&lt;/code&gt;
&lt;code&gt;niv init&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Next we define a &lt;code&gt;shell.nix&lt;/code&gt;.
We throw in &lt;code&gt;nix-bundle&lt;/code&gt; for later.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;Nix&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-legend"&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="org-property-name"&gt;Font used to highlight properties of an object.
For example, the declaration of fields in a struct.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-function-call"&gt;Font used to highlight function calls.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-property-use"&gt;Font used to highlight property references.
For example, property lookup of fields in a struct.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-type"&gt;Font used to highlight type and class names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-constant"&gt;Font used to highlight constants and labels.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-keyword"&gt;Font used to highlight keywords.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-variable-name"&gt;Font used to highlight variable names.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-nix-ts"&gt;&lt;code&gt;&lt;code&gt;&lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;{&lt;/span&gt;&lt;/span&gt; &lt;span class="org-variable-name"&gt;sources&lt;/span&gt; &lt;span class="org-operator"&gt;?&lt;/span&gt; &lt;span class="org-keyword"&gt;import&lt;/span&gt; &lt;span class="org-constant"&gt;./nix/sources.nix&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-delimiter"&gt;,&lt;/span&gt; &lt;span class="org-variable-name"&gt;pkgs&lt;/span&gt; &lt;span class="org-operator"&gt;?&lt;/span&gt; &lt;span class="org-keyword"&gt;import&lt;/span&gt; &lt;span class="org-type"&gt;sources&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;nixpkgs&lt;/span&gt; &lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-2"&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="org-punctuation"&gt;:&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-type"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-function-call"&gt;mkShell&lt;/span&gt; &lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-property-name"&gt;buildInputs&lt;/span&gt; &lt;span class="org-operator"&gt;=&lt;/span&gt; &lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-2"&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;nix-bundle&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;python39&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;python39&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;poetry&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-2"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class="org-delimiter"&gt;;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You can drop into the shell environment this defines with &lt;code&gt;nix-shell&lt;/code&gt;, but I much prefer to use &lt;code&gt;direnv&lt;/code&gt; for this.
It&amp;rsquo;s what separates man from beast.
In &lt;code&gt;.envrc&lt;/code&gt;:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;Shell-script&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-bash-ts"&gt;&lt;code&gt;&lt;code&gt;source_up&lt;/code&gt;
&lt;code&gt;use nix&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Beautiful.
Now when you &lt;code&gt;cd&lt;/code&gt; into the directory, &lt;code&gt;nix&lt;/code&gt; sets up your environment for you.
I use &lt;code&gt;source_up&lt;/code&gt; because I have a general-purpose &lt;code&gt;.envrc&lt;/code&gt; in &lt;code class="src src-bash"&gt;$&lt;span class="org-variable-name"&gt;HOME&lt;/span&gt;&lt;/code&gt;.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;direnv allow&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example" id="org95aa926"&gt;
&lt;code&gt;&amp;lt;nix configures everything for you&amp;gt;&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Once nix concludes, you&amp;rsquo;ll have a version-pinned copy of python 3.9 and poetry available.
You &lt;i&gt;can&lt;/i&gt; lean on nix to pull in python dependencies, but I find poetry (as a native python tool) a little easier to manage python dependencies with.
Initialize poetry and install dependencies.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;poetry init&lt;/code&gt;
&lt;code&gt;poetry add flask&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
We have an eminently-reproducible python dev environment, and here&amp;rsquo;s the VC-backed $5M series A blockchain application we want to run:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;mkdir demo&lt;/code&gt;
&lt;code&gt;cat demo/__init__.py&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;Python&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-legend"&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="org-operator"&gt;Font used to highlight operators.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-function-name"&gt;Font used to highlight function names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-string"&gt;Font used to highlight strings.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-type"&gt;Font used to highlight type and class names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-builtin"&gt;Font used to highlight builtins.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-variable-name"&gt;Font used to highlight variable names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-keyword"&gt;Font used to highlight keywords.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-python"&gt;&lt;code&gt;&lt;code&gt;&lt;span class="org-keyword"&gt;from&lt;/span&gt; flask &lt;span class="org-keyword"&gt;import&lt;/span&gt; Flask&lt;/code&gt;
&lt;code&gt;&lt;span class="org-variable-name"&gt;app&lt;/span&gt; &lt;span class="org-operator"&gt;=&lt;/span&gt; Flask&lt;span class="org-rainbow-delimiters-depth-1"&gt;(&lt;/span&gt;&lt;span class="org-builtin"&gt;__name__&lt;/span&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;)&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-type"&gt;@app.route&lt;/span&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;(&lt;/span&gt;&lt;span class="org-string"&gt;'/'&lt;/span&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;)&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-keyword"&gt;def&lt;/span&gt; &lt;span class="org-function-name"&gt;index&lt;/span&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;()&lt;/span&gt;:&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-keyword"&gt;return&lt;/span&gt; &lt;span class="org-string"&gt;"I will require a $10M series B to write a blockchain-based pet taxi app."&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-keyword"&gt;def&lt;/span&gt; &lt;span class="org-function-name"&gt;main&lt;/span&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;()&lt;/span&gt;:&lt;/code&gt;
&lt;code&gt;    app.run&lt;span class="org-rainbow-delimiters-depth-1"&gt;()&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-keyword"&gt;if&lt;/span&gt; &lt;span class="org-builtin"&gt;__name__&lt;/span&gt; &lt;span class="org-operator"&gt;==&lt;/span&gt; &lt;span class="org-string"&gt;'__main__'&lt;/span&gt;:&lt;/code&gt;
&lt;code&gt;    main&lt;span class="org-rainbow-delimiters-depth-1"&gt;()&lt;/span&gt;&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This runs and is an incredible testament to our capabilities as a real full-stack developer:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;poetry run python demo/__init__.py_&lt;/code&gt;
&lt;code&gt;http --body :5000&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example" id="org487d0cc"&gt;
&lt;code&gt;I will require a $10M series B to write a blockchain-based pet taxi app.&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
At &lt;i&gt;this point&lt;/i&gt; we could commit the files here and, as long as a colleague has &lt;code&gt;nix&lt;/code&gt; and &lt;code&gt;niv&lt;/code&gt; working, get a carbon copy of the relevant requirements to replicate our environment.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="outline-5" id="outline-container-builders-build"&gt;
&lt;h5 id="builders-build"&gt;Builders Build&lt;/h5&gt;
&lt;div class="outline-text-5" id="text-org5d9abf5"&gt;
&lt;p&gt;
Okay: we know that &lt;code&gt;nix&lt;/code&gt; and &lt;code&gt;niv&lt;/code&gt; are pedantic about being Functional in the sense that we have expressed the &lt;i&gt;inputs&lt;/i&gt; required to make our program run.
Assuming we define the &lt;i&gt;total&lt;/i&gt; set of necessary dependencies for a working application, &lt;code&gt;nix&lt;/code&gt; should be able to take that output and capture it, right?
&lt;/p&gt;

&lt;p&gt;
Two things make this possible.
The first is &lt;a href="https://www.tweag.io/blog/2020-08-12-poetry2nix/"&gt;poetry2nix&lt;/a&gt;.
Because poetry itself pins specific revisions of dependencies that we&amp;rsquo;re relying on for our project, we can translate those specific, pinned python modules into something nix can work with.
&lt;/p&gt;

&lt;p&gt;
Here&amp;rsquo;s what we put in &lt;code&gt;default.nix&lt;/code&gt;:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;Nix&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-legend"&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="org-property-name"&gt;Font used to highlight properties of an object.
For example, the declaration of fields in a struct.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-function-call"&gt;Font used to highlight function calls.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-property-use"&gt;Font used to highlight property references.
For example, property lookup of fields in a struct.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-type"&gt;Font used to highlight type and class names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-constant"&gt;Font used to highlight constants and labels.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-keyword"&gt;Font used to highlight keywords.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-variable-name"&gt;Font used to highlight variable names.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-nix-ts"&gt;&lt;code&gt;&lt;code&gt;&lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;{&lt;/span&gt;&lt;/span&gt; &lt;span class="org-variable-name"&gt;sources&lt;/span&gt; &lt;span class="org-operator"&gt;?&lt;/span&gt; &lt;span class="org-keyword"&gt;import&lt;/span&gt; &lt;span class="org-constant"&gt;./nix/sources.nix&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-delimiter"&gt;,&lt;/span&gt; &lt;span class="org-variable-name"&gt;pkgs&lt;/span&gt; &lt;span class="org-operator"&gt;?&lt;/span&gt; &lt;span class="org-keyword"&gt;import&lt;/span&gt; &lt;span class="org-type"&gt;sources&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;nixpkgs&lt;/span&gt; &lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-2"&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="org-punctuation"&gt;:&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-type"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;poetry2nix&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-function-call"&gt;mkPoetryApplication&lt;/span&gt; &lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-property-name"&gt;projectDir&lt;/span&gt; &lt;span class="org-operator"&gt;=&lt;/span&gt; &lt;span class="org-constant"&gt;./.&lt;/span&gt;&lt;span class="org-delimiter"&gt;;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-property-name"&gt;python&lt;/span&gt; &lt;span class="org-operator"&gt;=&lt;/span&gt; &lt;span class="org-type"&gt;pkgs&lt;/span&gt;&lt;span class="org-delimiter"&gt;.&lt;/span&gt;&lt;span class="org-property-use"&gt;python39&lt;/span&gt;&lt;span class="org-delimiter"&gt;;&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-bracket"&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Again &lt;code&gt;niv&lt;/code&gt; lets us pin &lt;code&gt;nixpkgs&lt;/code&gt; to something predictable, and &lt;code&gt;mkPoetryApplication&lt;/code&gt; is a function we can invoke that parses poetry files.
Now drop a line into &lt;code&gt;pyproject.toml&lt;/code&gt; to tell poetry how to run our project as a script.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;TOML&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-legend"&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="org-string"&gt;Font used to highlight strings.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-property-use"&gt;Font used to highlight property references.
For example, property lookup of fields in a struct.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-type"&gt;Font used to highlight type and class names.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-toml-ts"&gt;&lt;code&gt;&lt;code&gt;&lt;span class="org-bracket"&gt;[&lt;/span&gt;&lt;span class="org-type"&gt;tool.poetry.scripts&lt;/span&gt;&lt;span class="org-bracket"&gt;]&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-property-use"&gt;app&lt;/span&gt; &lt;span class="org-delimiter"&gt;=&lt;/span&gt; &lt;span class="org-string"&gt;'demo:main'&lt;/span&gt;&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Building this derivation will kick out our script in a form that has all of its dependencies expressed in a &amp;ldquo;sandboxed&amp;rdquo; sort of way.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;nix build&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Behold, a nix-built python application:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;./result/bin/app&lt;/code&gt;
&lt;code&gt;http --body :5000&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example" id="orgb51ef63"&gt;
&lt;code&gt;I will require a $10M series B to write a blockchain-based pet taxi app.&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="outline-5" id="outline-container-artifacts-art"&gt;
&lt;h5 id="artifacts-art"&gt;Artifacts Art&lt;/h5&gt;
&lt;div class="outline-text-5" id="text-org932a1cd"&gt;
&lt;p&gt;
Here&amp;rsquo;s where things get a little more bleeding edge.
If you poke around &lt;code&gt;result&lt;/code&gt;, you&amp;rsquo;ll see how it works: nix has setup its maze of symlinks and &lt;code&gt;/nix/store&lt;/code&gt; files to define the &lt;i&gt;total&lt;/i&gt; set of nuts and bolts to make the application run.
It turns out that you can wrap all of that up into something deployable.
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://github.com/matthewbauer/nix-bundle"&gt;nix-bundle&lt;/a&gt; is a cool project that wraps up the output of a &lt;code&gt;nix&lt;/code&gt; function into a self-contained executable.
You can read the details on the GitHub page, but the tl;dr is that we can ask it to take our built application and bundle it into what you can sort of think of as a statically-built executable.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;nix-bundle &lt;span class="org-string"&gt;'(import ./default.nix {})'&lt;/span&gt; /bin/app&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The first argument tells &lt;code&gt;nix-bundle&lt;/code&gt; what to build - which is just our poetry2nix function - and the second is what it should run from the result of the build (basically, what to invoke inside of &lt;code&gt;result&lt;/code&gt; if it were a &lt;code&gt;chroot&lt;/code&gt;).
&lt;/p&gt;

&lt;p&gt;
Run what you&amp;rsquo;ve built!
Startup time isn&amp;rsquo;t instant, because the single-file artifact is a compressed archive, but it should run as a standalone program.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;./app&lt;/code&gt;
&lt;code&gt;http --body :5000&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example" id="orgcba4e42"&gt;
&lt;code&gt;I will require a $10M series B to write a blockchain-based pet taxi app.&lt;/code&gt;
&lt;/pre&gt;


&lt;div class="figure" id="orge40184d"&gt;
&lt;p&gt;&lt;img alt="very-cool.gif" class="mainline" src="../assets/images/very-cool.gif" /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class="figure-number"&gt;Figure 1: &lt;/span&gt;Very Cool. Very Cool.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
We&amp;rsquo;ve built repeatable python artifact and bundled it into a single-file and self-contained executable.
Very cool.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="outline-4" id="outline-container-deploy-boy"&gt;
&lt;h4 id="deploy-boy"&gt;Deploy Boy&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org03fc0fb"&gt;
&lt;p&gt;
At this point we can use this executable however we choose if we want to deploy it somewhere.
We&amp;rsquo;ve already digested the meat of this post, so here&amp;rsquo;s the extra dessert if you&amp;rsquo;re interested.
I upload my artifact to the minio cluster in my homelab:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;mc cp app lan/artifacts/app-0.1.0-x86_64&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now let&amp;rsquo;s write a lil&amp;rsquo; Nomad job definition to run it:
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;HCL&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-legend"&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="org-type"&gt;Font used to highlight type and class names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-variable-name"&gt;Font used to highlight variable names.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="org-string"&gt;Font used to highlight strings.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-hcl"&gt;&lt;code&gt;&lt;code&gt;job &lt;span class="org-string"&gt;"app"&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-1"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-variable-name"&gt;datacenters&lt;/span&gt; = &lt;span class="org-rainbow-delimiters-depth-2"&gt;[&lt;/span&gt;&lt;span class="org-string"&gt;"lan"&lt;/span&gt;&lt;span class="org-rainbow-delimiters-depth-2"&gt;]&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-variable-name"&gt;region&lt;/span&gt; = &lt;span class="org-string"&gt;"global"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-variable-name"&gt;type&lt;/span&gt; = &lt;span class="org-string"&gt;"service"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  task &lt;span class="org-string"&gt;"app"&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-2"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;artifact&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-3"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-variable-name"&gt;source&lt;/span&gt; = &lt;span class="org-string"&gt;"https://my.local.domain/artifacts/app-0.1.0-x86_64"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-type"&gt;options&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-4"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;        &lt;span class="org-variable-name"&gt;checksum&lt;/span&gt; = &lt;span class="org-string"&gt;"sha256:deadbeefc0ffee"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-rainbow-delimiters-depth-4"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-rainbow-delimiters-depth-3"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-variable-name"&gt;driver&lt;/span&gt; = &lt;span class="org-string"&gt;"exec"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;env&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-3"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-variable-name"&gt;PYTHONUNBUFFERED&lt;/span&gt; = &lt;span class="org-string"&gt;"yes"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-rainbow-delimiters-depth-3"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;config&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-3"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-variable-name"&gt;command&lt;/span&gt; = &lt;span class="org-string"&gt;"./local/app-0.1.0-x86_64"&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-rainbow-delimiters-depth-3"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-type"&gt;resources&lt;/span&gt; &lt;span class="org-rainbow-delimiters-depth-3"&gt;{&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-variable-name"&gt;cpu&lt;/span&gt;    = 500&lt;/code&gt;
&lt;code&gt;      &lt;span class="org-variable-name"&gt;memory&lt;/span&gt; = 512&lt;/code&gt;
&lt;code&gt;    &lt;span class="org-rainbow-delimiters-depth-3"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;  &lt;span class="org-rainbow-delimiters-depth-2"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;code&gt;&lt;span class="org-rainbow-delimiters-depth-1"&gt;}&lt;/span&gt;&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
All done.
&lt;/p&gt;

&lt;span class="org-src-tab"&gt;&lt;span&gt;shell&lt;/span&gt;&lt;/span&gt;&lt;div class="org-src-container"&gt;
&lt;pre class="src src-shell"&gt;&lt;code&gt;&lt;code&gt;nomad run app.nomad&lt;/code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
It&amp;rsquo;s a simplified example, but my slightly-more-fleshed out real-world deployment is running great.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="outline-4" id="outline-container-final-thoughts"&gt;
&lt;h4 id="final-thoughts"&gt;Final Thoughts&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-orgbd323c5"&gt;
&lt;p&gt;
I&amp;rsquo;ve covered these points already, but to summarize:
&lt;/p&gt;

&lt;p&gt;
Nix has a &lt;i&gt;tremendous&lt;/i&gt; amount of utility in a few different ways.
A reproducible and consistent development environment.
A way to build and package an application for consistent &lt;i&gt;outputs&lt;/i&gt;.
Perhaps the most important point is that this is generally &lt;i&gt;language agnostic&lt;/i&gt;.
My example was python, but there are &lt;i&gt;lots&lt;/i&gt; of similar projects!
Go?
Use &lt;a href="https://github.com/kamilchm/go2nix"&gt;go2nix&lt;/a&gt;.
Haskell?
&lt;a href="https://github.com/NixOS/cabal2nix"&gt;cabal2nix&lt;/a&gt;.
Each variant turns an ordinary project into something that can leverage the utility of the Nix ecosystem.
(nix-bundle is the example, here but are other cool services like &lt;a href="https://cachix.org/"&gt;cachix&lt;/a&gt;)
&lt;/p&gt;

&lt;p&gt;
There are interesting options out there for building deployment artifacts.
I doubt Docker images are going anywhere, but I know that I&amp;rsquo;ve been envious of go&amp;rsquo;s ability to kick out easily portable executables, and general-purpose wrapping like this is pretty nice.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><author>Tyblog</author><pubDate>Thu, 10 Jun 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.tjll.net/container-scheduling-without-containers/</guid></item><item><title>You can’t create in a bad environment</title><link>https://ciesie.com/post/environments/</link><description>&lt;p&gt;Recently I haven’t been as productive as I’d like. Sure, the pandemic took its toll.
The summer is coming and if there is a time to consume what life has to offer,
instead of producing what you have to offer, this is it. Productivity should
never be the goal in itself.&lt;/p&gt;
&lt;p&gt;Nevertheless, I could be better at focusing on what’s important. Significant amount
of time gets wasted. It’s rarely caused by an unexpected, huge time sinks. It’s
usually small things - one YouTube video here, one Twitter doom scrolling there.
It’s not about replacing all those moments with work. Your time isn’t something
you have to trade for money or self improvement, but it is something you should
trade for positive/beneficial things. Before you buy something, you usually ask
yourself “is this worth my money?”. The more important question is “is this worth my time?”.&lt;/p&gt;</description><author>ciesie.com</author><pubDate>Thu, 10 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://ciesie.com/post/environments/</guid></item><item><title>Dreaming solutions</title><link>https://learnbyexample.github.io/mini/dreaming-solutions/</link><description>&lt;p&gt;&lt;a href="https://stackoverflow.com/q/67886163/4082052"&gt;This SO question&lt;/a&gt; was interesting and had various approaches to solve it. Here's a sample example to explain the problem to be solved:&lt;/p&gt;
&lt;pre class="language-ruby " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-ruby"&gt;&lt;span&gt;$ cat ip.txt
&lt;/span&gt;&lt;span&gt;caller_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;034082394234324&lt;/span&gt;&lt;span&gt;, clear_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;33335345435&lt;/span&gt;&lt;span&gt;,  direction=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;,
&lt;/span&gt;&lt;span&gt;caller_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;83479234234&lt;/span&gt;&lt;span&gt;,     clear_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;34836424733&lt;/span&gt;&lt;span&gt;, direction=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;2&lt;/span&gt;&lt;span&gt;,
&lt;/span&gt;&lt;span&gt;caller_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;83479234234&lt;/span&gt;&lt;span&gt;,     clear_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;64237384533&lt;/span&gt;&lt;span&gt;, direction=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;2&lt;/span&gt;&lt;span&gt;,
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;$ cat list.txt
&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;642
&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;3333
&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;534234235
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;$ cat op.txt
&lt;/span&gt;&lt;span&gt;caller_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;83479234234&lt;/span&gt;&lt;span&gt;,     clear_number=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;64237384533&lt;/span&gt;&lt;span&gt;, direction=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;2&lt;/span&gt;&lt;span&gt;,
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any data present in &lt;code&gt;list.txt&lt;/code&gt; has to be matched immediately after &lt;code&gt;clear_number=&lt;/code&gt; and the input line should also have &lt;code&gt;direction=2,&lt;/code&gt;. In the sample above, first line matches &lt;code&gt;3333&lt;/code&gt; but not the second criteria. The second line fails even though it has &lt;code&gt;642&lt;/code&gt; since it is not immediately after &lt;code&gt;clear_number=&lt;/code&gt;. The &lt;code&gt;list.txt&lt;/code&gt; file can have 10K-50K lines and &lt;code&gt;ip.txt&lt;/code&gt; is around 10GB.&lt;/p&gt;
&lt;p&gt;Here's a slightly modified answer based on existing solutions on that thread. Since the data present in &lt;code&gt;list.txt&lt;/code&gt; has to be partially matched after &lt;code&gt;clear_number=&lt;/code&gt;, a &lt;em&gt;single&lt;/em&gt; direct comparison with the keys saved in &lt;code&gt;arr&lt;/code&gt; is not possible. This solution loops over all the keys for every input line that matches the &lt;code&gt;direction=2,&lt;/code&gt; criteria (breaks the loop if a match is found early).&lt;/p&gt;
&lt;pre class="language-awk " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-awk"&gt;&lt;span style="color: #5597d6;"&gt;FNR&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;==&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;NR&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;arr&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;[&lt;/span&gt;&lt;span style="color: #d07711;"&gt;&amp;quot;=&amp;quot; &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;$0&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;]&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;next&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;$3&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;==&lt;/span&gt;&lt;span style="color: #d07711;"&gt;&amp;quot;direction=2,&amp;quot;&lt;/span&gt;&lt;span&gt;{
&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;for&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;i &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;in &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;arr&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span style="color: #c23f31;"&gt;if&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #b39f04;"&gt;index&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;$2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;i&lt;/span&gt;&lt;span&gt;)){
&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;print
&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;next
&lt;/span&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt="info" src="/images/info.svg" /&gt; &lt;em&gt;To run the solutions, use &lt;code&gt;mawk -f script.awk list.txt ip.txt&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In my dreams that night&lt;/strong&gt;, I realized that the solution can be improved drastically by looping over the digits after &lt;code&gt;clear_number=&lt;/code&gt; instead of looping over keys saved in &lt;code&gt;arr&lt;/code&gt;. Matching a key is &lt;code&gt;O(1)&lt;/code&gt;, so the time saving is huge since the inner loop is now a maximum of 12 (length of digits after &lt;code&gt;clear_number=&lt;/code&gt;) instead of looping a maximum of 10K-50K times! With a 35M sample input file and 12K keys that I created for testing, I found this solution to be about &lt;strong&gt;200&lt;/strong&gt; times faster.&lt;/p&gt;
&lt;pre class="language-awk " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-awk"&gt;&lt;span style="color: #5597d6;"&gt;FNR&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;==&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;NR&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;arr&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;[&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;$0&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;]&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;next&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;$3&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;==&lt;/span&gt;&lt;span style="color: #d07711;"&gt;&amp;quot;direction=2,&amp;quot;&lt;/span&gt;&lt;span&gt;{
&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;val&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;=&lt;/span&gt;&lt;span style="color: #b39f04;"&gt;substr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;$2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;14&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span style="color: #c23f31;"&gt;for&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;=&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #b39f04;"&gt;length&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;val&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;i&lt;/span&gt;&lt;span style="color: #72ab00;"&gt;++&lt;/span&gt;&lt;span&gt;)
&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span style="color: #c23f31;"&gt;if&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #b39f04;"&gt;substr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;val&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #b3933a;"&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style="color: #5597d6;"&gt;i&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;in &lt;/span&gt;&lt;span style="color: #5597d6;"&gt;arr&lt;/span&gt;&lt;span&gt;){
&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;print
&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;next
&lt;/span&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><author>learnbyexample</author><pubDate>Thu, 10 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/mini/dreaming-solutions/</guid></item><item><title>Can you Spot an Underfunded Technology Department?</title><link>https://www.danstroot.com/posts/2021-06-10-can-you-spot-an-underfunded-technology-dept</link><description>&lt;img alt="post image" src="https://danstroot.imgix.net/assets/blog/img/old-computer.jpeg" /&gt;&lt;br /&gt;&lt;br /&gt;People go into technology because they love building new things, solving business problems, and making businesses operate better. The last thing on earth most technologists want to say is “no”.  When your technology team starts telling you no it may be that they are feeling overwhelmed. There are many unintuitive signs that indicate a technology team might be overwhelmed or underfunded, such as buying premium support contracts, or using expensive consultants. Read on to learn the telltale signs of an underfunded technology organization.&lt;br /&gt;&lt;br /&gt;This post &lt;a href="https://www.danstroot.com/posts/2021-06-10-can-you-spot-an-underfunded-technology-dept"&gt;Can you Spot an Underfunded Technology Department?&lt;/a&gt; first appeared on &lt;a href="https://www.danstroot.com"&gt;Dan Stroot's Blog&lt;/a&gt;</description><author>Dan Stroot</author><pubDate>Thu, 10 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.danstroot.com/posts/2021-06-10-can-you-spot-an-underfunded-technology-dept</guid></item><item><title>Software will extinguish cars</title><link>https://stop.zona-m.net/2021/06/software-will-extinguish-cars/</link><description>&lt;p&gt;If they continue to be made outside reality, that is.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 10 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/software-will-extinguish-cars/</guid></item><item><title>Nullish coalescing in JavaScript (??)</title><link>https://duncanlock.net/blog/2021/06/09/nullish-coalescing-in-javascript/</link><description>&lt;section id="preamble"&gt;&lt;p&gt;My new favourite thing in &lt;span class="caps"&gt;JS&lt;/span&gt; is Nullish&amp;nbsp;coalescing:&lt;/p&gt;
&lt;div class="quote-block"&gt;&lt;blockquote&gt;&lt;p&gt;The nullish coalescing operator (&lt;code&gt;??&lt;/code&gt;) is a logical operator that returns its right-hand side operand when its left-hand side operand is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, and otherwise returns its left-hand side&amp;nbsp;operand.&lt;/p&gt;&lt;footer&gt;&amp;#8212; &lt;cite&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator"&gt;Nullish Coalescing Operator&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;&lt;/div&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thing1&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thing2&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also works well with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining"&gt;Optional Chaining Operator&lt;/a&gt;, which is also great - not more &amp;#8220;Uncaught TypeError: Cannot read property &amp;#8216;thing1&amp;#8217; of&amp;nbsp;undefined&amp;#8221;:&lt;/p&gt;
&lt;div class="listing-block"&gt;&lt;pre class="rouge highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;things&lt;/span&gt;&lt;span class="p"&gt;.?.&lt;/span&gt;&lt;span class="nx"&gt;thing1&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;things&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;thing2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is like logical or - &lt;code&gt;||&lt;/code&gt; - but instead of testing for &lt;code&gt;truthy&lt;/code&gt; &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; &lt;code&gt;falsy&lt;/code&gt;, it tests for &lt;code&gt;nullish&lt;/code&gt; - which is often what you&amp;nbsp;want.&lt;/p&gt;
&lt;p&gt;Nullish all the&amp;nbsp;things!&lt;/p&gt;&lt;/section&gt;
&lt;section class="doc-section level-1"&gt;&lt;h2 id="_references"&gt;References&lt;/h2&gt;&lt;div class="ulist"&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator"&gt;Nullish Coalescing&amp;nbsp;Operator&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy"&gt;Falsy&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/section&gt;</description><author>duncan­lock­.net</author><pubDate>Wed, 09 Jun 2021 21:31:39 GMT</pubDate><guid isPermaLink="true">https://duncanlock.net/blog/2021/06/09/nullish-coalescing-in-javascript/</guid></item><item><title>A Project of One's Own</title><link>https://heidenstedt.org/links/a-project-of-ones-own/</link><description>&lt;p&gt;
      &lt;em&gt;Best viewed on the &lt;a href="https://heidenstedt.org/links/a-project-of-ones-own/"&gt;original page&lt;/a&gt;, where extended functionality like the
    footnote helper is available.&lt;/em&gt;
    &lt;/p&gt;&lt;p&gt;&lt;a href="http://paulgraham.com/own.html"&gt;A Project of One&amp;rsquo;s Own&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;TLDR:&lt;/p&gt;
&lt;p&gt;The article, titled &amp;ldquo;Own&amp;rdquo;, is written by Paul Graham and was published in June 2021. It discusses the concept of working on a project of one&amp;rsquo;s own and how it differs from ordinary work. Here are the key points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Working on a project of one&amp;rsquo;s own is compared to skating, which is more fun and productive than ordinary work (walking). This kind of work is often exciting and engaging, even though it may not always lead to happiness due to the challenges and problems encountered.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The author argues that a significant proportion of great work has been done by people who were &amp;ldquo;skating&amp;rdquo; in this sense. He emphasizes the importance of autonomy and the sense of ownership in work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The article also discusses the disconnect between the work children do (like building a treehouse) and the work adults do. It criticizes the school system for not making it clear to children that their playful projects could lead to serious work in adulthood.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The author prefers his children to work on ambitious projects of their own rather than focusing on getting good grades. He argues that such projects have more predictive value for future success.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The article also explores the concept of collaboration and how it fits into the idea of owning a project. It suggests that collaboration can either involve sharing a single project or working on separate projects that fit together.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The author concludes by encouraging adults to recapture the carefree confidence they had as children when starting new projects. He suggests that adults can have the best of both worlds: being deliberate in choosing to work on projects of their own and carelessly confident in starting new ones.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description><author>Mia Heidenstedt</author><pubDate>Wed, 09 Jun 2021 08:10:37 GMT</pubDate><guid isPermaLink="true">https://heidenstedt.org/links/a-project-of-ones-own/</guid></item><item><title>Copyright madness, barbecued</title><link>https://stop.zona-m.net/2021/06/copyright-madness-barbecued/</link><description>&lt;p&gt;Forget &amp;ldquo;rare, well done or medium&amp;rdquo;: &amp;ldquo;CRAZY done&amp;rdquo; is the new barbecue standard.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 09 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/copyright-madness-barbecued/</guid></item><item><title>My tennis racquets, a trip through time</title><link>https://www.bajkowski.com/my-tennis-racquets-trip-through-time/</link><description>A trip down memory lane through the various tennis racquets that I've used over the last 30 years</description><author>Bajkowski.com</author><pubDate>Wed, 09 Jun 2021 01:43:57 GMT</pubDate><guid isPermaLink="true">https://www.bajkowski.com/my-tennis-racquets-trip-through-time/</guid></item><item><title>Ask HN: Favorite Blogs by Individuals?</title><link>https://heidenstedt.org/links/ask-hn-personal-blogs/</link><description>&lt;p&gt;
      &lt;em&gt;Best viewed on the &lt;a href="https://heidenstedt.org/links/ask-hn-personal-blogs/"&gt;original page&lt;/a&gt;, where extended functionality like the
    footnote helper is available.&lt;/em&gt;
    &lt;/p&gt;&lt;p&gt;&lt;a href="https://news.ycombinator.com/item?id=27302195"&gt;Ask HN: Favorite Blogs by Individuals?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;TLDR:&lt;/p&gt;
&lt;p&gt;The discussion on the provided Hacker News link revolves around various topics, primarily focusing on the appreciation of a historian&amp;rsquo;s blog that frequently appears on Hacker News. The blog is praised for its detailed and technical descriptions of mechanical processes, which some users liken to the work of a programmer. The author&amp;rsquo;s approach to discussing topics such as archery, which includes elements of physics, empirical studies, historical anecdotes, and comparisons to modern video games and TV, is also appreciated.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a discussion about a blog post on medieval medicine, which is praised for its insightful exploration of the topic. Some users express their appreciation for the writer&amp;rsquo;s style, which includes the use of parentheses, italics, and bolding to clarify the structure of thoughts.&lt;/p&gt;
&lt;p&gt;The thread also includes a discussion about a blog called &amp;ldquo;A half-hour to learn Rust,&amp;rdquo; which is recommended for those interested in learning Rust with a good programming background but no specific Rust knowledge.&lt;/p&gt;
&lt;p&gt;There are also mentions of other blogs and resources, such as diff.blog, a platform for discovering new developer blogs, and a discussion about the cost of domain names. Some users share their favorite blogs and discuss the benefits of using an RSS feed reader.&lt;/p&gt;</description><author>Mia Heidenstedt</author><pubDate>Wed, 09 Jun 2021 00:50:00 GMT</pubDate><guid isPermaLink="true">https://heidenstedt.org/links/ask-hn-personal-blogs/</guid></item><item><title>Learn With Me: Julia - Bitwise Operators (#4)</title><link>https://geekmonkey.org/lwm-julia-bitwise-operators/</link><description>This articles covers bitwise operators in Julia and uses them to implement a CRC32 algorithm to validate PNG data chunks.</description><author>geekmonkey</author><pubDate>Tue, 08 Jun 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/lwm-julia-bitwise-operators/</guid></item><item><title>Plum Tree Cottage</title><link>https://one.mikro2nd.net/pages/cottage/</link><description>Plum Tree Cottage The cottage nestles among the trees &amp;mdash; plums of several varieties, lemon, apricot and indigenous trees and bushes in wild profusion &amp;mdash; well away from the main house, in cosy seclusion and peace. Perfect for nature lovers who enjoy a quiet, simple life.
The main part of the cottage consists of a bedroom (double bed, cupboards), en suite toilet/shower and a kitchen/lounge area, fully cupboarded and includes stove, refrigerator, cookware, crockery and cutlery.</description><author>one mikro2nd</author><pubDate>Tue, 08 Jun 2021 16:27:46 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/cottage/</guid></item><item><title>Bookmarklet - stay here</title><link>https://river.me/blog/bookmarklet-stay-here/</link><description>A bookmarklet that enables a prompt before navigating away from your current location</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 08 Jun 2021 14:38:23 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/bookmarklet-stay-here/</guid></item><item><title>Summer</title><link>https://blog.herlein.com/post/summer/</link><description>&lt;p&gt;Where am I? Summer!&lt;/p&gt;</description><author>Greg Herlein</author><pubDate>Tue, 08 Jun 2021 11:00:01 GMT</pubDate><guid isPermaLink="true">https://blog.herlein.com/post/summer/</guid></item><item><title>Sergeant Hartmann chips are coming</title><link>https://stop.zona-m.net/2021/06/sergeant-hartmann-chips-are-coming/</link><description>&lt;p&gt;And they will motivate you, Pvt Pyle!!! (cit.)&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 08 Jun 2021 02:59:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/sergeant-hartmann-chips-are-coming/</guid></item><item><title>The Quickest Antenna Design of the Year</title><link>https://shielddigitaldesign.com/posts/2021/fast-antenna/</link><description>&lt;p&gt;
During a recent weekend, I found myself with some parts laying around which I
hadn't used in quite a while. First in the bin was an old Orange Pi
One&lt;sup class="footnote-reference"&gt;&lt;a href="#footnote-1" id="footnote-reference-1"&gt;1&lt;/a&gt;&lt;/sup&gt; (an underpowered SBC similar to the Raspberry Pi) which I
was going to use to run a video conferencing screen but which proved
to be unable to run the graphical Linux installation well enough to be
usable. My &lt;span class="glossary-term" tabindex="0" title="A very cheap software-defined radio (SDR) based on DVB-T TV tuner dongles that use the Realtek RTL2832U chip."&gt;
    RTL-SDR
&lt;/span&gt;

 stick also happened to be out (I can't
actually remember why I grabbed it a few weeks ago for the first time
in years). As I cleaned everything up and got ready to put it away, I
realized that I was probably never going to use this stuff again, so
instead of trashing it I decided to put it to good use!&lt;/p&gt;</description><author>Shield Digital Design</author><pubDate>Mon, 07 Jun 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://shielddigitaldesign.com/posts/2021/fast-antenna/</guid></item><item><title>UXLondon 2021</title><link>http://evantravers.com/articles/2021/06/07/uxlondon-2021/</link><description>&lt;p&gt;My team and I were able to virtually attend UXLondon 2021 last week. The team at ClearLeft did a &lt;em&gt;fantastic&lt;/em&gt; job on the event. I thought it was well designed as a 100% virtual and intentionally danced between the poles of awkward and advantage. I thought the interview segments after the talks were inspired and kept a sense of engagement and momentum, and the Miro board was a great way to make people feel like they were sharing notes and insights from the discussion.&lt;/p&gt;
&lt;p&gt;There was a through-line of ethics through the whole conference that I enjoyed. The &amp;quot;design is the underdog&amp;quot; narrative is tired and no longer true. I think that asking ourselves &amp;quot;now that we are here, how do we avoid causing harm?&amp;quot; is a much more mature conversation.&lt;/p&gt;
&lt;p&gt;I did make some sketchnotes… but I was utterly humbled by the &lt;a href="https://twitter.com/uxlondon/status/1400539380289347584?s=21"&gt;gorgeous work&lt;/a&gt; of &lt;a href="https://www.instagram.com/zldrawings/"&gt;Zsofi Lang&lt;/a&gt;… which I ended up aping. 🤷 I think the monochrome strategy will be a permanent addition to my toolbox.&lt;/p&gt;
&lt;p&gt;I need to go back and rewatch some of the sessions, but here is a sampling of sketch notes with my main takeaway:&lt;/p&gt;
&lt;h3&gt;&lt;a class="anchor" href="#continuous-discovery-by-theresa-torres" id="continuous-discovery-by-theresa-torres"&gt;&lt;/a&gt;Continuous Discovery by &lt;a href="https://www.producttalk.org/"&gt;Theresa Torres&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="continuous discovery" src="https://images.evantravers.com/articles/2021/06/continuousdiscovery.jpg" /&gt;
  &lt;figcaption&gt;Interview to discover, not to confirm.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;&lt;a class="anchor" href="#killing-products-by-amy-zima" id="killing-products-by-amy-zima"&gt;&lt;/a&gt;Killing Products by &lt;a href="https://amyzima.com/"&gt;Amy Zima&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="killing products" src="https://images.evantravers.com/articles/2021/06/killingproducts.jpg" /&gt;
  &lt;figcaption&gt;Think of products as portfolios of features. Prune your portfolio.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;&lt;a class="anchor" href="#already-a-researcher-by-gregg-bernstein" id="already-a-researcher-by-gregg-bernstein"&gt;&lt;/a&gt;Already a Researcher by &lt;a href="http://gregg.io/"&gt;Gregg Bernstein&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="already a researcher" src="https://images.evantravers.com/articles/2021/06/alreadyaresearcher.jpg" /&gt;
  &lt;figcaption&gt;Everyone can research… so make it easy for everyone.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;&lt;a class="anchor" href="#designing-for-cognitive-bias-by-david-dylan-thomas" id="designing-for-cognitive-bias-by-david-dylan-thomas"&gt;&lt;/a&gt;Designing for Cognitive Bias by &lt;a href="https://www.daviddylanthomas.com/"&gt;David Dylan Thomas&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="cognitive bias" src="https://images.evantravers.com/articles/2021/06/cognitivebias.jpg" /&gt;
  &lt;figcaption&gt;Framing bias is a tool for good and evil.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;&lt;a class="anchor" href="#how-to-get-wicked-good-by-karen-vanhouten" id="how-to-get-wicked-good-by-karen-vanhouten"&gt;&lt;/a&gt;How to Get Wicked Good by &lt;a href="https://karenvanhouten.com/"&gt;Karen VanHouten&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="how to get wicked good" src="https://images.evantravers.com/articles/2021/06/wickedgood.jpg" /&gt;
  &lt;figcaption&gt;Endurance is to be trained. Tools are gear.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;&lt;a class="anchor" href="#better-onboarding-by-krystal-higgins" id="better-onboarding-by-krystal-higgins"&gt;&lt;/a&gt;Better Onboarding by &lt;a href="https://www.kryshiggins.com/"&gt;Krystal Higgins&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="better onboarding" src="https://images.evantravers.com/articles/2021/06/betteronboarding.jpg" /&gt;
  &lt;figcaption&gt;It's the whole journey until the user achieves the main goal of the product.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;&lt;a class="anchor" href="#practical-growth-design-by-molly-norris-walker" id="practical-growth-design-by-molly-norris-walker"&gt;&lt;/a&gt;Practical Growth Design by &lt;a href="https://www.norris-walker.design/"&gt;Molly Norris Walker&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt="practical growth design" src="https://images.evantravers.com/articles/2021/06/practicalgrowthdesign.jpg" /&gt;
  &lt;figcaption&gt;Optimize low-barriers to action before high-barriers to action to move the user along the value path.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;It was a great event, and I look forward to the Festival throughout the rest of the month!&lt;/p&gt;</description><author>trv.rs</author><pubDate>Mon, 07 Jun 2021 14:25:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/06/07/uxlondon-2021/</guid></item><item><title>Running ECS on CIS Hardened Amazon Linux</title><link>https://yehudacohen.substack.com/p/running-ecs-on-cis-hardened-amazon</link><description>A journey deep into awsvpc container networking...</description><author>Fun With The Cloud</author><pubDate>Mon, 07 Jun 2021 08:02:11 GMT</pubDate><guid isPermaLink="true">https://yehudacohen.substack.com/p/running-ecs-on-cis-hardened-amazon</guid></item><item><title>Short: Retrofuturetrospectives</title><link>https://sonnet.io/posts/hot-air-balloon/</link><description>&lt;p&gt;I'm a big fan of &lt;a href="https://www.funretrospectives.com/"&gt;Fun retros&lt;/a&gt;, and I like smuggling my doodles into my work*.&lt;/p&gt;
&lt;p&gt;If your retrospectives feel a bit repetitive or you're working with a team you don't know well and want to learn a bit more about them before you hit the ground, check out &lt;a href="https://www.funretrospectives.com/hot-air-balloon-bad-weather/"&gt;Hot-air Balloon, Bad Weather&lt;/a&gt;. It's quite versatile and works well with remote setups.&lt;/p&gt;
&lt;p&gt;And, feel free to use the template I made!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sonnet.io/images/hot-air-balloon/borders.png" target="_blank"&gt;Version A (borders)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sonnet.io/images/hot-air-balloon/no_borders.png" target="_blank"&gt;Version B (plain)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It should work fine as a background for Mural, Miro, Figma Jam board or whatever is your jam (fig's mine).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Retro template" src="https://sonnet.io/images/hot-air-balloon/preview.png" /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;* There's still a bunch of businesses in the Square Mile with ponies hidden in their documentation. The more JIRA you use, the more ponies you get.&lt;/p&gt;
&lt;p&gt;PS. Speaking of facilitation materials, if you're looking for something less developer-oriented check out the &lt;a href="https://toolbox.hyperisland.com/"&gt;Hyper Island Toolbox&lt;/a&gt;.&lt;/p&gt;</description><author>Rafał Pastuszak</author><pubDate>Mon, 07 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://sonnet.io/posts/hot-air-balloon/</guid></item><item><title>Useful bootstrap checklist for Go Projects</title><link>https://boyter.org/posts/useful-bootstrap-for-go-projects/</link><description>&lt;p&gt;Mostly here because I keep forgetting all the things I want to put into a Go project when I start it. Collected into a list of things I actually need to do, tools to install and how to get things working.&lt;/p&gt;
&lt;p&gt;This page is a work in progress and something I will add to as I remember things or discover something new.&lt;/p&gt;
&lt;h3 id="static-analysis"&gt;Static Analysis&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://golangci-lint.run/usage/install/"&gt;https://golangci-lint.run/usage/install/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Install that and run &lt;code&gt;golangci-lint run ./...&lt;/code&gt; from the root when checking in.&lt;/p&gt;</description><author>Ben E. C. Boyter</author><pubDate>Mon, 07 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://boyter.org/posts/useful-bootstrap-for-go-projects/</guid></item><item><title>Bottom-Up Idea Exploration</title><link>https://www.swyx.io/bottom-up-ideas</link><description>My own content creation philosophy, explored through my own personal story with React</description><author>swyx's site RSS Feed</author><pubDate>Sun, 06 Jun 2021 19:42:16 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/bottom-up-ideas</guid></item><item><title>Webinar Recording: 6 Common SI/PI Issues Lurking in Your Design And How to Prevent Them</title><link>https://shielddigitaldesign.com/posts/2021/ema-webinar-sipi-problems/</link><description>&lt;p&gt;
I recently presented a webinar with EMA Design Automation to explain
some common signal and power integrity problems that I've
encountered. All of these issues are ones which I've dealt with in the
past, and each one shows how ECAD tools can be used to find and fix
them before sending a design out for manufacturing. Specifically, each
example uses the new Sigrity Aurora analysis features embedded
directly within the Cadence PCB Editor, which reduces a lot of the
back-and-forth design/analyze/fix cycle that often occurs. &lt;/p&gt;</description><author>Shield Digital Design</author><pubDate>Sun, 06 Jun 2021 19:00:00 GMT</pubDate><guid isPermaLink="true">https://shielddigitaldesign.com/posts/2021/ema-webinar-sipi-problems/</guid></item><item><title>Emacs org block completion on melpa</title><link>https://xenodium.com/emacs-org-block-completion-on-melpa</link><description>&lt;p&gt;When enabled, the character &amp;quot;&amp;lt;&amp;quot; triggers company completion of org blocks.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-org-block-completion-on-melpa/hello-company-org-block.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I get the occasional ping to package the &lt;a href="https://xenodium.com/emacs-org-block-company-completion"&gt;code from this post&lt;/a&gt; and publish it &lt;a href="https://melpa.org/#/company-org-block"&gt;on melpa&lt;/a&gt;. Finally gave it a go. Moved the code &lt;a href="https://github.com/xenodium/company-org-block"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This was my first time publishing on melpa. The process was very &lt;a href="https://github.com/melpa/melpa/pull/7593"&gt;smooth&lt;/a&gt;. Big thanks to melpa volunteers!&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 06 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/emacs-org-block-completion-on-melpa</guid></item><item><title>Business Urban Legends</title><link>/communication/2021/06/business_urban_legends.html</link><description>&lt;p&gt;There are a few business urban legends I’ve heard over the years that I try to trace down. It’s interesting to me because some seem simple to source, but end up being not so simple. I try to find the source for Drucker’s quote “culture eats strategy for breakfast” and a more esoteric one about chimpanzees forming a behavior to stop ladder climbers. I feel like there’s lots of these but, for now, it’s just these two and hope that these notes help save time for future citation searchers.&lt;/p&gt;

&lt;h2 id="trying-to-avoid-passing-on-rumors"&gt;Trying to avoid passing on rumors&lt;/h2&gt;

&lt;p&gt;I find that I’m frequently trying to translate concepts and ideas between different groups of people, using metaphors and concepts that both groups can understand and get us closer to a shared idea. However, in trying to get to shared understanding, I don’t want to accidentally insert some fake stuff in that shared reality. A common understanding that includes a lie or inaccuracy can end up taking up lots of time figuring out and fixing. So as much as possible, I like to cite the source for facts and ideas as I think it helps to both encourage better discussion and understanding and to serve as a check on my natural laziness &lt;sup id="fnref:programmer_quadrant"&gt;&lt;a class="footnote" href="#fn:programmer_quadrant" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt; to prevent me from wanting to believe something is true by forcing me to track down and find the citation.&lt;/p&gt;

&lt;p&gt;It’s fun to talk about urban legends, but in a professional setting spending time on business urban legends is better spent on working on whatever we’re supposed to work on.&lt;/p&gt;

&lt;p&gt;There are two particular ideas that I’ve wanted to use, but never easily been able to find the actual source:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;“Culture eats strategy for breakfast,” attributed to Peter Drucker, is great for trying to convey how important it is to work on organizational culture because if the people in an org have shared values, vision, and practices that will work better than any particular strategy or single idea, especially if it goes against culture.&lt;/li&gt;
  &lt;li&gt;Groupthinking chimpanzees stopping each other from climbing a ladder. I have no idea where this comes from, but is great to explain systems where unexplained behaviors exist and no one knows the source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I expect most people know a bit about &lt;a href="https://en.wikipedia.org/wiki/Peter_Drucker"&gt;Drucker&lt;/a&gt; so I’ll talk about chimpanzees first.&lt;/p&gt;

&lt;h2 id="chimpanzees"&gt;Chimpanzees&lt;/h2&gt;

&lt;p&gt;The chimpanzee story goes like this…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There’s an experiment where a scientist set up a room that had 10 chimpanzees. He set up the room with two way mirrors so he could see in, but the chimps couldn’t see out.&lt;/li&gt;
  &lt;li&gt;He added a ladder in the center with a bucket of bananas at the top.&lt;/li&gt;
  &lt;li&gt;When a single chimp tries to climb the ladder, the scientist would toss freezing water onto all the chimps until they stopped trying to climb. Eventually all the chimps stopped bothering because any attempt to get the bananas would result in a freezing bath.&lt;/li&gt;
  &lt;li&gt;The scientist would replace one chimp so there were 9 chimps who had been frozen so didn’t bother and one new chimp. The new chimp would try to get the bananas and get cold water and then stop.&lt;/li&gt;
  &lt;li&gt;The scientist kept rotating chimps and the exiting chimps would stop the new chimp from trying as they didn’t like the freezing water either. So it got to the point where new chimps never even got frozen, because they would be stopped before they got far enough to trigger freezing water.&lt;/li&gt;
  &lt;li&gt;A new chimp would come in, get clobbered by the others, and eventually stop trying to get to the ladder. And they would join the pack in stopping the next new chimps.&lt;/li&gt;
  &lt;li&gt;As chimps got replaced, eventually there were no chimps who ever got the frozen water, but they all had the behavior ingrained in them to stop any attempts up the ladder. But they didn’t have a real reason why, they just had the group behavior without the causative reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t think this is real, but I want to use it so much because there are many processes where we can get stuck in a circular reasoning loop of “I do it because it’s always been done. It’s always been done this way because we do it this way.”&lt;/p&gt;

&lt;p&gt;Of course, using first principles analysis helps to break out of this loop, but it’s such a nice story to illustrate the potential history of how we can get stuck in these situations.&lt;/p&gt;

&lt;p&gt;I wanted to use this in a speech, and spent a few hours trying to find the source and never did. Not only can I not find the experiment, I can’t even find the person to first tell the story. I hate not being able to give credit to who invented stuff.&lt;/p&gt;

&lt;p&gt;For example, here’s a cartoon version of the story I found on &lt;a href="https://skeptics.stackexchange.com/questions/6828/was-the-experiment-with-five-monkeys-a-ladder-a-banana-and-a-water-spray-condu"&gt;Skeptics StackExchange&lt;/a&gt; but I’m not sure they created the image, they were just asking about it. And a &lt;a href="https://www.google.com/search?tbs=sbi:AMhZZiuoRIlqzmpNXfAD9aeLCZ19SVo6EemLBzRKiJbj2Ttmtwt8XUV6_1F5MQ5aEPk94pfooDYCf38WK0nuW-5_1JzBbjezWT7cZnt6ehkWDC5CLqLYZTgnA3G-W22pD6SeSAygJuo-ySXjdfh-q28Wfxzp1xLsItnsK5PmHUf7z0kgUH7T_1MmE_1HZtDyBsLT3OCFNCavN1dYq3lhrqRyif1_1IajuBSPCDR3mYNmHcWWqTS_16irDBDJ10FGtmOj4daqbwIfXr2T_1pgAPw3UVcBTViMTmo8HanZDWmMZpegl3JhJdkJXlSAWnySV-F1wbq8OuHHs-zD-RYNeMd0GIbjSstZBHdLnS7uw&amp;amp;hl=en"&gt;google reverse image search&lt;/a&gt; on the &lt;a href="https://i.stack.imgur.com/MyQki.jpg"&gt;imgur link&lt;/a&gt; gives lots of hits, but no clear provenance.&lt;/p&gt;

&lt;p&gt;&lt;img alt="Monkey Ladder cartoon" src="/assets/images/monkey_ladder.jpg" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://skeptics.stackexchange.com/users/4020/flimzy"&gt;Skeptics StackExchange User Flimzy&lt;/a&gt; seems to have the closest answer in finding a book by Galef in 1976 that references a paper by Stephenson in 1967. The experiment isn’t as described but seems true in principle. Flimzy links to the &lt;a href="https://books.google.co.za/books?id=x_hfmoOaHpoC&amp;amp;lpg=PA77&amp;amp;ots=IqFtSYj5di&amp;amp;dq=Social%20Transmission%20of%20Acquired%20Behavior%3A%20A%20Discussion%20of%20Tradition%20and%20Social%20Learning&amp;amp;lr&amp;amp;pg=PA88#v=snippet&amp;amp;q=%22trained%20adult%20male%20and%20female%22&amp;amp;f=false"&gt;Galef book in Google books&lt;/a&gt;, but finding the journal article is a pain.&lt;/p&gt;

&lt;p&gt;The citation in Galef’s book, &lt;strong&gt;Social Transmissions of Acquired Behavior&lt;/strong&gt;_, is…&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-citation"&gt;Stephenson, G. R. (1967). Cultural acquisition of a specific learned response among rhesus monkeys. In: Starek, D., Schneider, R., and Kuhn, H. J. (eds.), Progress in Primatology, Stuttgart: Fischer, pp. 279-288
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But googling for a fifty year old journal doesn’t let me find the journal and directly download. I find something with &lt;a href="https://www.scribd.com/doc/73492989/Stephenson-1966-Cultural-Acquisition-of-a-Specific-Learned-Response-Among-Rhesus-Monkeys"&gt;that title on scribd with a date of 1966&lt;/a&gt;, but I’m not going to sign in to download it. And I wouldn’t want to force anyone following my citation to have to sign up for scribd just to check my work or follow my research. Thankfully other bloggers have been down this path and &lt;a href="https://mimeticmargins.com/tag/cultural-acquisition-of-a-specific-learned-response-among-rhesus-monkeys-pdf/"&gt;Erik Buys’ post&lt;/a&gt; includes a &lt;a href="https://erikbuys.files.wordpress.com/2012/04/cultural-acquisition-of-a-specific-learned-response-among-rhesus-monkeys-gordon-r-stephenson.pdf"&gt;helpful link to a PDF of the article that he hosts on wordpress that is at least easy to download and looks like the real article&lt;/a&gt;. Thanks Erik, wherever you got this.&lt;/p&gt;

&lt;p&gt;Although I would feel kind of off with a citation like…&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-citation"&gt;Stephenson, G. R. (1967). Cultural acquisition of a specific learned response among rhesus monkeys. In: Starek, D., Schneider, R., and Kuhn, H. J. (eds.), Progress in Primatology, Stuttgart: Fischer, pp. 279-288 via https://erikbuys.files.wordpress.com/2012/04/cultural-acquisition-of-a-specific-learned-response-among-rhesus-monkeys-gordon-r-stephenson.pdf, accessed 2021-06-06.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So this business urban legend ends up being partially true, but in the actual experiment it’s not as “juicy” and is a harder story to tell accurately. On page 284, Stephenson describes the experiment so a conditioned monkey stops a newcomer from interacting with the “banana” equivalent from my chimpanzee story, and the newcomer doesn’t interact with it. And air blasts were used rather than freezing cold water, But at least there’s always someone who knows the reason not to interact. Sadly, they didn’t continue on with a third monkey who never received the air blast to stop them. And there’s some sex in this real version…&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-quote"&gt;The demonstrator subject, upon being placed in the apparatus, retired to the end furthest from his conditioning object. The naive subject, when placed into the apparatus ten seconds later, first went to and mounted the demonstrator subject, then proceeded to the object without hesitation, looked at it and slowly reached out towards it. At this moment, the demonstrator subject came from the far end of the apparatus, grabbed the naive subject by the flanks and pulled him away. The object remained in place on the board. The demonstrator then turned and sexually presented to the naive partner. The naive partner, although it subsequently approached the object several times, did not touch the object in the remaining 14 minutes of the session.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Knowing the potential origin of this urban legend at least lets me feel like less of a fraud, but still doesn’t let me stick to my plan of always providing a citation to the original source. Maybe if I’m lucky, someone like Gladwell can write a book and describe the experiment, but I don’t think having a citation to a Gladwell book is very good for establishing ethos and shared reality.&lt;/p&gt;

&lt;h2 id="drucker-quote"&gt;Drucker quote&lt;/h2&gt;

&lt;p&gt;I think I first heard the quote way back in business school and must have heard this hundreds, or thousands of times. So much so that I wouldn’t want to use it in a presentation for fear of boring the audience. But I wanted to cite it in an internal analysis note to try to make a case for assessing potential strategies proposed with organizational and partner culture. Basically just a general pointer that this isn’t a random idea from me, but something that is sort of baseline in strategic plans.&lt;/p&gt;

&lt;p&gt;But, oddly, this was harder than I thought to actually cite where Drucker brought up this idea and after googling for an hour or so I discarded the idea and didn’t end up using the reference. It was interesting how many sites quote this without actually referencing a book or paper or article from Drucker where he says this.&lt;/p&gt;

&lt;p&gt;This was a few years ago and now we have the wonderful work of the &lt;a href="https://quoteinvestigator.com/2017/05/23/culture-eats/"&gt;Quote Investigator web site&lt;/a&gt; that I am so grateful for tracing down and investigating where these things come from. They go back as far as 2000 with a reference to an unnamed person from the technology consulting firm, Giga Information Group. So it seems no one knows the actual person who came up with this quote but doesn’t seem to be Drucker and likely isn’t anyone where saying the name would convey any authority. I certainly can’t think of an audience where quoting Giga Information Group (or even their founder Gartner or the Gartner firm) would be very helpful in establishing ethos. It’s probably the equivalent of quoting wikipedia as the sole source for an idea, rather than a collection of facts.&lt;/p&gt;

&lt;p&gt;Not sure why this is misattributed to Drucker, but I think because so much of Drucker’s good management books do seem to have the general idea that culture is more important than any distinct action. Of course, he worked in management and so it makes sense that he would write about how the people of an organization and collected culture was the most important thing to work on.&lt;/p&gt;

&lt;p&gt;It’s funny because my own memory made me think I had heard this in the 90s in business school and again in the early 2000s. If Giga coined this expression, I couldn’t have heard it in the 90s and it was pretty unlikely that I heard it in the early 2000s. Just another example of how memory cannot always be trusted and citations are even more helpful.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This all seems to head toward why I like to cite and trace the citations of others because it helps to actually understand what the underlying basis is for things. It’s boring, and I frequently want to skip it, but is worth the time spent. Both to help others not have to go through the effort to find sources and also to avoid misusing and further the spread of misinformation, even accidentally.&lt;/p&gt;

&lt;h3 id="future-blog-ideas"&gt;Future Blog Ideas&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/chitchcock/1281611"&gt;Steve Yegge’s excellent Google Plus post about platforms&lt;/a&gt; references an Amazon “Big Mandate” email that made people use APIs or they would be fired. I’ve never found a real copy of this email and would like to know how much is true and how much is conflated. Comically, Yegge’s post is harder to find because of the Google Plus fiasco and now relies on people rehosting. One of many examples why &lt;a href="https://web.archive.org/"&gt;archive.org&lt;/a&gt; and the ideas around &lt;a href="https://ipfs.io/"&gt;ipfs&lt;/a&gt; are so important so information doesn’t get lost, or worse, slightly and imperceptibly modified.&lt;/p&gt;

&lt;h3 id="stuff-i-read-while-working-on-this-post-that-you-might-want-to-read-too"&gt;Stuff I Read While Working on This Post (That You Might Want to Read Too)&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/List_of_urban_legends"&gt;Wikipedia List of Urban Legends&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Peter_Drucker"&gt;Peter F Drucker Wikipedia bio&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.wisdompills.com/monkey-ladder/"&gt;The ‘Monkey Ladder’: A Famous Social Experiemnt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://skeptics.stackexchange.com/questions/6828/was-the-experiment-with-five-monkeys-a-ladder-a-banana-and-a-water-spray-condu"&gt;Was the experiment with five monkeys, a ladder, a banana and a water spray conducted?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://sixsigmadsi.com/10-hungry-monkeys-a-story-of-cultural-training/"&gt;“10 Hungry Monkeys”, A Story of Cultural Training&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://whatsthepont.blog/2015/03/14/5-monkeys-bananas-ladder-water-why-do-we-comply-with-daft-rules-in-organisations/"&gt;5 Monkeys, Bananas, Ladder, Water. Why do we comply with daft rules in organisations?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.answers.com/Q/Did_the_monkey_banana_and_water_spray_experiment_ever_take_place"&gt;Answers.com Did the monkey banana and water spray experiment ever take place?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://mimeticmargins.com/tag/cultural-acquisition-of-a-specific-learned-response-among-rhesus-monkeys-pdf/"&gt;Foolish Monkeys&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.managementcentre.co.uk/management-consultancy/culture-eats-strategy-for-breakfast/"&gt;Culture eats strategy for breakfast&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://quoteinvestigator.com/2017/05/23/culture-eats/"&gt;Quote Investigator Culture Eats Strategy for Breakfast&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://en.wikiquote.org/wiki/Peter_Drucker#Managing_for_the_Future%3A_The_1990%27s_and_Beyond_%281992%29"&gt;Wikiquote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="references"&gt;References&lt;/h3&gt;

&lt;div class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:programmer_quadrant"&gt;
      &lt;p&gt;&lt;a href="https://prepend.com/programming/2009/05/programmer-classifications-by-quadrant.html"&gt;Programmer Classifications by Quadrant&lt;/a&gt; &lt;a class="reversefootnote" href="#fnref:programmer_quadrant"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</description><author>PREPEND</author><pubDate>Sun, 06 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">/communication/2021/06/business_urban_legends.html</guid></item><item><title>From one to many: Self-governed communities in three steps</title><link>https://nutcroft.mataroa.blog/blog/from-one-to-many-self-governed-communities-in-three-steps/</link><description>&lt;p&gt;I think there is merit in being part of a community for a cause. The cause can be anything, from a hobby to a societal issue. Sometimes the creation of the community just springs out of conversations and shared values. Some other times it starts in the mind of one person. How do we start from that One person’s thought and end up in a community of shared ownership?&lt;/p&gt;
&lt;p&gt;We might unconsciously associate communities with grassroots movements and democratic governance but this doesn’t have to be the case. Not all communities are democratic and many times it’s hard to say whether one is.&lt;/p&gt;
&lt;p&gt;For this text, though, let’s assume we aim for democratic self-governance. The hard question we aspire to answer is how we start with One founder and end up with many collective owners while also being a sustainably self-managed group of people.&lt;/p&gt;
&lt;h2 id="step-1-get-people-hyped"&gt;Step 1: Get people hyped&lt;/h2&gt;
&lt;p&gt;The cause drives the community. The One is firstly part of the cause. This cause will probably already have some established gathering places (virtual or IRL) in some form or another.&lt;/p&gt;
&lt;p&gt;Even though our economy is competitive, our communities can only survive through cooperation. Interdependence is crucial and through that network communities can flourish. Being as independent as possible is considered important for survival yet I have found out that it’s usually the opposite that happens. However much independence a community has, it’s impossible to survive; it is through dependencies to other communities (through the edges of a graph) that make the communities themselves (the nodes of the graph) more powerful.&lt;/p&gt;
&lt;p&gt;This is why I think it’s an important first step for the first One to join these communities and their gatherings. This can mean a number of things in reality. Maybe the cause organises on Facebook Groups or through meetups. Or maybe they are under another umbrella cause or organisation. Whatever the case, this is where One starts.&lt;/p&gt;
&lt;p&gt;To illustrate further, maybe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The cause is about fixed gear bicycles, and the gathering square for many is at a specific bike shop.&lt;/li&gt;
&lt;li&gt;The cause is about helping refugees in a city and people coordinate through Facebook Groups.&lt;/li&gt;
&lt;li&gt;The cause is about functional programming and there is a meetup group that organises presentations every month.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once One has joined the existing communities, they can connect with them and share their vision. This is what gets people hyped. On our examples above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Wouldn’t it be awesome if we had a fixed gear bicycle race in the streets of our city?”&lt;/li&gt;
&lt;li&gt;“Wouldn’t it be amazing if we could actually provide shelter to the newly homeless refugees?”&lt;/li&gt;
&lt;li&gt;“Wouldn’t it be great if we had more people write in functional languages and subsequently raise the quality of our libraries?”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once people are hyped, they are ready to dedicate themselves for the cause. They are ready to spend a few hours per week or per month to realise this awesome vision that was conceived and shared with them.&lt;/p&gt;
&lt;h2 id="step-2-share-ownership"&gt;Step 2: Share ownership&lt;/h2&gt;
&lt;p&gt;Once there are some additional members, the first thing the One will notice is that this is the One’s thing. It’s their baby. They made it an entity and the fellow associates try to improve it. Some may contribute a bit, others more, but maybe no one as much as the first One. This is a crucial point. It’s when the One might want to share ownership. In this way, the One actively shows to the associates who believed in them and followed them that this is their baby too. The babyness metamorphoses into a distributed essence.&lt;/p&gt;
&lt;p&gt;In order for the One to share ownership, they need to actually share the ownership. Hand the keys to the associates, whether those are login credentials or actual keys of an office; or maybe adding them as directors in a limited company. Convince them with actions that it’s their baby too.&lt;/p&gt;
&lt;p&gt;In my experience from the past decade, people embrace that. They respect it and do not betray it.&lt;/p&gt;
&lt;p&gt;To judge whether the ownership sharing process was successful is to ask whether the concept of the One within the community has died.&lt;/p&gt;
&lt;p&gt;Once that’s done the community will be entering—among other things—a spiral of informal and formal ceremonies. The formal ones are usually the essence of the community. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cycling communities go for bike rides. Maybe every Friday at 9pm. That's the formal ceremony. But some evenings, people gather at the aforementioned bike shop and talk and drink beers. This is where they schedule the bike rides. This is the informal ceremony.&lt;/li&gt;
&lt;li&gt;Refugee Facebook groups donate food and clothes and help refugees. They have a weekly meeting where they gather all the food and clothes and deliver them to the shelter (formal ceremony). Everyday at lunch and afternoons, they learn about the news of the cause, think of new ways to help and share views (informal ceremonies).&lt;/li&gt;
&lt;li&gt;Programming communities do meetups or hackathons or conferences. They have scheduled events (formal ceremonies) or casual hangouts offline or online (informal ceremonies).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s a spiral because it’s a cycle yet also slightly different every time. There is progress to it. There is a certain directionality, which is hopefully towards improvement.&lt;/p&gt;
&lt;p&gt;Once these ceremonies are set, they are hard to change. They become part of the culture of the community. This is important to be aware of in case of the ceremonies not being nice.&lt;/p&gt;
&lt;p&gt;Even though ceremonies are processes, they are not explicit processes. They are implicit processes and this is the reason that they are not enough to fight inherent entropy.&lt;/p&gt;
&lt;h2 id="step-3-establish-processes"&gt;Step 3: Establish processes&lt;/h2&gt;
&lt;p&gt;To tackle inherent entropy, a community needs processes—the explicit kind.&lt;/p&gt;
&lt;p&gt;These are the boring bits which if not existent, the harder it will be to keep the community from withering away. It will also not be obvious the community needs processes until too late. Thus, they need to be set early. Too early and people will lose their enthusiasm from the bureaucracy; too late and they won’t be bothered to participate at all.&lt;/p&gt;
&lt;p&gt;These processes establish the community, like a ship sailing on its own. They need to include answers to things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do we get new members?&lt;/li&gt;
&lt;li&gt;How do we introduce new projects?&lt;/li&gt;
&lt;li&gt;How do we make decisions collectively?&lt;/li&gt;
&lt;li&gt;What kind is our entity towards others, such as the city, other communities, the government?&lt;/li&gt;
&lt;li&gt;What are some of the values we share and actualise in this community?&lt;/li&gt;
&lt;li&gt;How do we handle disagreements?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Furthermore, it should be noted that in this balance of processes that needs to be achieved, there is another factor that usually causes derailment. Arguing for the point of arguing and not for the point of practical progress. Some people are more susceptible than others in this, but in general, there needs to be active re-evaluation of the scope of the processes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“What if someone evil comes and decides to do a hostile takeover of our community?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The above is an example of a question that tends to extend the scope of process definition infinitely. My advice is to contain these kind of discussions, either temporally (eg. timebox them) or by their cardinality (designate a working group of a few people to figure it out).&lt;/p&gt;
&lt;p&gt;The general rule is to create processes for the current people and the current problems. Beyond that, it can wait.&lt;/p&gt;
&lt;p&gt;Now, to provide an opinion to the question above, I think the balance is between shielding old members and sharing ownership with the new. If you shield too much, then you’re less democratic and new members are not as genuine members. If you share ownership too much, then it’s easier for new members to change the community faster than the community’s culture has got to them. I generally vote sharing ownership more and risking takeovers as—usually—moving into a new community is easy enough.&lt;/p&gt;
&lt;h2 id="epilogue"&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;Democracy is hard but I think it’s worth it. Especially for communities which usually begin with volunteer work, democracy is both of vital importance and—sometimes surprisingly—the default form it starts with. In addition to that, the less people there are, the easier democracy is to implement. For instance, in a city or country level implementing democracy is orders of magnitude harder. Thus, if one values democracy, it’s worth practising it in a small community.&lt;/p&gt;
&lt;p&gt;The crucial thing to accept in these kind of communities is that this is everyone’s baby and that ownership only exists as shared ownership. Even though one started it, or one has made the most significant contribution, or one fights for it more, it’s still fair and possible for that person to be voted out tomorrow. Avoiding that is definitely an objective, but accepting it is the key. This acceptance is what solidifies the distributed ownership.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Freedom is not worth having if it does not include the freedom to make mistakes.”&lt;br /&gt;
— Mahatma Gandhi&lt;/p&gt;
&lt;/blockquote&gt;</description><author>nutcroft</author><pubDate>Sun, 06 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nutcroft.mataroa.blog/blog/from-one-to-many-self-governed-communities-in-three-steps/</guid></item><item><title>Gig workers of the world...</title><link>https://stop.zona-m.net/2021/06/gig-workers-of-the-world.../</link><description>&lt;p&gt;Unite. But with whom?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 06 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/gig-workers-of-the-world.../</guid></item><item><title>Switching from Windows PC to Mac and why I switched back</title><link>https://www.jimwestergren.com/switching-from-windows-pc-to-mac-and-why-i-switched-back</link><description>I am a SEO and web developer that has been using Windows since more than two decades. In may 2021 the fan of my current lap top started to make a lot of noise and I bought a MacBook Pro M1 which was highly recommended to me by coworkers - I was told that I &amp;#91;...&amp;#93;</description><author>Jim Westergren</author><pubDate>Sun, 06 Jun 2021 02:05:00 GMT</pubDate><guid isPermaLink="true">https://www.jimwestergren.com/switching-from-windows-pc-to-mac-and-why-i-switched-back</guid></item><item><title>Object Keypoints</title><link>/project/2021/06/05/object-keypoints.html</link><description>Object Keypoints is a simple method to build a model that can track pre-defined points on objects in 3D. It includes a way to scan the objects using a robot arm, a tool to annotate the points, a script to fit the model and a pipeline to detect the 3D points at runtime using the trained model. It can handle multiple objects in a frame simultanously and does not require object instance segmentation.</description><author>keke.dev</author><pubDate>Sat, 05 Jun 2021 22:00:00 GMT</pubDate><guid isPermaLink="true">/project/2021/06/05/object-keypoints.html</guid></item><item><title>Book Review: Alpi Ribelli: Storie di montagna, resistenza e utopia</title><link>https://nicolaiarocci.com/book-review-alpi-ribelli-storie-di-montagna-resistenza-e-utopia/</link><description>&lt;p&gt;The idea behind this book is fascinating. As the subtitle suggests, the book
collects stories of rebel mountaineers of all kinds. Some chose to disobey
orders; others built refuges of resistance, outposts of autonomy and
laboratories of social innovation.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Alpi Ribelli book cover" src="https://nicolaiarocci.com/images/alpi-ribelli-book-cover.jpeg#right" /&gt;
The collection is rich and varied. We go from the heretics who went with Fra
Dolcino to the partisans who stopped the Nazi fascists in the mountains of
Cuneo and Belluno, up to the contemporary movements against the high-speed
train in the Susa Valley. Some high-profile mountain climbers are present as
well. Tita Piaz, the Dolomiti Devil; the Nuovo Mattino climbers who, inspired
by the revolution going in Yosemite , let go of the classic &amp;ldquo;fight with the
Alp&amp;rdquo; approach to mountain climbing; the very notable (and totally unknown to
me) story of Guido Rossa, worker, trade unionist, and fantastic rock climber.
The Italian Alpine Club internal struggle from which the Mountain Wilderness
association originated.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 05 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/book-review-alpi-ribelli-storie-di-montagna-resistenza-e-utopia/</guid></item><item><title>6502 “Illegal” Opcodes Demystified</title><link>https://www.masswerk.at/nowgobang/2021/6502-illegal-opcodes</link><description>A closer look at the “llegal” opcodes and undocumented instructions of the MOS 6502 MPU.</description><author>mass:werk – Now Go Bang!</author><pubDate>Sat, 05 Jun 2021 09:00:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/6502-illegal-opcodes</guid></item><item><title>My Ideal Engineering Organization</title><link>https://www.pseudofreedom.com/p/my-ideal-engineering-organization</link><description>Disclaimer</description><author>PseudoFreedom</author><pubDate>Sat, 05 Jun 2021 08:23:09 GMT</pubDate><guid isPermaLink="true">https://www.pseudofreedom.com/p/my-ideal-engineering-organization</guid></item><item><title>Eli5: I Seriously Cant Wrap My Head Around the Idea of Time Is Relative</title><link>https://studiofreya.org/reddit/eli5-i-seriously-cant-wrap-my-head-around-the-idea-of-time-is-relative/</link><description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/explainlikeimfive/comments/nsrbnn/comment/h0o1sc9/"&gt;https://www.reddit.com/r/explainlikeimfive/comments/nsrbnn/comment/h0o1sc9/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><author>Studiofreya SSG Site</author><pubDate>Sat, 05 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://studiofreya.org/reddit/eli5-i-seriously-cant-wrap-my-head-around-the-idea-of-time-is-relative/</guid></item><item><title>Multithreaded JavaScript: Concurrency Beyond the Event Loop</title><link>https://thomashunter.name/posts/2021-06-05-multithreaded-javascript-early-release</link><author>Thomas Hunter II</author><pubDate>Sat, 05 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://thomashunter.name/posts/2021-06-05-multithreaded-javascript-early-release</guid></item><item><title>Lithium from oceans may be great...</title><link>https://stop.zona-m.net/2021/06/lithium-from-oceans-may-be-great.../</link><description>&lt;p&gt;But not for the reasons you think.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 05 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/lithium-from-oceans-may-be-great.../</guid></item><item><title>Open Source: What Happens When the Free Lunch Ends?</title><link>https://nicolaiarocci.com/open-source-what-happens-when-the-free-lunch-ends/</link><description>&lt;p&gt;The article I&amp;rsquo;m linking today is authored by Aaron Stannard and focuses on &lt;a href="https://aaronstannard.com/dotnetoss-free-lunch-ends/"&gt;the
drama currently going on in the .NET Open Source ecosystem&lt;/a&gt;. We&amp;rsquo;ve all been
there. A dependency we took aeons ago goes unmaintained or changes its
licensing model. Why does this happen? Because at some point, projects need to
become sustainable or else they fail.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[&amp;hellip;] it&amp;rsquo;s inexpensive for maintainers to support a small number of users
with relatively similar demands - but once a project achieves critical mass
and the demand on the maintainers exceeds their desire to supply, something
will have to give.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Fri, 04 Jun 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/open-source-what-happens-when-the-free-lunch-ends/</guid></item><item><title>Monad Transformers - the prelude to ZPure</title><link>http://justinhj.github.io/2021/06/04/monad-transformers-the-prelude-to-zpure.html</link><description>&lt;div style="padding: 30px;"&gt;
  &lt;img alt="Diagram of monad transformers with the ZPure type" border="1" class="img" src="../../../images/monadtransformers.png" width="600" /&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-orga6db714"&gt;
&lt;h2 id="orga6db714"&gt;Readers and Writers and Transformers&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-orga6db714"&gt;
&lt;p&gt;
BTW you can check out the video here instead: 
&lt;a href="https://youtu.be/tEnZZlOn-jI"&gt;Functional Justin - Another Angle on Monad Transformers&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
There are twenty pages here or over an hour of videos, so let me help you decide if it's worth your time.
&lt;/p&gt;
&lt;/div&gt;

&lt;div class="outline-3" id="outline-container-org9365b75"&gt;
&lt;h3 id="org9365b75"&gt;Who are you?&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org9365b75"&gt;
&lt;p&gt;
You are a Scala programmer or interested in Scala and may have some Haskell or interest in pure functional programming.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-3" id="outline-container-org027dc19"&gt;
&lt;h3 id="org027dc19"&gt;What do I cover?&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org027dc19"&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;Introduction to Monad transformers&lt;/li&gt;
&lt;li&gt;Implement the WriterT monad in Scala 3 from scratch and used in a program&lt;/li&gt;
&lt;li&gt;Implement the ReaderT monad in Scala 3, also from scratch, and use it&lt;/li&gt;
&lt;li&gt;Why not both? Stack the Reader and Writer monads on top of each other&lt;/li&gt;
&lt;li&gt;Effect rotation and ZIO Prelude's ZPure, another approach to combining effects&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-org5369a1f"&gt;
&lt;h2 id="org5369a1f"&gt;Evaluating expressions&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org5369a1f"&gt;
&lt;p&gt;
In previous blogs and videos, I've described a program that evaluates
arithmetic expressions as this is a nice testbed for various
functional effects. So far I've demonstrated how by using different
data types and type classes one can make the same program behave
differently.
&lt;/p&gt;

&lt;p&gt;
This is important for a couple of reasons. It means that you can
compose interesting programs from smaller, well-understood components,
and because we can understand and change the behaviour of our program
by using different types.
&lt;/p&gt;

&lt;p&gt;
As a starting point, lets begin with a version of the program that has
error handling using the Either data type and Numeric which is a
generic implementation of numbers.
&lt;/p&gt;

&lt;p&gt;
If you didn't catch up on earlier posts let's recap. An example run of
the program requires an environment (symbol table) provided using the
Scala 3 implicit mechanism (using the given keyword). I call eval on a
sample expression tree which yields either a Right or a Left result
since I'm using Either as the result effect type.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;  given envMap&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Map&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"x"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;7&lt;/span&gt;, &lt;span style="color: #79a8ff;"&gt;"y"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;6&lt;/span&gt;, &lt;span style="color: #79a8ff;"&gt;"z"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;22&lt;/span&gt;)

  &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;exp1&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Mul&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;Var&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"z"&lt;/span&gt;), &lt;span style="color: #00bcff;"&gt;Add&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;10&lt;/span&gt;), &lt;span style="color: #00bcff;"&gt;Sub&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;Var&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"x"&lt;/span&gt;), &lt;span style="color: #00bcff;"&gt;Var&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"y"&lt;/span&gt;))))

  &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;eval1&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(exp1)

  println(s&lt;span style="color: #79a8ff;"&gt;"Eval exp gives &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$eval1&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;)

&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;[info] running Scala3EvalEither &lt;/span&gt;
&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Eval exp gives Right(242)&lt;/span&gt;

&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
I represent the errors as a Scala 3 enum which is a nice way to create
ADT's (algebraic data types), similar to how you would do it in Rust.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;enum &lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt; {
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;InvalidSymboName&lt;/span&gt;
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt;
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;DivisionByZero&lt;/span&gt;
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The next things to look at are an ADT to represent the program steps a return type.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;
enum &lt;span style="color: #00bcff;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt;
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;A&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Add&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;left&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], &lt;span style="color: #00d3d0;"&gt;right&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]) &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Sub&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;left&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], &lt;span style="color: #00d3d0;"&gt;right&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]) &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Mul&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;left&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], &lt;span style="color: #00d3d0;"&gt;right&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]) &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Div&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;left&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], &lt;span style="color: #00d3d0;"&gt;right&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]) &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Var&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;identifier&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;String&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]

&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Map&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]

&lt;span style="color: #b6a0ff;"&gt;import&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Exp&lt;/span&gt;.&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;

&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] ?=&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The &lt;code&gt;Env&lt;/code&gt; type is a simple map for strings to values that we will use
as a symbol table so that variables can be looked up at runtime. The
&lt;code&gt;?-&amp;gt;&lt;/code&gt; syntax indicates that the return type is a context function. An
earlier blog discusses that, but in short, it allows us to thread our
Env symbol table through the computation easily.
&lt;/p&gt;

&lt;p&gt;
Here is the main body of the code.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;eval&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](exp&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
  exp &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt;
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Var&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;id&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleVar(id)
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value)
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Add&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleAdd(l,r)
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Sub&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleSub(l,r)
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Div&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleDiv(l,r)
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Mul&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleMul(l,r)

&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleAdd&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](l&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] , r&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] )&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(l) + eval(r)
&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleSub&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](l&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] , r&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] )&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(l) - eval(r)
&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleMul&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](l&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] , r&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] )&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(l) * eval(r)
&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleDiv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](l&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] , r&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] )&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(l) / eval(r)

&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleVar&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](s&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;String&lt;/span&gt;)&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
  summonEnv.get(s) &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt; {
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Some&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value)
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;None&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Left&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;.&lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt;)
  }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Those arithmetic operators you see are operating not on integers,
doubles or some other concrete type, but are working on a type A that
has a Numeric instance. You may wonder then how that &lt;code&gt;+&lt;/code&gt; operator
knows what to do? The answer is that I implemented an instance of
Numeric for the type &lt;code&gt;Numeric[Either[EvalError,A]]&lt;/code&gt;.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Implement Numeric for EvalResult&lt;/span&gt;
given evalResultNumeric[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;]&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]] &lt;span style="color: #b6a0ff;"&gt;with&lt;/span&gt; {

  &lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], fb&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
    fa.map2(fb)((a,b) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; a + b)
  }
  &lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;... and so on&lt;/span&gt;
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Whilst this is a lot of overhead for a simple program, as your
programs scale in complexity, this level of abstraction lets you
control effects as well as swap them in and out as your requirements
change without having to rewrite the core logic.
&lt;/p&gt;

&lt;p&gt;
As an example, let's introduce a Monad Transformer and show how to
integrate it with the program above.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-org1d9e441"&gt;
&lt;h2 id="org1d9e441"&gt;WriterT&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org1d9e441"&gt;
&lt;p&gt;
Let's say we want to take an existing effectful program and add a new
effect to it. The effect I will demonstrate is logging. There is a
data type called Writer which represents a value and a log. 
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #00bcff;"&gt;Writer&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](run&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; (&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This is not very interesting on its own but if you make a program from
Writers, sequencing them together using the Monad's flatMap operation
for example, then the end result consists of a final value and a log
for &lt;code&gt;each step of the program&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
But since I already committed to using Either, if I change the type to
Writer then I would lose the ability to handle errors. Instead what I
want is to keep the Either effect and &lt;code&gt;wrap&lt;/code&gt; it with the capability of
the Writer monad.
&lt;/p&gt;

&lt;p&gt;
Monad transformers are the answer. Now the trouble with monads is that
they don't compose manually together. As I covered in a previous blog,
applicatives do. You can take any two applicative effects such as
Either and List and compose them with simple functions. 
&lt;/p&gt;

&lt;p&gt;
With Monads the composition of any particular monad has to be
hand-crafted, so if I want to stack a Reader on top of an Either,
which I do, then I need to implement a ReaderT (reader transformer).
&lt;/p&gt;

&lt;p&gt;
It only needs to be implemented once and for all and can then be applied
to any other Monad (not just for Either). The idea is to make an
implementation of Reader that wraps another Monadic data type.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;class&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;[&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](&lt;span style="color: #f78fe7;"&gt;private&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;wrapped&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;F&lt;/span&gt;[(&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;)])
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here you can see the definition of the WriterT data type. The
difference between WriterT and Writer is that the WriterT wraps an
existing monad. Note that there is no need to constrain the
higher-kinded type F to be a Monad, but later on when we use it in
various ways it is possible to constrain F to be a Functor,
Applicative or Monad depending on the use-case. Choosing the type
bounds that constrain what the wrapped type must support based on the
individual functions needs gives you more flexibility.
&lt;/p&gt;

&lt;p&gt;
For example, if you have a data type that has a map operation but no
meaningful way to make a flatMap, you can still use the Monad
transformer as long as you only use Functor level methods.
&lt;/p&gt;
&lt;/div&gt;

&lt;div class="outline-3" id="outline-container-org24d8b6c"&gt;
&lt;h3 id="org24d8b6c"&gt;Lifting&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org24d8b6c"&gt;
&lt;p&gt;
To use WriterT there needs to be a mechanism to take your inner data
type (Either in this case) and make an instance of WriterT.  That can
be done like this in my implementation by using the WriterT
constructor. For example let's say we have an Either instance we can
transform it to a WriterT like this.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;e1&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;10&lt;/span&gt;)
&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;w1&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;(e1.map(n &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; (&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;.empty[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;], n)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
It's not straightforward because the WriterT wrapped type must be
&lt;code&gt;F[(W,A)]&lt;/code&gt; and we had an F[A]. That is why I need to use the map
operation to take any value the Either may have and combine it with an
empty log. Here we assume the log is a list of strings and Scala is
able to infer that too.
&lt;/p&gt;

&lt;p&gt;
Since this needed often the lift method is often added which takes
care of creating an empty log message and mapping it for us.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;object&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt;
  &lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;lift&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;[&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;F&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])(using m&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Monoid&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Functor&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
    &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;.map(fa)(a &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; (m.zero, a)))

&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;... &lt;/span&gt;

  &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;e1&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;10&lt;/span&gt;)
  &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;w1&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; 
    &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;.lift(e1)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Couple of interesting things to note about the lift method type
signature. For one you can see that the log must be a Monoid. A Monoid
is a type that must have two useful operations that make it useful for
logs: It must be able to produce an empty element of whatever type it
is specialized for, and it must be able to join that type together.
&lt;/p&gt;

&lt;p&gt;
This gives the user the flexibility to use any data type for the log
and not have to worry about providing an empty log or an append
function. The example here is a monoid since it is a list of
strings. Obviously we can produce an empty list, and the append
function is also trivial, so if you look at my Monoid instance for
lists you can see the implementation is trivial.
&lt;/p&gt;

&lt;p&gt;
Another interesting thing is the Functor type constraint. As I
mentioned above, although we call them Monad transformers, they can be
used with Functors, Applicatives and Monads. Since the lift function
only needs map, it needs only the Functor type constraint.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-3" id="outline-container-org3132388"&gt;
&lt;h3 id="org3132388"&gt;Evaluating expressions with a log&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org3132388"&gt;
&lt;p&gt;
Now I'll walk through the changes needed to convert the expression
evaluator from having the return type Either, to being one of
WriterT[Either]
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Without log&lt;/span&gt;
&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] ?=&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;With log&lt;/span&gt;
&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] ?=&amp;gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The next step is to make small changes to my programs implementation
to manage this new type. As you can see, the simplest change, handling
a basic numeric value, just involves lifting our original Either and
adding a log entry.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span class="linenr"&gt;1: &lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;eval&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](exp&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
&lt;span class="linenr"&gt;2: &lt;/span&gt;  exp &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt;
&lt;span class="linenr"&gt;3: &lt;/span&gt;    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Var&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;id&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleVar(id)
&lt;span class="linenr"&gt;4: &lt;/span&gt;    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;.lift[[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](&lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value)).tell(&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;(s&lt;span style="color: #79a8ff;"&gt;"Val &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$value&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;))
&lt;span class="linenr"&gt;5: &lt;/span&gt;    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Add&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleAdd(l,r)
&lt;span class="linenr"&gt;6: &lt;/span&gt;    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Sub&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleSub(l,r)
&lt;span class="linenr"&gt;7: &lt;/span&gt;    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Div&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleDiv(l,r)
&lt;span class="linenr"&gt;8: &lt;/span&gt;    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Mul&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;l&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; handleMul(l,r)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
You can see in line 4 the code is a matter of lifting the value
wrapped in an Either. The type annotation is needed and creates some
noise.  I use the tell function to add a log entry for this step.
&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;tell&lt;/code&gt; is a method on the WriterT data type itself, and it takes
advantage of the log types monoid to combine this new log entry with
any prior ones. 
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;tell&lt;/span&gt;(l1&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;W&lt;/span&gt;)(using m&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Monoid&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;], f&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Functor&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
  &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;(wrapped.map{
    (l2,a) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
      (m.combine(l2, l1), a)
  })
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
By this technique at the end of a computation we
should see a log of entries.
&lt;/p&gt;

&lt;p&gt;
For example, the expression &lt;code&gt;Val(10)&lt;/code&gt; would be logged as &lt;code&gt;"Val
10"&lt;/code&gt;. Having a step-by-step log of your application has various uses
including the following.
&lt;/p&gt;

&lt;ol class="org-ol"&gt;
&lt;li&gt;Debugging. View the state of your computation in detail&lt;/li&gt;
&lt;li&gt;Auditing and statistics. Analyze the log of your computation for business information.&lt;/li&gt;
&lt;li&gt;Restore a failed computation. You can log at each step enough information to resume an expensive computation that may have been interrupted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
These kinds of benefits come with traditional logging, but building it
into your application in a pure and type rich way can amplify the
benefits.
&lt;/p&gt;

&lt;p&gt;
Let's take a look at the symbol table lookup part of the program.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleVar&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](s&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;String&lt;/span&gt;)&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
  summonEnv.get(s) &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt; {
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Some&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; {
      &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;.lift[[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](&lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value)).tell(&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;(s&lt;span style="color: #79a8ff;"&gt;"Var &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$s&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; (&lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$value&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;)"&lt;/span&gt;))
    }
    &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;None&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;.lift(&lt;span style="color: #00bcff;"&gt;Left&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;.&lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt;))
  }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Again the change is virtually mechanical. We lifted our old code and
added the tell call to add some logging information. When we view
variable lookups in the log you will see something like &lt;code&gt;Var("x")&lt;/code&gt;
written as &lt;code&gt;Var x (7)&lt;/code&gt; where 7 is its value in the symbol table.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-3" id="outline-container-org44604ed"&gt;
&lt;h3 id="org44604ed"&gt;Extending numeric&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org44604ed"&gt;
&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleAdd&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](l&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] , r&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] )&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WithEnv&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(l) + eval(r)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The remainder of the program involves expressions like this one. We
use the &lt;code&gt;+&lt;/code&gt; operator to add two other expressions together. How that
works is a combination of operator overloading, extension methods and
implementing an implicit implementation of Numeric for our new WriterT
return type.
&lt;/p&gt;

&lt;p&gt;
Here I'm defining an implicit instance of Numeric that handles things
are Writers around Eithers. In previous posts, this is where I first
implemented addition for different types of number, and then added the
ability to handle errors in a type safe and functional manner.
&lt;/p&gt;

&lt;p&gt;
I'm just extending that technique to handle a more complicated type.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;given evalResultWNumeric[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;]&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]] &lt;span style="color: #b6a0ff;"&gt;with&lt;/span&gt;
  &lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;... implementations&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The implementation of Add assuming a Monadic instance is available is as follows.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;M&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; writerTMonad[[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;]]

&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], fb&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
    &lt;span style="color: #00bcff;"&gt;M&lt;/span&gt;.flatMap(fa) {
      a &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;M&lt;/span&gt;.map(fb){
        b &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
          a + b
      }
    } &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
  }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Which does the job but it doesn't include any logging. We can add that too.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], fb&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
    &lt;span style="color: #00bcff;"&gt;M&lt;/span&gt;.flatMap(fa) {
      a &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;M&lt;/span&gt;.flatMap(fb){
        b &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;result&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; a + b 
          &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;w1&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;.lift(&lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(result))
          w1.tell(&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;(s&lt;span style="color: #79a8ff;"&gt;"Added &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$a&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; and &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$b&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; giving &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$result&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;))
      }
    }
  }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Note that by nesting the flatMaps we have access to a,b and the result
of the computation so we can put all of that into the tell call,
resulting in a log like &lt;code&gt;Added 22 and 23 giving 45&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
There's nothing really wrong with this implementation, but it's
important to always think about the principle of least power. Did I
really need a Monad here? Well in fact there is a great function for
applying a computation across two different effects and that is
&lt;code&gt;map2&lt;/code&gt;. It also requires only Applicative, so I can use that instead.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;App&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; writerTApplicative[[&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A1&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;]]

 &lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], fb&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
   &lt;span style="color: #00bcff;"&gt;App&lt;/span&gt;.map2(fa)(fb) {
     &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; (&lt;span style="color: #00d3d0;"&gt;a&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;b&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; a + b
   }
 }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This simplifies the code greatly but notice that I am no longer
logging anything. Unfortunately, I no longer have access to the result
of the computation. One clean solution I found here was to write a
helper method that is like a logging version of map2. Like map2 it
takes a function of two arguments to map the effect values, but it
takes a second function that takes the two values and their result and
lets you build a log entry from them.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;mapTell2&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;B&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;C&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;[&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;](fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;],fb&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;B&lt;/span&gt;],fabc&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; (&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;B&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;C&lt;/span&gt;,fabcw&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; (&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;B&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;C&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;)
                           (using m&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Monoid&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;], f&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Monad&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;W&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;C&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
   &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;r&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; fa.unwrap().map2(fb.unwrap()){
     &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; ((&lt;span style="color: #00d3d0;"&gt;al&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;a&lt;/span&gt;),(&lt;span style="color: #00d3d0;"&gt;bl&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;b&lt;/span&gt;)) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
       &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;c&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; fabc(a,b)
       &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;w&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; fabcw(a,b,c)
       &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;prev&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; m.combine(al,bl)
       (m.combine(prev,w),c)
   }
   &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;(r)
 }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
While this looks like a handful what it is really doing is
straightforward. Like map2 the input is two effects. First I unwrap
them which gives us the inner effect, and running map2 on those gives
the log and the value of each effect.
&lt;/p&gt;

&lt;p&gt;
Once I've run the user function fabc on those values, I have the
result value c and I can use that to build a log with the fabcw
function. Finally, we need to combine the prior logs with the new log
and return the result.
&lt;/p&gt;

&lt;p&gt;
Here's the function in action.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;sub&lt;/span&gt;(a&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], b&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResultW&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
     mapTell2(a,b,(a, b) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; a / b,(a,b,c) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;(s&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$c&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;: subtracted &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$a&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; from &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$b&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;))
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
By moving all that complexity into a helper function, each operator is now quite simple.
&lt;/p&gt;

&lt;pre class="example"&gt;
[info] running Scala3EvalEitherTWriter 
WriterT(Right((List(Var y (6), Var x (7), Val 10, Var z (22)),45)))
Var y (6)
Var x (7)
Val 10
Var z (22)
exp01 WriterT(Left(DivisionByZero))
&lt;/pre&gt;

&lt;p&gt;
In summary, you can use WriterT to convert any effectful program into
one with step-by-step logging.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-org697c96c"&gt;
&lt;h2 id="org697c96c"&gt;ReaderT&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org697c96c"&gt;
&lt;p&gt;
Another useful data type with a Monad instance is the Reader. As the
name may imply, this is the conceptual opposite of Writer. i.e.,
instead of a computation writing its progress to a log, the Reader
provides an environment of some type that the application can read
from as it progresses.
&lt;/p&gt;

&lt;p&gt;
In the program so far I've been using Scala 3 context functions to
pass around the symbol table. There are reasons you may want to do
that with a Reader instead. Perhaps you want to take advantage of the
compositionality and lawfulness of Reader. Perhaps you want the
context function reserved for some other purpose. Of course you may be
using Scala 2 and not have access to the context function feature at
all.
&lt;/p&gt;

&lt;p&gt;
In one of my videos, I show the process of replacing context functions
with the ReaderT monad transformer. Let's walk through the process
here.
&lt;/p&gt;

&lt;p&gt;
First of all let's look at the data type. Like the WriterT, the
ReaderT wraps another higher kinded type F. As you can see, there is a
second type parameter R, which is the type of the read-only
environment. Also, you can see from the signature is that what the
ReaderT contains is a function from R to the F[A]. How that is used
will become clear.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;class&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;ReaderT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;[&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;R&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](run&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;R&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Just like with WriterT we also would benefit from a lift function that
lets us take any instance of F and wrap it. Here I'm saying if you
have some effect &lt;code&gt;F[A]&lt;/code&gt; I will give you a ReaderT that wraps it. You
can run it with some environment and it will yield that &lt;code&gt;F[A]&lt;/code&gt; again.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;object&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt;
  &lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;lift&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;[&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;R&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;F&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;ReaderT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;F&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;R&lt;/span&gt;,&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;(&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; fa)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
When rewriting the program above we can now look up variables from the
symbol table. We are returning a function that when given an
environment can search it for the required symbol. 
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleVar&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](s&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;String&lt;/span&gt;)&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;RResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
   &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;((env&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
     env.get(s) &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt; {
       &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Some&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value)
       &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;None&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Left&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;.&lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt;)
     })
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Literal values are also simple, just lift the Either from before.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;.lift(&lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The arithmetic operations don't change at all since ReaderT has an
applicative instance we can just go ahead and use map2.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(fa&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], fb&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;EvalResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
    fa.map2(fb)((a,b) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; a + b)
  }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here is what needs to be done to run the code. The main difference is
that we build a chain of Reader effects then execute them by passing
an environment to the run method.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;env1&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Map&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"x"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;1&lt;/span&gt;, &lt;span style="color: #79a8ff;"&gt;"y"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;10&lt;/span&gt;, &lt;span style="color: #79a8ff;"&gt;"z"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;100&lt;/span&gt;)
&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;exp1&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Add&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;Mul&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;10&lt;/span&gt;), &lt;span style="color: #00bcff;"&gt;Var&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"y"&lt;/span&gt;)),&lt;span style="color: #00bcff;"&gt;Var&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"z"&lt;/span&gt;))
println(eval(exp1).run(env1)) 

&lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;Right(100))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-orgae19976"&gt;
&lt;h2 id="orgae19976"&gt;WriterT and ReaderT&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-orgae19976"&gt;
&lt;div style="padding: 30px;"&gt;
  &lt;img alt="Diagram of monad transformers" border="1" class="img" src="../../../images/monadtransformer.png" width="600" /&gt;
&lt;/div&gt;

&lt;p&gt;
One thing I find wonderful about functional programming is its
compositionality. I've shown that you can stack WriterT and ReaderT on
top of any effect to imbue that effect with more capabilities.
&lt;/p&gt;

&lt;p&gt;
Now given that WriterT can wrap a monadic effect to give that effect
logging, and further that ReaderT itself is a monad, it follows that
you can wrap WriterT around ReaderT to give some effect the powers of
both! This would work the other way around, and of course, you could
also make an EitherT monad transformer, giving even more
possibilities.
&lt;/p&gt;

&lt;p&gt;
The next step is to change the programs return type to be&amp;#x2026;
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;[
  [&lt;span style="color: #00bcff;"&gt;RA&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;EA&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;EA&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;RA&lt;/span&gt;],
  &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;],
  &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Next to modify the program to handle the new effect types.
&lt;/p&gt;

&lt;p&gt;
The implementation to get a value is easy enough. Starting from the
inside out the value is put into an Either, lifted into ReaderT and
lifted once more into WriterT.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;.lift(
     &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;.lift(
       &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(value))).tell(&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;(s&lt;span style="color: #79a8ff;"&gt;"Literal value &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$value&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Handling variable lookup we take care of the lookup first then wrap
that into a writer.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleVar&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;](s&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;String&lt;/span&gt;)&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;WriterT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;RA&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;EA&lt;/span&gt;] =&amp;gt;&amp;gt; &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;EA&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;RA&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt;
  &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;((env&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
    env.get(s) &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt; {
      &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Some&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Right&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;(s&lt;span style="color: #79a8ff;"&gt;"Looked up var &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$s&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; (&lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$value&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;)"&lt;/span&gt;),value)
      &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;None&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Left&lt;/span&gt;(&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;.&lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt;)
  }))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
I'll skip the rest of the program since the theme is the same; wrap
the reader code with the writer code and you're done. Let's take a
look at how to run the program.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;envMap&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Int&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Map&lt;/span&gt;(&lt;span style="color: #79a8ff;"&gt;"x"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;7&lt;/span&gt;, &lt;span style="color: #79a8ff;"&gt;"y"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;6&lt;/span&gt;, &lt;span style="color: #79a8ff;"&gt;"z"&lt;/span&gt; -&amp;gt; &lt;span style="color: #00bcff;"&gt;22&lt;/span&gt;)

&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;eval1&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(exp1).unwrap().run(envMap)

eval1.foreach {
  (log, value) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
    println(s&lt;span style="color: #79a8ff;"&gt;"Result is &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$value\n&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;)
    log.foreach {
      println(&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt;)
    }
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here you can see that our program has to be run sort of inside
out. &lt;code&gt;eval(exp1)&lt;/code&gt; gives us a &lt;code&gt;WriterT&lt;/code&gt;. By calling &lt;code&gt;unwrwap&lt;/code&gt; I get the
&lt;code&gt;ReaderT&lt;/code&gt; inside it, which I can then run by passing the environment.
&lt;/p&gt;

&lt;p&gt;
The response is either an error or a tuple of our log and result,
which we can then iterate over to print it out.
&lt;/p&gt;

&lt;pre class="example"&gt;
Result is 990

Looked up var z (22)
Literal value 10
Literal value 2
Divided 10 by 2 (5)
Literal value 2
Subtracted 2 from 5 (3)
Looked up var x (7)
Looked up var y (6)
Multiplied 7 by 6 (42)
Added 3 to 42 (45)
Multiplied 22 by 45 (990)

&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-org7a383cf"&gt;
&lt;h2 id="org7a383cf"&gt;Monad Transformers - some takeaways&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org7a383cf"&gt;
&lt;p&gt;
You can see that monad transformers offer some expressive power since
they allow us to manually combine different effect types to get the
benefit of them all at once. 
&lt;/p&gt;

&lt;p&gt;
This comes at significant cost though. All this nesting creates
additional JVM objects that take up heap space and may cause extra
work for the garbage collector. 
&lt;/p&gt;

&lt;p&gt;
For the programmer, the ergonomics are not great. You have to remember
the level of nesting you're at at each point of your program and make
sure to do the right amount of lifting.
&lt;/p&gt;

&lt;p&gt;
Just look at this single simple expression. So much space is taken up
by the type signature, all the simplicity and elegance is lost.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleAdd&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](l&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] , r&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Exp&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] )&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; 
  &lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;RA&lt;/span&gt;] =&amp;gt;&amp;gt; 
    &lt;span style="color: #00bcff;"&gt;ReaderT&lt;/span&gt;[[&lt;span style="color: #00bcff;"&gt;EA&lt;/span&gt;] =&amp;gt;&amp;gt; 
      &lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;EvalError&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;EA&lt;/span&gt;], 
      &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;RA&lt;/span&gt;],
    &lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;],
    &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(l) + eval(r)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Now there ways to help out the Scala compiler and reduce the amount of
boilerplate, namely "kinda curried type parameters" which is a
technique used heavily in Scalaz and Zio.
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://tpolecat.github.io/2015/07/30/infer.html"&gt;https://tpolecat.github.io/2015/07/30/infer.html&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Thinking that the poor type inference is maybe my fault I've also
written this code using Scalaz and Cats and you can see that each
implementation has its pros and cons. (BTW in these libraries ReaderT
is called Kleisli)
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://github.com/justinhj/ZPurePlay/blob/master/src/main/scala/EvalScalaz.scala"&gt;Reader Writer example in Scalaz&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://github.com/justinhj/ZPurePlay/blob/master/src/main/scala/EvalCats.scala"&gt;Reader Writer example in Typelevel Cats&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Both implementations had the same problem as I did when not writing
out the types in full. Scalaz had the same trouble inferring that my
type was applicative so I had to summon the instance explicitly.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #f78fe7;"&gt;implicit&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;rwApply&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Apply&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;WriterT&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;List&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;],&lt;span style="color: #00bcff;"&gt;Kleisli&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Either&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Error&lt;/span&gt;,?],&lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;],?],?]]
rwApply.apply2(x,y) {
       &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; (&lt;span style="color: #00d3d0;"&gt;a&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;b&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; a + b
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Cats was able to handle it resulting in less code and needed less type
annotations in general.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;x.map2(y) {
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; (&lt;span style="color: #00d3d0;"&gt;a&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;b&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; a + b
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-org0f5756a"&gt;
&lt;h2 id="org0f5756a"&gt;Effect rotation with Zio Prelude's ZPure&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org0f5756a"&gt;
&lt;p&gt;
&lt;a href="https://github.com/zio/zio-prelude"&gt;Zio Prelude&lt;/a&gt; is a new library that acts as a sort of add on library to
&lt;a href="https://github.com/zio/zio"&gt;ZIO&lt;/a&gt; (a zero-dependency Scala library for asynchronous and concurrent
programming) which provides an alternative approach to functional
abstractions in Scala.
&lt;/p&gt;

&lt;p&gt;
For the purposes of this blog I'm interested in particular
experimental data type within the library called &lt;code&gt;ZPure&lt;/code&gt;. ZPure has 6
type parameters and supports the operations of monads, applicatives
and functors, albeit the names are changed and some of the
abstractions too.
&lt;/p&gt;

&lt;p&gt;
From Prelude's own source code "&lt;code&gt;ZPure&lt;/code&gt; can be used to model a variety
of effects including context, state, failure, and logging.", let's see
how it compares to monad transformers to implement the program above.
&lt;/p&gt;

&lt;p&gt;
ZPure has six type parameters, which may seem like a lot, but bear in
mind that every time you combine monads you get more type parameters,
but they are stacked vertically not horizontally. With ZPure you start
with all the different effect types one might need but you don't need
to keep adding more and more, and you don't suffer from creating
multiple objects per layer of effect.
&lt;/p&gt;

&lt;div style="padding: 30px;"&gt;
  &lt;img alt="ZPure type parameters" border="1" class="img" src="../../../images/zpure.png" width="600" /&gt;
&lt;/div&gt;

&lt;p&gt;
In the diagram the parameters are.
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;code&gt;W&lt;/code&gt; Logging. The type of logs that this effect produces, analogous to Writer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;S&lt;/code&gt; State. There are two S's because the type encodes an input and output state type&lt;/li&gt;
&lt;li&gt;&lt;code&gt;R&lt;/code&gt; Reader. The type of the read-only environment&lt;/li&gt;
&lt;li&gt;&lt;code&gt;E&lt;/code&gt; Error. The type of the error channel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A&lt;/code&gt; Value. The type of the happy path computed value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Just as before all I need to do is change my programs effect type and
modify the implementation accordingly. The code for this section can
be found &lt;a href="https://github.com/justinhj/ZPurePlay/blob/master/src/main/scala/EvalZpure.scala"&gt;here&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Although Prelude supports Scala 3 now I rewrote my program in Scala 2
in order to do the Scalaz and Cats versions, so the following is also
pre Scala 3 friendly code. First I made a sealed trait to represent
the error types and an alias to indicate that my log will be
strings. 
&lt;/p&gt;

&lt;p&gt;
Note that ZPure is opinionated about the logging type. In the more
conventional approach, the log has to be a monoid instance. With ZPure
the log is handled using the ZIO &lt;code&gt;Chunk&lt;/code&gt; data type which is a
high-performance data structure with a pure functional interface. What
that means for us is that we can consider our log type as a single
entity and not worry about how it is appended.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #f78fe7;"&gt;sealed&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;trait&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Error&lt;/span&gt;
&lt;span style="color: #b6a0ff;"&gt;object&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Error&lt;/span&gt;
&lt;span style="color: #b6a0ff;"&gt;object&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;DivisionByZero&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;extends&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Error&lt;/span&gt;

&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Log&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
These types will represent the &lt;code&gt;E&lt;/code&gt; or error, and &lt;code&gt;W&lt;/code&gt; or log. We can
encode our symbol table using the &lt;code&gt;R&lt;/code&gt; parameter.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Map&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;String&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The final type looks like this.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;type&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Log&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;Any&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;Any&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], &lt;span style="color: #00bcff;"&gt;Error&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Note that I am not using the initial or updated state here so I use
Any for those parameters so as not to constrain them.
&lt;/p&gt;

&lt;p&gt;
Once again let's start with the implementation of literal values.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Val&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; 
  &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;.succeed(value).log(s&lt;span style="color: #79a8ff;"&gt;"Literal value &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$value&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
What's going on here is simple and refreshingly free of type
annotation. Firstly I construct a ZPure consisting of the literal
value, and then add a log using ZPure's log method. 
&lt;/p&gt;

&lt;p&gt;
Now let's implement the symbol table lookup of variables.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span class="linenr"&gt; 1: &lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;handleVar&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;](s&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;String&lt;/span&gt;)&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
&lt;span class="linenr"&gt; 2: &lt;/span&gt;    &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;.environment[&lt;span style="color: #00bcff;"&gt;Any&lt;/span&gt;, &lt;span style="color: #00bcff;"&gt;Env&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]].flatMap {
&lt;span class="linenr"&gt; 3: &lt;/span&gt;      env &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="linenr"&gt; 4: &lt;/span&gt;       &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;.fromOption(env.get(s)).
&lt;span class="linenr"&gt; 5: &lt;/span&gt;       mapError(&lt;span style="color: #b6a0ff;"&gt;_&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color: #00bcff;"&gt;EvalZPure&lt;/span&gt;.&lt;span style="color: #00bcff;"&gt;SymbolNotFound&lt;/span&gt;).
&lt;span class="linenr"&gt; 6: &lt;/span&gt;       flatMap{
&lt;span class="linenr"&gt; 7: &lt;/span&gt;        a &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="linenr"&gt; 8: &lt;/span&gt;          &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;.log(s&lt;span style="color: #79a8ff;"&gt;"Var &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$s&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; value &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$a&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;).as(a)
&lt;span class="linenr"&gt; 9: &lt;/span&gt;       }      
&lt;span class="linenr"&gt;10: &lt;/span&gt;    }
&lt;span class="linenr"&gt;11: &lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
First I use the ZPure.environment to summon the symbol table and
flatMap over it so we can access it as a concrete value &lt;code&gt;env&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Remember that looking up the variable in the symbol table
is going to return an Option since it is a normal map get. We can then
use the ZPure.fromOption to convert that to a ZPure.
&lt;/p&gt;

&lt;p&gt;
We may be a ZPure at that point but we have the wrong error type. In
ZPure and option is simulated by yielding a success value A for Some,
or giving Unit in the error channel to indicate None. This reuse of
the error channel is neat, but since I would like a homogenous error
type for the program I need to convert that error channel from unit to
my own custom Error type. 
&lt;/p&gt;

&lt;p&gt;
To do that I use mapError which takes a function to map the error from
one type to another.
&lt;/p&gt;

&lt;p&gt;
The final step is to add a log. Since I would like the log to show
both the variable name and the actual value I have to nest it inside a
flatMap so we can access the success value. The last thing to note
here is the &lt;code&gt;as&lt;/code&gt; which helps the type system make a ZPure of the right
type.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #f78fe7;"&gt;implicit&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;numericZResult&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;]&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; 
  &lt;span style="color: #00bcff;"&gt;Numeric&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;new&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Numeric&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;]] {
    &lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(x&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], y&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
      x.zip(y).flatMap{&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; (&lt;span style="color: #00d3d0;"&gt;a&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;b&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;.succeed(a + b).log(s&lt;span style="color: #79a8ff;"&gt;"Add &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$a&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; and &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$b&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;)}
  }
  &lt;span style="color: #a8a8a8;"&gt;// &lt;/span&gt;&lt;span style="color: #a8a8a8;"&gt;and so on&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The final component is to make an instance of Numeric. Prelude takes
the approach to naming that concepts should be plain English where
possible, so it uses &lt;code&gt;succeed&lt;/code&gt; instead of &lt;code&gt;pure&lt;/code&gt; or &lt;code&gt;unit&lt;/code&gt; and
similarly &lt;code&gt;zip&lt;/code&gt; instead of applicative terminology.
&lt;/p&gt;

&lt;p&gt;
That being the case, we use zip here to combine the left and right
side effects and apply the appropriate arithmetic operator.
&lt;/p&gt;

&lt;p&gt;
So far so good, but we can't log the result of the computation. This
is easily solved.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;def&lt;/span&gt; &lt;span style="color: #feacd0;"&gt;add&lt;/span&gt;(x&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;], y&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;])&lt;span style="color: #b6a0ff;"&gt;:&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Result&lt;/span&gt;[&lt;span style="color: #00bcff;"&gt;A&lt;/span&gt;] &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; {
    x.zip(y).flatMap{&lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; (&lt;span style="color: #00d3d0;"&gt;a&lt;/span&gt;,&lt;span style="color: #00d3d0;"&gt;b&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;  {
      &lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;result&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; a + b
      &lt;span style="color: #00bcff;"&gt;ZPure&lt;/span&gt;.succeed(result).log(s&lt;span style="color: #79a8ff;"&gt;"Add &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$a&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; and &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$b&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt; (&lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$result&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;)"&lt;/span&gt;)}
    }
  }
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
The last part to note is the driver code to run the program.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-scala"&gt;&lt;span style="color: #b6a0ff;"&gt;val&lt;/span&gt; &lt;span style="color: #00d3d0;"&gt;eval1&lt;/span&gt; &lt;span style="color: #b6a0ff;"&gt;=&lt;/span&gt; eval(exp1).provide(env1).runAll()

eval1._2 &lt;span style="color: #b6a0ff;"&gt;match&lt;/span&gt; {
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Right&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;value&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; 
    println(s&lt;span style="color: #79a8ff;"&gt;"Succeeded with value &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;${value._2}&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;)
    eval1._1.foreach {
      l &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt; 
        println(l)
  }
  &lt;span style="color: #b6a0ff;"&gt;case&lt;/span&gt; &lt;span style="color: #6ae4b9;"&gt;Left&lt;/span&gt;(&lt;span style="color: #00d3d0;"&gt;err&lt;/span&gt;) &lt;span style="color: #b6a0ff;"&gt;=&amp;gt;&lt;/span&gt;
    println(s&lt;span style="color: #79a8ff;"&gt;"oops! &lt;/span&gt;&lt;span style="color: #00d3d0;"&gt;$err&lt;/span&gt;&lt;span style="color: #79a8ff;"&gt;"&lt;/span&gt;)
}
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Here I run eval to create the effect, provide to give it the runtime
environment and runAll. To be clear what eval does is not run a
program, but build a data structure of objects based on the ZPure
operations, and that data structure is evaluated in Prelude using an
efficient interpreter, that yields the result of the program, the log
and any state changes.
&lt;/p&gt;

&lt;pre class="example"&gt;
Succeeded with value 200
Literal value 10
Var x value 1
Mul 10 with 1 (10)
Var y value 10
Mul 10 with 10 (100)
Var z value 100
Add 100 and 100 (200)
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="outline-2" id="outline-container-orgcd6c223"&gt;
&lt;h2 id="orgcd6c223"&gt;Conclusion&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-orgcd6c223"&gt;
&lt;p&gt;
In this post I implemented and motivated ReaderT and WriterT and
transforming monads to combine effects. Even in Haskell where they
originated, monad transformers come with caveats about performance and
ergonomics. In Scala they are not used often.
&lt;/p&gt;

&lt;p&gt;
There are techniques and libraries to make their use more
convenient. Although I have not used it &lt;a href="https://github.com/typelevel/cats-mtl"&gt;Cats MTL&lt;/a&gt; offers solutions to
some of the problems, but it is not widely used.
&lt;/p&gt;

&lt;p&gt;
Although ZIO Prelude's ZPure is still somewhat experimental it seems
offer the benefits of monad transformers such as composability and a
prinicipled type safe api. But, it is also much easier to work with
and anecdotally more performant on the JVM.
&lt;/p&gt;

&lt;p&gt;
In the future I'm looking forward to exploring some traditional
functional programming problems using Prelude and ZPure.
&lt;/p&gt;

&lt;p&gt;
Hope you enjoyed this post and got to the end. You can find my contact
details at the top of the page; I always welcome feedback and
questions.
&lt;/p&gt;

&lt;p&gt;
(C)2021 Justin Heyes-Jones, All Rights Reserved.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description><author>Functional[Justin]</author><pubDate>Fri, 04 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://justinhj.github.io/2021/06/04/monad-transformers-the-prelude-to-zpure.html</guid></item><item><title>Web Frameworks in 60 Seconds</title><link>https://egghead.io/courses/web-frameworks-in-60-seconds-964d25d0?af=dzcrp6</link><description>Each lesson is a 60-second mini tutorial of a different web framework</description><author>Rodrigo Pombo</author><pubDate>Fri, 04 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://egghead.io/courses/web-frameworks-in-60-seconds-964d25d0?af=dzcrp6</guid></item><item><title>Making 123dev Website</title><link>https://justingarrison.com/blog/2021-06-04-making-123dev-website/</link><description>Problems I figured out when creating 123dev.email</description><author>Justin Garrison's Homepage</author><pubDate>Fri, 04 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://justingarrison.com/blog/2021-06-04-making-123dev-website/</guid></item><item><title>Coolest hard-tech companies in NYC 2021</title><link>http://notes.eatonphil.com/coolest-tech-companies-in-nyc-2021.html</link><description>&lt;p&gt;For years I've kept a private list of really cool tech companies in
NYC. Now that I'm funemployed it's the perfect time to publish. This
list is influenced by 1) my perception of the difficulty of the
engineering behind the product and 2) the company's educational and
OSS presence.&lt;/p&gt;
&lt;p&gt;With no further ado and in no particular order, here's my list!&lt;/p&gt;
&lt;h3 id="backtrace"&gt;Backtrace&lt;/h3&gt;&lt;p&gt;This company builds a product for debugging
mobile crashes. Your app produces a crash dump and their debugger will
help you figure out what went wrong. That's freaking awesome.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://backtrace.io"&gt;https://backtrace.io&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="equinix-metal-(previously-packet)"&gt;Equinix Metal (previously Packet)&lt;/h3&gt;&lt;p&gt;This company provides an API around scheduling hardware servers in
their datacenters, not virtual machines. That's nuts.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://packet.com"&gt;https://packet.com&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="digital-ocean"&gt;Digital Ocean&lt;/h3&gt;&lt;p&gt;Ok I used to work for Linode and am still a massive fan but I love all
the clouds and this post is about NYC not Philly. If you want to learn
how Linux works you have to work here.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.digitalocean.com/"&gt;https://www.digitalocean.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="ns1"&gt;NS1&lt;/h3&gt;&lt;p&gt;This company does DNS. Seeing as &lt;a href="https://www.cyberciti.biz/humour/a-haiku-about-dns/"&gt;it was
DNS&lt;/a&gt;, if you want
to understand how the internet works go work for this group.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ns1.com/"&gt;https://ns1.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="squarespace"&gt;SquareSpace&lt;/h3&gt;&lt;p&gt;The first program I made in 7th grade was a Java program that
generated HTML from terminal prompts in my first attempt at a
CMS. Stuff that builds stuff is amazing and SquareSpace is kinda OG.&lt;/p&gt;
&lt;p&gt;They also just IPO-ed so the comp won't be imaginary!&lt;/p&gt;
&lt;p&gt;Disclosure: my wife works here, but they've been on my list longer
than that.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.squarespace.com/"&gt;https://www.squarespace.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="grafana"&gt;Grafana&lt;/h3&gt;&lt;p&gt;Amazing platform. Everyone who can't afford Splunk or doesn't want to
buy competitor's products uses ElasticSearch and Grafana. I didn't
realize until double-checking my research that Grafana is even based
in NYC. Let's hope they're hiring developers here.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://grafana.com/"&gt;https://grafana.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="frame.io"&gt;Frame.io&lt;/h3&gt;&lt;p&gt;It's like Figma for video. Clearly the future.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.frame.io/"&gt;https://www.frame.io/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="datadog"&gt;DataDog&lt;/h3&gt;&lt;p&gt;DataDog feels like the only real competitor in the hosted server analytics.&lt;/p&gt;
&lt;p&gt;Their stock has been doing surprisingly well, or maybe I'm just tired
from WeWork, Uber, et al.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.datadoghq.com/"&gt;https://www.datadoghq.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="chronosphere"&gt;Chronosphere&lt;/h3&gt;&lt;p&gt;I'm a sucker for startups doing hosted data and search because that's
really hard. Chronosphere does Uber-scale log storage/analysis.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://chronosphere.io/"&gt;https://chronosphere.io/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="cockroach-labs"&gt;Cockroach Labs&lt;/h3&gt;&lt;p&gt;Worst company name but maybe one of the single coolest products in
NYC. They built a PostgreSQL compatible scalable platform in
Go. Everything about that is amazing.&lt;/p&gt;
&lt;p&gt;They've also turned down my application like 5 times now though so
maybe they're very picky. :)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/"&gt;https://www.cockroachlabs.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="mongodb"&gt;MongoDB&lt;/h3&gt;&lt;p&gt;It's cloud scale! Need more be said.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.mongodb.com/"&gt;https://www.mongodb.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="trail-of-bits"&gt;Trail of Bits&lt;/h3&gt;&lt;p&gt;I don't actually understand what they do or if they have a product but
their &lt;a href="https://github.com/trailofbits"&gt;Github presence&lt;/a&gt; is amazing and
they're dedicated to educating the community which is one of the most
important things I think a company can do.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.trailofbits.com/"&gt;https://www.trailofbits.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="capsule8"&gt;Capsule8&lt;/h3&gt;&lt;p&gt;I moved to NYC for this company because the founders and product are
insane. If you want to learn how compilers and Linux don't work,
you've got to come here.&lt;/p&gt;
&lt;p&gt;Disclosure: I own stock.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://capsule8.com/"&gt;https://capsule8.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="two-sigma"&gt;Two Sigma&lt;/h3&gt;&lt;p&gt;Algorithmic trading? Maybe the smartest guys in NYC? They don't accept
candidates without bachelor's degrees or they just don't like me. ;)
They also host the only good tech meetups in NYC: Linux User Group and
Papers We Love.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.twosigma.com/"&gt;https://www.twosigma.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="jane-street"&gt;Jane Street&lt;/h3&gt;&lt;p&gt;Another algorithmic trading company but this time with OCaml. They're
so crazy &lt;a href="https://blog.janestreet.com/what-the-interns-have-wrought-2018/"&gt;you should see what the intern
built&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.janestreet.com/"&gt;https://www.janestreet.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="vimeo"&gt;Vimeo&lt;/h3&gt;&lt;p&gt;Everybody loves an underdog story. And the &lt;a href="https://www.linkedin.com/pulse/now-shes-ceo-vimeo-after-rejected-dozens-companies-mamta-shah-/"&gt;CEO seems really
cool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://vimeo.com/"&gt;https://vimeo.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="etsy"&gt;Etsy&lt;/h3&gt;&lt;p&gt;Their blog posts and engineering organization philosophy are widely
regarded. And they've got a sweet headquarters in Brooklyn.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.etsy.com/"&gt;https://www.etsy.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="sisense"&gt;Sisense&lt;/h3&gt;&lt;p&gt;If you're not using ElasticSearch and you're not using Splunk, you
might be using Sisense. Again, I'm a big sucker for data and analytics
platforms.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.sisense.com/"&gt;https://www.sisense.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="codeacademy"&gt;CodeAcademy&lt;/h3&gt;&lt;p&gt;I am 100% on board with giving people opportunities in tech.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.codecademy.com/"&gt;https://www.codecademy.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="stack-overflow"&gt;Stack Overflow&lt;/h3&gt;&lt;p&gt;They were just bought! But they still exist I suppose. If you love
.NET you've got to work here.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/"&gt;https://stackoverflow.com/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="that's-it!"&gt;That's it!&lt;/h3&gt;&lt;p&gt;Tell me what you think and if I'm missing any hard-tech companies in
NYC. I'm sure I am.&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Here's my light-hearted take on some of the coolest tech companies in NYC in 2021&lt;a href="https://twitter.com/Frame_io?ref_src=twsrc%5Etfw"&gt;@Frame_io&lt;/a&gt; &lt;a href="https://twitter.com/equinixmetal?ref_src=twsrc%5Etfw"&gt;@equinixmetal&lt;/a&gt; &lt;a href="https://twitter.com/digitalocean?ref_src=twsrc%5Etfw"&gt;@digitalocean&lt;/a&gt; &lt;a href="https://twitter.com/capsule8?ref_src=twsrc%5Etfw"&gt;@capsule8&lt;/a&gt; &lt;a href="https://twitter.com/NS1?ref_src=twsrc%5Etfw"&gt;@NS1&lt;/a&gt; &lt;a href="https://twitter.com/grafana?ref_src=twsrc%5Etfw"&gt;@grafana&lt;/a&gt; &lt;a href="https://twitter.com/CockroachDB?ref_src=twsrc%5Etfw"&gt;@CockroachDB&lt;/a&gt; &lt;a href="https://twitter.com/squarespace?ref_src=twsrc%5Etfw"&gt;@squarespace&lt;/a&gt; &lt;a href="https://twitter.com/chronosphereio?ref_src=twsrc%5Etfw"&gt;@chronosphereio&lt;/a&gt; &lt;a href="https://twitter.com/datadoghq?ref_src=twsrc%5Etfw"&gt;@datadoghq&lt;/a&gt; &lt;a href="https://twitter.com/MongoDB?ref_src=twsrc%5Etfw"&gt;@MongoDB&lt;/a&gt; &lt;a href="https://twitter.com/trailofbits?ref_src=twsrc%5Etfw"&gt;@trailofbits&lt;/a&gt; &lt;a href="https://twitter.com/twosigma?ref_src=twsrc%5Etfw"&gt;@twosigma&lt;/a&gt; &lt;a href="https://twitter.com/Vimeo?ref_src=twsrc%5Etfw"&gt;@Vimeo&lt;/a&gt; &lt;a href="https://twitter.com/Etsy?ref_src=twsrc%5Etfw"&gt;@Etsy&lt;/a&gt; and more&lt;a href="https://t.co/ZAcvptvLbZ"&gt;https://t.co/ZAcvptvLbZ&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1400815765117353989?ref_src=twsrc%5Etfw"&gt;June 4, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Fri, 04 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/coolest-tech-companies-in-nyc-2021.html</guid></item><item><title>Mindfulness, or work. Pick one</title><link>https://stop.zona-m.net/2021/06/mindfulness-or-work.-pick-one/</link><description>&lt;p&gt;What do YOU want to do after a relaxing nap?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 03 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/mindfulness-or-work.-pick-one/</guid></item><item><title>Getting Credit for Invisible Work</title><link>https://blog.harterrt.com/invisible-work.html</link><description>&lt;p&gt;Last month I gave a talk at &lt;a href="https://csvconf.com/speakers/#ryan-harter"&gt;csv,conf&lt;/a&gt;
on "Getting Credit for Invisible Work".
The (amazing) csv,conf organizers just published a 
&lt;a href="https://www.youtube.com/watch?v=W7zT-GRDSCw"&gt;recording of the talk&lt;/a&gt;.
(&lt;a href="https://blog.harterrt.com/static/invisible_work_preso/#p1"&gt;slides here&lt;/a&gt;).
Give it a watch! It's only 20m long (including the Q&amp;amp;A).&lt;/p&gt;
&lt;p&gt;Invisible work is a concept I've been trying to …&lt;/p&gt;</description><author>blog.harterrt.com</author><pubDate>Thu, 03 Jun 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.harterrt.com/invisible-work.html</guid></item><item><title>Diversity in engineering teams</title><link>https://nindalf.com/posts/diversity/</link><description>This article discusses the importance of diversity in engineering teams and how it can lead to better software products.</description><author>Krishna's blog</author><pubDate>Thu, 03 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nindalf.com/posts/diversity/</guid></item><item><title>Let your streaks end naturally</title><link>https://utf9k.net/blog/let-your-streaks-end-naturally/</link><description>As it says on the tin, all good things must come to an end</description><author>utf9k</author><pubDate>Thu, 03 Jun 2021 02:50:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/let-your-streaks-end-naturally/</guid></item><item><title>How malloc broke Serenity's JPGLoader, or: how to win the lottery</title><link>https://sin-ack.github.io/posts/jpg-loader-bork/</link><description>&lt;p&gt;I got the chance to investigate an interesting bug in
&lt;a href="https://serenityos.org/"&gt;SerenityOS&lt;/a&gt; this week. It was related to the decoding
of JPG images in the operating system. For some reason, when a JPG image is
viewed, it comes out like this:&lt;/p&gt;
&lt;figure&gt;&lt;img alt="Lenna,
showing up with incorrect colors." src="https://sin-ack.github.io/images/serenity-jpgloader/lenna-broken.png" /&gt;&lt;figcaption&gt;
      &lt;p&gt;Lenna,
showing up with incorrect colors.&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Weird, huh? Also seems like a simple confusion of RGB vs. BGR. And sure enough,
making the following change on &lt;code&gt;JPGLoader.cpp&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="chroma" tabindex="0"&gt;&lt;code class="language-diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-   const Color color { (u8)block.y[pixel_index], (u8)block.cb[pixel_index], (u8)block.cr[pixel_index] };
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;&lt;/span&gt;&lt;span class="gi"&gt;+   const Color color { (u8)block.cr[pixel_index], (u8)block.cb[pixel_index], (u8)block.y[pixel_index] };
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;&lt;/span&gt;    context.bitmap-&amp;gt;set_pixel(x, y, color);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;makes the image show up correctly. Case closed!&lt;/p&gt;
&lt;p&gt;&amp;hellip;not. Why did this even break in the first place?&lt;/p&gt;</description><author>sin-ack's writings</author><pubDate>Thu, 03 Jun 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://sin-ack.github.io/posts/jpg-loader-bork/</guid></item><item><title>Save the Pirate Bay of Science</title><link>https://stop.zona-m.net/2021/06/save-the-pirate-bay-of-science/</link><description>&lt;p&gt;It&amp;rsquo;s important. Really.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 02 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/save-the-pirate-bay-of-science/</guid></item><item><title>Understanding behaviours</title><link>https://adamcraven.com/writing/understanding-behaviours/</link><description/><author>Writing on Adam Craven</author><pubDate>Wed, 02 Jun 2021 14:23:27 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/understanding-behaviours/</guid></item><item><title>Lessons learnt migrating a Flask application to Python 3</title><link>https://gaganpreet.in/posts/lessons-learnt-python-3-migration/</link><description>&lt;p&gt;
&lt;img alt="Gears" src="https://gaganpreet.in/images/2021/python3-unsplash.jpg" /&gt;
&lt;/p&gt;
&lt;p&gt;I recently migrated a medium sized Flask application from Python 2 to Python 3. Python 2 was retired over a year ago and upgrading the app was long overdue. I imagine there are still others who could use my lessons to migrate their apps.&lt;/p&gt;
&lt;h3 class="relative group" id="background"&gt;Background &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" href="#background"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;A medium sized Flask application, ~20000 LoC running on Heroku with multiple celery workers. The app was already using the latest version of Python 2.7, which is a necessary stepping stone from 2.x to 3.x.&lt;/p&gt;</description><author>Gaganpreet Arora</author><pubDate>Wed, 02 Jun 2021 13:11:26 GMT</pubDate><guid isPermaLink="true">https://gaganpreet.in/posts/lessons-learnt-python-3-migration/</guid></item><item><title>The Song of Achilles</title><link>https://arunmani.in/library/the-song-of-achilles/</link><description>Trojan War but from a different amazing viewpoint.</description><author>Arun Mani</author><pubDate>Wed, 02 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://arunmani.in/library/the-song-of-achilles/</guid></item><item><title>Simple text blur bookmarklet</title><link>https://alesruzicka.net/blog/eng/blur/</link><description>&lt;p&gt;Ever need to blur out some text on website for screenshot?&lt;/p&gt;
&lt;p&gt;Simple blurring using image editor can be time consuming and can also &lt;a href="https://lifehacker.com/how-to-uncover-blurred-information-in-photographs-1648562658"&gt;be reversed&lt;/a&gt;.&lt;/p&gt;</description><author>English articles on AlesRuzicka.net</author><pubDate>Wed, 02 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://alesruzicka.net/blog/eng/blur/</guid></item><item><title>A Fresh Beginning. Hello SvelteKit!</title><link>https://www.davidwparker.com/posts/2021-06-01-a-fresh-beginning-hello-sveltekit</link><description>A short post on converting this blog (www.davidwparker.com) to SvelteKit; I explain why I converted, and how I did most of the conversion.</description><author>David W Parker</author><pubDate>Wed, 02 Jun 2021 00:31:57 GMT</pubDate><guid isPermaLink="true">https://www.davidwparker.com/posts/2021-06-01-a-fresh-beginning-hello-sveltekit</guid></item><item><title>The problem with high-tech corporations. And geeks</title><link>https://stop.zona-m.net/2021/06/the-problem-with-high-tech-corporations.-and-geeks/</link><description>&lt;p&gt;Technology is never the problem. Or the solution.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 01 Jun 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/06/the-problem-with-high-tech-corporations.-and-geeks/</guid></item><item><title>Learn With Me: Julia - Structs and Binary I/O (#3)</title><link>https://geekmonkey.org/learn-with-me-julia-structs-and-binary-i-o-3/</link><description>&lt;p&gt;Diagrams.net (formerly draw.io) is a fantastic website and tool that allows you to create rich diagrams. The service is entirely free and diagrams can be saved to your Google Drive, Dropbox, or downloaded to your computer. Additionally, diagrams.net allows you to export your diagrams to various formats&lt;/p&gt;</description><author>geekmonkey</author><pubDate>Tue, 01 Jun 2021 17:15:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/learn-with-me-julia-structs-and-binary-i-o-3/</guid></item><item><title>Remind the user what your service does</title><link>https://utf9k.net/blog/remind-the-user-what-your-service-does/</link><description>You haven't emailed me for 2 years! Who are you again?</description><author>utf9k</author><pubDate>Tue, 01 Jun 2021 13:12:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/remind-the-user-what-your-service-does/</guid></item><item><title>Comment your closing braces</title><link>https://river.me/blog/comment-closing-braces/</link><description>This one trick will save you 80% of your wikitext debugging time!</description><author>River Writes - A MediaWiki Blog</author><pubDate>Tue, 01 Jun 2021 08:22:52 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/comment-closing-braces/</guid></item><item><title>More 6502 Updates</title><link>https://www.masswerk.at/nowgobang/2021/more-6502-updates</link><description>Further improvements on the Virtual 6502 suite.</description><author>mass:werk – Now Go Bang!</author><pubDate>Tue, 01 Jun 2021 06:45:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/more-6502-updates</guid></item><item><title>Emacs DWIM: do what ✨I✨ mean</title><link>https://xenodium.com/emacs-dwim-do-what-i-mean</link><description>&lt;p&gt;Update: There's a DWIM &lt;a href="https://xenodium.com/emacs-dwim-swiper-vs-isearch-vs-phi-search/"&gt;follow-up for searching&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-dwim-do-what-i-mean/do-what-i-mean.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I was a rather puzzled the first time I spotted DWIM in an Emacs interactive command name. Don't think I remember what the command itself was, but what's important here is that &lt;a href="https://en.wikipedia.org/wiki/DWIM"&gt;DWIM&lt;/a&gt; stands for &lt;a href="https://en.wikipedia.org/wiki/DWIM"&gt;do what I mean&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I love DWIM interactive commands. They enable commands to be smarter and thus pack more functionality, without incurring the typical cognitive overhead associated with remembering multiple commands (or key bindings). The Emacs manual does a great job describing DWIM for the &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Comment-Commands.html"&gt;comment-dwim&lt;/a&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The word “dwim” is an acronym for “Do What I Mean”; it indicates that this command can be used for many different jobs relating to comments, depending on the situation where you use it.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's really great to find built-in DWIM-powered Emacs commands. Third-party packages often include them too. I typically gravitate towards these commands and bind them in my Emacs config. Examples being upcase-dwim, downcase-dwim, or mc/mark-all-dwim.&lt;/p&gt;
&lt;p&gt;But what if the DWIM command doesn't exist or the author has written a command for what &lt;em&gt;they&lt;/em&gt; mean? This is your editor, so you can make it do what &lt;em&gt;you&lt;/em&gt; mean.&lt;/p&gt;
&lt;p&gt;Take for example, &lt;a href="https://orgmode.org/manual/Handling-Links.html"&gt;org-insert-link&lt;/a&gt;, bound to &lt;em&gt;C-c C-l&lt;/em&gt; by default. It's handy for inserting &lt;a href="https://orgmode.org/guide/Hyperlinks.html"&gt;org mode links&lt;/a&gt;. I used it so frequently that I quickly internalized its key binding. Having said that, I often found myself doing some lightweight preprocessing prior to invoking &lt;em&gt;org-insert-link&lt;/em&gt;. What if I can &lt;em&gt;make org-insert-link do what I mean&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;What do I mean?&lt;/h2&gt;
&lt;h3&gt;Use URLs when in clipboard&lt;/h3&gt;
&lt;p&gt;If the URL is already in the clipboard, don't ask me for it. Just use it.&lt;/p&gt;
&lt;h3&gt;Use the region too&lt;/h3&gt;
&lt;p&gt;If I have a region selected and there's a URL in the clipboard, just sort it out without user interaction.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-dwim-do-what-i-mean/link-this-text.gif" /&gt;&lt;/p&gt;
&lt;h3&gt;Automatically fetch titles&lt;/h3&gt;
&lt;p&gt;Automatically fetch URL titles from their HTML tag, but ask me for tweaks before insertion.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-dwim-do-what-i-mean/do-what-i-mean.gif" /&gt;&lt;/p&gt;
&lt;h3&gt;Fallback to org-insert-link&lt;/h3&gt;
&lt;p&gt;If my DWIM rules don't apply, fall back to using good ol' &lt;a href="https://orgmode.org/manual/Handling-Links.html"&gt;org-insert-link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My most common use case here is when editing an existing link where I don't want neither its title nor URL automatically handled.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-dwim-do-what-i-mean/edit-link.gif" /&gt;&lt;/p&gt;
&lt;h2&gt;The code&lt;/h2&gt;
&lt;p&gt;This is your own DWIM command that does what &lt;em&gt;you&lt;/em&gt; mean. Strive to write a clean implementation, but hey you can be forgiven for not handling all the cases that other folks &lt;em&gt;may&lt;/em&gt; want or inlining more code than usual. The goal is to bend your editor a little, not write an Emacs package.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(defun ar/org-insert-link-dwim ()
  &amp;quot;Like `org-insert-link' but with personal dwim preferences.&amp;quot;
  (interactive)
  (let* ((point-in-link (org-in-regexp org-link-any-re 1))
         (clipboard-url (when (string-match-p &amp;quot;^http&amp;quot; (current-kill 0))
                          (current-kill 0)))
         (region-content (when (region-active-p)
                           (buffer-substring-no-properties (region-beginning)
                                                           (region-end)))))
    (cond ((and region-content clipboard-url (not point-in-link))
           (delete-region (region-beginning) (region-end))
           (insert (org-make-link-string clipboard-url region-content)))
          ((and clipboard-url (not point-in-link))
           (insert (org-make-link-string
                    clipboard-url
                    (read-string &amp;quot;title: &amp;quot;
                                 (with-current-buffer (url-retrieve-synchronously clipboard-url)
                                   (dom-text (car
                                              (dom-by-tag (libxml-parse-html-region
                                                           (point-min)
                                                           (point-max))
                                                          'title))))))))
          (t
           (call-interactively 'org-insert-link)))))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Org web tools package&lt;/h2&gt;
&lt;p&gt;I showed how to write your own DWIM command, so you can make Emacs do what ✨you✨ mean. &lt;em&gt;ar/org-insert-link-dwim&lt;/em&gt; was built for my particular needs.&lt;/p&gt;
&lt;p&gt;Having said all of this, alphapapa has built a great package with helpers for the org web/link space. It doesn't do what I mean (for now anyway), but it may work for you: &lt;a href="https://github.com/alphapapa/org-web-tools"&gt;org-web-tools: View, capture, and archive Web pages in Org-mode&lt;/a&gt;[^2].&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Tue, 01 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/emacs-dwim-do-what-i-mean</guid></item><item><title>Distributed Systems with Node.js: Humble Books Bundle</title><link>https://thomashunter.name/posts/2021-06-01-distributed-systems-with-nodejs-humble-books-bundle</link><author>Thomas Hunter II</author><pubDate>Tue, 01 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://thomashunter.name/posts/2021-06-01-distributed-systems-with-nodejs-humble-books-bundle</guid></item><item><title>The cost of HTTPS connections - improve site speed by self hosting fonts</title><link>https://hartenfeller.dev/blog/selfhosting-fonts-performance</link><description>The loading speed of my site improved dramatically in unideal conditions by not loading fonts from Google Fonts but self hosting them instead.</description><author>Philipp Hartenfeller Blog RSS Feed</author><pubDate>Tue, 01 Jun 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://hartenfeller.dev/blog/selfhosting-fonts-performance</guid></item><item><title>On "anti Facebook" platforms, in Europe and beyond</title><link>https://stop.zona-m.net/2021/05/on-anti-facebook-platforms-in-europe-and-beyond/</link><description>&lt;p&gt;Quick! How would you feel about these events:&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 01 Jun 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/on-anti-facebook-platforms-in-europe-and-beyond/</guid></item><item><title>Easily showcase your Google Scholar metrics in Jekyll</title><link>https://jonathanchang.org/blog/easily-showcase-your-google-scholar-metrics-in-jekyll/</link><description>&lt;p&gt;Whether you’re applying for academic positions, comparing yourself to your colleagues, or determining whether people on Twitter really deserve their grant funding, it’s important to look at scholarly metrics such as &lt;a href="https://en.wikipedia.org/wiki/H-index"&gt;h-index&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Author-level_metrics#i10-index"&gt;i10-index&lt;/a&gt;, and number of citations. However, showing them off directly in your CV is a major bummer, especially when you’re an &lt;a href="https://www.theguardian.com/higher-education-network/2015/feb/06/academic-superstars-stop-playing-to-the-cameras-and-get-back-to-your-labs"&gt;academic superstar&lt;/a&gt; and are too busy to update these vital statistics for all the conferences, grants, and positions you’re applying for! In this blog post, I’ll tell you three easy steps to automatically fetch these stats from &lt;a href="https://scholar.google.com/citations?user=z5el-twAAAAJ"&gt;Google Scholar&lt;/a&gt; and display them on your online vita with &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;
  &lt;p&gt;First, take the below code, save it as a new file, &lt;code&gt;scholar_stats.rb&lt;/code&gt;, and place it in your &lt;a href="https://jekyllrb.com/docs/plugins/installation/"&gt;&lt;code&gt;_plugins&lt;/code&gt; folder&lt;/a&gt;.&lt;sup class="footnote-ref"&gt;&lt;a href="#fn1" id="fnref1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
  &lt;div class="language-ruby highlighter-rouge"&gt;
    &lt;div class="highlight"&gt;
      &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open-uri'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'nokogiri'&lt;/span&gt;
&lt;p&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Jekyll&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScholarStats&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Generator&lt;/span&gt;
&lt;span class="c1"&gt;# Replace &lt;code&gt;SCHOLAR_ID&lt;/code&gt; with your own Google Scholar ID&lt;/span&gt;
&lt;span class="no"&gt;SCHOLAR_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;‘XXXXXXXXXX’&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="no"&gt;SCHOLAR_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;‘&lt;a href="http://scholar.google.com/citations?hl=en&amp;amp;amp;user="&gt;http://scholar.google.com/citations?hl=en&amp;amp;amp;user=&lt;/a&gt;’&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SCHOLAR_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;SCHOLAR_ID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tbl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;‘table’&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;
&lt;span class="n"&gt;tbl_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;‘id’&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;SCHOLAR_ID&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;tbl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;‘tr’&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="n"&gt;cell_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;‘td’&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tbl_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cell_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;’-’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;’_’&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cell_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;‘scholar’&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tbl_data&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Second, update the &lt;code&gt;SCHOLAR_ID&lt;/code&gt; constant with your Google Scholar ID. This is taken from the link for your Google Scholar profile, like &lt;code&gt;https://scholar.google.com/citations?user=z5el-twAAAAJ&lt;/code&gt;, and it’s the characters after the &lt;code&gt;user=&lt;/code&gt; part. The &lt;code&gt;SCHOLAR_ID&lt;/code&gt; for this Scholar profile would therefore be &lt;code&gt;z5el-twAAAAJ&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you might need to add these to your &lt;code&gt;Gemfile&lt;/code&gt; and run &lt;code&gt;bundle install&lt;/code&gt;:&lt;/p&gt;
&lt;div class="language-ruby highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"nokogiri"&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"open-uri"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Or install them yourself with &lt;code&gt;gem install open-uri nokogiri&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once you’ve done this, you should have access to a few new variables in your Jekyll installation. You can use them in the Markdown or HTML documents using Liquid templates like so:&lt;/p&gt;
&lt;div class="highlighter-rouge"&gt;
  &lt;div class="highlight"&gt;
    &lt;pre class="highlight"&gt;&lt;code&gt;* [Profile](https://scholar.google.com/citations?user={{ site.data.scholar.id }})
* Citations: {{ site.data.scholar.citations }}
* h-index: {{ site.data.scholar.h_index }}
* i10-index: {{ site.data.scholar.i10_index }}
&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This Markdown code on my Jekyll site translates to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://scholar.google.com/citations?user=z5el-twAAAAJ"&gt;Profile&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Citations: 2878&lt;/li&gt;
  &lt;li&gt;h-index: 16&lt;/li&gt;
  &lt;li&gt;i10-index: 17&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now sit back, relax, and watch as your numbers go up!&lt;/p&gt;
&lt;section class="footnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn1"&gt;
      &lt;p&gt;If you are using something like &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt; to publish your site, you might not be able to use custom plugins! Instead, generate your Jekyll site locally and then push the generated static files up to your git repository. &lt;a class="footnote-backref" href="#fnref1"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/section&gt;</description><author>Jonathan Chang</author><pubDate>Tue, 01 Jun 2021 00:00:00 GMT</pubDate><guid isPermaLink="true">https://jonathanchang.org/blog/easily-showcase-your-google-scholar-metrics-in-jekyll/</guid></item><item><title>Traderino and its first week of automated cryptocurrency trading</title><link>https://ailef.tech/2021/05/31/traderino-and-its-first-week-of-automated-cryptocurrency-trading/</link><description>This is the follow-up to my previous post about Traderino, a bot that trades cryptocurrency futures on Binance and reports his action on Telegram. Jump to the previous article if you want to know more, or keep reading to see what happened during his first week of simulated trading. Timing has been far from perfect [&amp;#8230;]</description><author>AILEF</author><pubDate>Mon, 31 May 2021 09:15:36 GMT</pubDate><guid isPermaLink="true">https://ailef.tech/2021/05/31/traderino-and-its-first-week-of-automated-cryptocurrency-trading/</guid></item><item><title>Blowout Creek</title><link>https://sam.hooke.me/trip/2021/05/blowout-creek/</link><description>&lt;div id="map-main"&gt;&lt;/div&gt;




&lt;p&gt;No trip report yet.&lt;/p&gt;</description><author>Sam Hooke</author><pubDate>Mon, 31 May 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://sam.hooke.me/trip/2021/05/blowout-creek/</guid></item><item><title>Galley Cabinets</title><link>https://dustin.lammiman.ca/teardrop/galley1/</link><description>&lt;p&gt;We've got all the galley cabinets framed in. The stuff above the counters to will all be sliding doors. Cooler is on a shelf that pulls out, and has a 12v electrical socket it plugs into. Bellow the cabinet we still need to build some drawers and cabinet doors.&lt;/p&gt;</description><author>Dustin.Lammiman</author><pubDate>Mon, 31 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://dustin.lammiman.ca/teardrop/galley1/</guid></item><item><title>Python Dataclasses from Scratch</title><link>https://xavd.id/blog/post/python-dataclasses-from-scratch/</link><description>Learn about Python's dataclass module and implement a working version from scratch!&lt;br /&gt;&lt;br /&gt;&lt;a href="https://xavd.id/blog/post/python-dataclasses-from-scratch/"&gt;Read the whole thing&lt;/a&gt;.</description><author>The David Brownman Blog</author><pubDate>Mon, 31 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xavd.id/blog/post/python-dataclasses-from-scratch/</guid></item><item><title>June</title><link>https://gyani.net/blog/june/</link><description>.. almost half of the year is gone - 
		  This is going to be a brief post. Kinda sleepy, don’t feel like typing a lot. Will just summarize the month and set out some goals. Retrospective Running: I wanted to run 172k minimum, and 200k ideally. I ran 201k, I was supposed to get a game at 200k. I...</description><author>Gyanendra Mishra</author><pubDate>Sun, 30 May 2021 21:30:00 GMT</pubDate><guid isPermaLink="true">https://gyani.net/blog/june/</guid></item><item><title>Let Robot Overlords Inspect Your Butt</title><link>https://nonhuman.party/post/robots_inspect_your_butt/</link><description>As a consumer of food; has it ever occurred to you that despite the great power of government; and its involvement in the food supply; there are still black markets for food?</description><author>Non-Human Party</author><pubDate>Sun, 30 May 2021 18:13:32 GMT</pubDate><guid isPermaLink="true">https://nonhuman.party/post/robots_inspect_your_butt/</guid></item><item><title>Elegy for a Profession</title><link>https://cmdev.com/blog/2021-05-30-elegyforaprofession/</link><description>A song of the discarded</description><author>The Cranky Developer on Crater Moon Development</author><pubDate>Sun, 30 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://cmdev.com/blog/2021-05-30-elegyforaprofession/</guid></item><item><title>A popular comic character just escaped from control freaks</title><link>https://stop.zona-m.net/2021/05/a-popular-comic-character-just-escaped-from-control-freaks/</link><description>&lt;p&gt;This time, at least.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sun, 30 May 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/a-popular-comic-character-just-escaped-from-control-freaks/</guid></item><item><title>2021–05–30: PinePhone keyboard – more observations</title><link>https://xnux.eu/log/#035</link><author>megi's PinePhone Development Log</author><pubDate>Sun, 30 May 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#035</guid></item><item><title>Review: 12 Week Year</title><link>http://evantravers.com/articles/2021/05/29/review-12-week-year/</link><description>&lt;p&gt;&lt;img alt="" src="https://images.evantravers.com/articles/2021/05/12wy.JPG" /&gt;&lt;/p&gt;&lt;p&gt;After several false starts I finally finished reading &lt;em&gt;The 12 Week Year&lt;/em&gt; at the end of last year. I have mixed feelings about the full system they sell… but I heartily recommend the base principles.&lt;/p&gt;
&lt;p&gt;The book starts with a simple claim: we already know what to do, we just don't do it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The barrier standing between you and the life you are capable of living is a lack of consistent execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This really did grab me. Even with all my systems and automation, the most important tasks and projects seem perpetually shunted into a never-arriving future. The first half of the book argues that we abandon our systems before a chance of effect. They present a useful a mental model called the Emotional Cycle of Change:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Uninformed optimism&lt;/strong&gt;: &amp;quot;Running will be awesome!&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Informed pessimism&lt;/strong&gt;: &amp;quot;My feet hurt!&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valley of despair&lt;/strong&gt;: &amp;quot;Is it always like this?&amp;quot; (This is where we usually quit!)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Informed optimism&lt;/strong&gt;: &amp;quot;It's just sore… and I can do this tomorrow.&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Success and fulfillment&lt;/strong&gt;: &amp;quot;I'm a runner.&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The authors recommend a two-fold system: first creating an aspirational vision, then planning within the twelve weeks. I tend to fight the touchy-feely aspects of the aspirational vision, but it has helped me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the critical first step to executing well is creating and maintaining a compelling vision of the future that you want even more than you desire your own short-term comfort.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After you've designed the vision, you plan within the constraints of the twelve weeks… and use the bounds to create what the authors call &amp;quot;productive tension&amp;quot;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a heightened sense of urgency and increased focus on the critical few…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While I'm all about the &lt;a href="http://evantravers.com/articles/2019/09/01/review-essentialism-by-greg-mckeown/"&gt;critical few&lt;/a&gt;, I find constant deadlines to be &lt;em&gt;stressful&lt;/em&gt;. However, I know folks who love deadlines. Everyone responds &lt;a href="https://bookworm.fm/107/"&gt;differently to the same motivations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Despite my apprehension with the celebration of urgency, I've been experimenting with some of the core principles over the last six months.&lt;/p&gt;
&lt;p&gt;In my task manager I have had a couple odd &amp;quot;projects&amp;quot;: Leadership at Work. House. Marriage. They contain tasks but they never really complete. It's hard to feel like I'm making progress, no celebration or review… these areas are simultaneously the most important areas of my life… and the places where the most important tasks go to hide for &lt;em&gt;years&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt="Projects in a task manager, showing Q2" src="https://images.evantravers.com/articles/2021/05/Q2images.jpeg" /&gt;
&lt;/figure&gt;
&lt;p&gt;I &amp;quot;canceled&amp;quot; the Eternal Projects, and migrated their contents to new projects labeled something like &amp;quot;&amp;lt;project&amp;gt; Q1&amp;quot;. I also gave each project a Deadline corresponding with the end of my first 12 week year. I'm now on my second or third 12 week year. Merely having a goal of what I want to accomplish in the formerly &amp;quot;Eternal Projects&amp;quot; within those 12 weeks, and having regular times to review and adjust my strategies for accomplishing those tasks means that many formerly &amp;quot;stuck&amp;quot; tasks have been completed.&lt;/p&gt;
&lt;p&gt;This past quarter I started a proper 12WY plan: identifying the two or three practices that will bring me closer to our family's aspirational goal. I also am using their idea of &amp;quot;strategic blocks&amp;quot; (very similar to &lt;a href="http://evantravers.com/articles/2019/02/09/review-deep-work-by-cal-newport/"&gt;Newport's Deep Work time&lt;/a&gt;) to make sure that I put time on those critical few actions. I still lack a proper scoreboard like they recommend, but I will certainly implement this next quarter. (Probably in my &lt;a href="http://evantravers.com/articles/series/simple-markdown-zettelkasten/"&gt;Simple Markdown Zettelkasten&lt;/a&gt;!) For now, I can see the completed tasks in each project at the end of a 12WY.&lt;/p&gt;
&lt;p&gt;In many ways this has all pointed out a weakness in my previous implementations of &lt;a href="http://evantravers.com/articles/2020/12/28/plan-your-year/"&gt;Plan Your Year&lt;/a&gt; (which I still &lt;em&gt;love&lt;/em&gt;!) In previous years I never revisited the plan so the first quarter of the year was bursting with productive action… dying somewhere around April. I think the two systems are fully compatible and intend to use them together. I would say that Plan Your Year is more family oriented, and less deadline-focused, if that is something that tends to drag your motivation more than propel it.&lt;/p&gt;
&lt;p&gt;I still feel &lt;em&gt;very&lt;/em&gt; strangely toward the book. I first feel aversion towards the celebration of deadlines, but the wisdom of being diligent with the time we have coupled with the helpful mental model of the Emotional Cycle of Change has made this a book I keep talking about.&lt;/p&gt;</description><author>trv.rs</author><pubDate>Sat, 29 May 2021 23:14:00 GMT</pubDate><guid isPermaLink="true">http://evantravers.com/articles/2021/05/29/review-12-week-year/</guid></item><item><title>What I wish I knew before building a Gatsby source plugin</title><link>https://tommasoamici.com/blog/what-i-wish-i-knew-before-building-a-gatsby-source-plugin/</link><description>What I learned building a Gatsby plugin to source my collection of board games through BoardGameGeek's API.&lt;hr /&gt;&lt;a href="https://tommasoamici.com/blog/what-i-wish-i-knew-before-building-a-gatsby-source-plugin/"&gt;Read more&lt;/a&gt;</description><author>Tommaso Amici</author><pubDate>Sat, 29 May 2021 23:02:41 GMT</pubDate><guid isPermaLink="true">https://tommasoamici.com/blog/what-i-wish-i-knew-before-building-a-gatsby-source-plugin/</guid></item><item><title>Army Of The Dead</title><link>https://olshansky.info/movie/army_of_the_dead/</link><description>Olshansky's review of Army Of The Dead</description><author>🦉 olshansky 🦁</author><pubDate>Sat, 29 May 2021 21:47:06 GMT</pubDate><guid isPermaLink="true">https://olshansky.info/movie/army_of_the_dead/</guid></item><item><title>Trade Wars 2002 and its connection to Eve Online</title><link>https://nicolaiarocci.com/trade-wars-2002-and-its-connection-to-eve-online/</link><description>&lt;p&gt;Trade Wars 2002 was a  great 1991 online game I hosted on one of my BBSes &lt;a href="https://nicolaiarocci.com/a-trip-down-memory-lane-fidonet-and-usenet/"&gt;back
in the day&lt;/a&gt;. Not sure if it was Lorien or Phoenix BBS; it might have been
the latter given the game&amp;rsquo;s release date. I totally forgot TW2002 until
yesterday when I spotted this &lt;a href="https://if50.substack.com/p/1991-trade-wars-2002"&gt;1991: Trade Wars 2002&lt;/a&gt; article on the &lt;em&gt;50
Years of Text Games&lt;/em&gt; newsletter.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Trade Wards 2002" src="https://nicolaiarocci.com/images/trade-wars-2002.png" /&gt;&lt;/p&gt;
&lt;p&gt;I humbly confess that, until yesterday, I never made the obvious connection
between TW2002 and Eve Online. That&amp;rsquo;s quite startling considering that I&amp;rsquo;ve
been a beta player first and then an avid Eve player for a few years (Eve was
also the last game I seriously played on a computer.)&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Sat, 29 May 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/trade-wars-2002-and-its-connection-to-eve-online/</guid></item><item><title>Assembling the Nearer keyboard</title><link>https://voussoir.net/writing/kb_nearer</link><description>&lt;article&gt;
&lt;p&gt;&lt;a href="/writing"&gt;Back to writing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a class="tag_link" href="/writing/tags/electronics"&gt;[electronics]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Assembling the Nearer keyboard&lt;/h1&gt;&lt;ol id="table_of_contents"&gt;Table of contents&lt;li&gt;&lt;a href="#glamour_shots"&gt;Glamour shots&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#quick_links"&gt;Quick links&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#designing_the_layout"&gt;Designing the layout&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#switches_caps"&gt;Switches &amp;amp; caps&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#microcontroller"&gt;Microcontroller&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#designing_the_switchplate"&gt;Designing the switchplate&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#designing_the_wiring_matrix"&gt;Designing the wiring matrix&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#building_the_firmware"&gt;Building the firmware&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#arrival_of_the_switchplate"&gt;Arrival of the switchplate&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#hot_glue_crimes_against_humanity"&gt;Hot glue crimes against humanity&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#wiring"&gt;Wiring&lt;/a&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="#columns"&gt;Columns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#diodes"&gt;Diodes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#rows"&gt;Rows&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#controller"&gt;Controller&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#the_base"&gt;The base&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#stabilizers"&gt;Stabilizers&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#conclusion"&gt;Conclusion&lt;/a&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="#price_breakdown"&gt;Price breakdown&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#what_id_do_differently"&gt;What I'd do differently&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id="glamour_shots"&gt;Glamour shots&lt;a class="header_anchor_link" href="#glamour_shots"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/glamour_1.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/glamour_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/glamour_2.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/glamour_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/glamour_3.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/glamour_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/glamour_4.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/glamour_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
&lt;center&gt;

&lt;/center&gt;
&lt;/p&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;a class="header_anchor_link" href="#introduction"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I've wanted a mechanical keyboard for a long time, but have never been happy with the options available to purchase. The main reason, as trivial as it will sound, is that I prefer the Home/End block of keys to be arranged vertically. The standard arrangement is horizontal, and I've never seen a mechanical keyboard with the vertical arrangement.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/home_end.png" /&gt;&lt;/p&gt;
&lt;p&gt;This alone was enough to get me interested in building my own keyboard, but I put it off for a long time because I knew it would be expensive. Finally, I've gotten around to it. The solution to price anxiety is to start buying some of the parts before calculating the total cost of the project so that you can tell yourself "Well, I can't stop now!".&lt;/p&gt;
&lt;p&gt;The result is not perfect, but it's nearer.&lt;/p&gt;
&lt;p&gt;I find myself leaning towards the word "assembling" rather than "building" for this project. I have been continuously amazed at how much work the keyboard community has put into the resources and tooling that make assembling a keyboard honestly very easy. Everyone has coordinated around the keyboard-layout-editor data format which makes the transition between each tool at each step of the process totally seamless. Really, I feel like I didn't do a whole lot except for the soldering. But we'll get to that in a bit.&lt;/p&gt;
&lt;p&gt;The only actual hard part is making the choices — switch type, plate material, base material, layout, keycaps. You'll see people talk about the sound, feel, and resonance of each part, but it's hard to tell the difference on their videos.&lt;/p&gt;
&lt;p&gt;This article will be a mixture of build log, diary, and tutorial. It's a bit long-winded and over-detailed, but that's because I have a new keyboard I need to break in so I want to type a lot. Maybe you'll find something here that other build logs omit. I hope it is useful to somebody.&lt;/p&gt;
&lt;h2 id="quick_links"&gt;Quick links&lt;a class="header_anchor_link" href="#quick_links"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://www.keyboard-layout-editor.com"&gt;http://www.keyboard-layout-editor.com&lt;/a&gt; (&lt;a href="https://github.com/ijprest/keyboard-layout-editor"&gt;github&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://builder.swillkb.com"&gt;http://builder.swillkb.com&lt;/a&gt; (&lt;a href="https://github.com/swill/kad"&gt;github&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kbfirmware.com"&gt;https://kbfirmware.com&lt;/a&gt; (&lt;a href="https://github.com/ruiqimao/qmkbuilder"&gt;github&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/qmk/qmk_toolbox/releases"&gt;https://github.com/qmk/qmk_toolbox/releases&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="designing_the_layout"&gt;Designing the layout&lt;a class="header_anchor_link" href="#designing_the_layout"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The layout was designed with &lt;a href="http://www.keyboard-layout-editor.com"&gt;keyboard-layout-editor.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is the final layout of Nearer:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_final.png" /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[{x:1.5,a:7},"F13","F14","F15","F16",{x:0.5},"F17","F18","F19","F20",{x:0.5},"F21","F22","F23","F24",{x:0.25,sm:"cherry"},"Scroll Lock","Num Lock"],
[{x:0.25},"Esc",{x:0.25},"F1","F2","F3","F4",{x:0.5},"F5","F6","F7","F8",{x:0.5},"F9","F10","F11","F12",{x:0.25},"Print Screen","Pause Break",{x:0.25},"A/한","한자"],
[{y:0.25,x:0.5},"~","1","2","3","4","5","6","7","8","9","0","-",{w:2},"Back",{x:0.25},"Home","End",{x:0.25},"Back","/","*","-"],
[{w:1.5},"Tab","Q","W","E","R","T","Y","U","I","O","P","[","]","\\",{x:0.25},"Insert","PgUp",{x:0.25},"7","8","9",{h:2},"+"],
[{w:1.5},"Caps Lock","A","S","D",{n:true},"F","G","H",{n:true},"J","K","L",{a:5},":\n;","\"\n'",{a:7,w:2},"Enter",{x:0.25},"Delete","PgDn",{x:0.25},"4",{n:true},"5","6"],
[{w:1.5},"Shift","Z","X","C","V","B","N","M",{a:5},"&amp;lt;\n,","&amp;gt;\n.","?\n/",{a:7,w:2},"Shift","=",{x:2.5},"1","2","3",{h:2},"Enter"],
[{y:-0.75,x:14.75},"↑"],
[{y:-0.25,w:1.5},"Ctrl","Win","Alt",{w:6},"Space","Alt","Menu",{w:1.5},"Ctrl",{x:4,w:2},"0","."],
[{y:-0.75,x:13.75},"←","↓","→"]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's how the layout evolved. I started by drawing my previous keyboard, a Logitech Classic Keyboard 200. It's just a regular $10 office keyboard, but it's the one responsible for my obsession with vertical Home/End.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_0.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;F13-24&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The whole reason I started this project was so that I could have my vertical home/end block, but I felt I should come up with some more customization ideas to justify the time and money. Well... I wouldn't mind having a dozen more F keys to bind, so let's go with that.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_1.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ortholinear&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I wasn't originally interested in ortholinear keyboards, but I saw &lt;a href="https://www.youtube.com/watch?v=Ho_CFfdsmc8" title="Are Ortholinear Keyboards Better Than Staggered?"&gt;this video&lt;/a&gt; by Ben Vallack and was convinced to try it out. Watching this video is like watching the mask come off a Scooby Doo villain. It would be one thing if the traditional QWERTY stagger matched the natural curvature of the hands or something, but the stagger leans in the same direction for both hands. There's just no excuse. So while I'm not ready to commit to a true ergo layout with staggered columns or split halves, I'm willing to make the ortho switch. I'll admit, despite all the time I've spent typing, I'm still really bad at reliably hitting numbers on the number row. Ortho should make that easier once I know that 4 and 7 are above F and J.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KP-Backspace&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I always hate when I'm reaching for End or numpad-slash and accidentally hit Num Lock, so I knew from the beginning I'd be moving Num Lock and putting a backspace in its place. That meant putting Num Lock up with Scroll Lock, which makes for a nice 2×2 arrangement. The best part is that Print Screen and Pause/Break are the only ones I plan on pressing regularly &lt;a class="footnote_link" href="#footnote_text_1" id="footnote_link_1" title="[1] Pause is used to pause commandline applications, and Ctrl+Break is used to kill commandline applications that aren't terminating on Ctrl+C."&gt;[1]&lt;/a&gt;, so it's nice to not have Scroll Lock in between them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A/한&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I gave myself a dedicated English/한글 toggle key so I could stop relying on my &lt;a href="https://git.voussoir.net/voussoir/ahk/src/branch/master/custom_hangul.ahk"&gt;Alt+Menu AHK script&lt;/a&gt;. The AHK solution works perfectly fine, but I would occasionally blunder the Menu keypress and be left navigating the toolbar of the current application thanks to the Alt press. I put it next to Right Shift because why is that key so long in the first place.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_2.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_3.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shift-equals&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I was having a hard time fitting the Equals button between Minus and Backspace without reducing Backspace to 1u or taking 1 and Q out of alignment, which I wasn't willing to do. I decided to put it next to shift, and while I knew it would take some time to get used to, it's not unreasonable. That's right, I'd rather break my muscle memory on the equals key than on the home/end block. That's how dedicated I am.&lt;/p&gt;
&lt;p&gt;This meant moving A/한 above the numpad. The downside is that this is a much farther reach, but the upside is I have plenty of room up there for a 한자 key too, which I wouldn't have included otherwise. Realistically, I could just map Ctrl+A한 to 한자, so if I ever come up with another need for that key that's what I'll do. You know, after I use up all 24 F keys.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_4.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_5.png" /&gt;&lt;/p&gt;
&lt;p&gt;Although I liked the idea of the 1.25u Ctrl giving the left edge of the board a sort of curvature, I found during prototyping that pressing hotkeys is much easier with a 1.5u Ctrl because I tend to strike the leftmost edge of the Ctrl key, especially now that A and Z are physically closer to the left edge of the board — the hand posture for Ctrl+Z is much tighter on this layout compared to the original.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/layout_final.png" /&gt;&lt;/p&gt;
&lt;p&gt;Speaking of hand posture, let's compare the ortholinear layout with the traditional stagger more closely:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/stagger_translucent.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/stagger.gif" /&gt;&lt;/p&gt;
&lt;p&gt;By far the biggest letter changes are on the Z row, with each key moving 0.5u to the left. You can see how much Ctrl hotkeys have changed now that the 0.75u horizontal gap is completely gone. For the first few weeks of using this keyboard, I was constantly overshooting my copy-pastes.&lt;/p&gt;
&lt;p&gt;The Q row feels almost identical, and I'm happy to report that the 1u backslash is not giving me any trouble. The number row is almost a full 1u farther to the right now, which is mostly not a problem since I don't feel like I ever really internalized the old stagger anyway. However, underscore did give me a hard time since it's almost a full 1u to the right from where it used to be, and now neighbors backspace. Equal has definitely taken some getting used to, but I've got the muscle memory for it now.&lt;/p&gt;
&lt;p&gt;&lt;a class="footnote_text" href="#footnote_link_1" id="footnote_text_1"&gt;[1]&lt;/a&gt; Pause is used to pause commandline applications, and Ctrl+Break is used to kill commandline applications that aren't terminating on Ctrl+C.&lt;/p&gt;
&lt;h2 id="switches_caps"&gt;Switches &amp;amp; caps&lt;a class="header_anchor_link" href="#switches_caps"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The layout has 118 keys, and I knew I wanted a few spares of each size. I went with 140×&lt;a href="https://kbdfans.com/products/switches-70"&gt;Gateron Yellow from KBDFans&lt;/a&gt; and 129×&lt;a href="https://www.aliexpress.com/item/32850729893.html"&gt;blank black PBT caps from NPKC&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/switches.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/switches.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For as long as I've been thinking about buying a mechanical keyboard, I've always imagined I would choose Blues for the clicky sound. But, I read various forum threads about clicky vs linear and saw that a lot of clicky users find them to be a nuisance after a while, or felt that actuation pressure should be learned by muscle memory instead of pushing through the click. Some day I'm sure I'll do another keyboard and I'd like to try clickies eventually, but I decided to do linear Yellows this time. Although typing on the linears isn't exactly quiet, you certainly can press keys silently if you need to. The typing experience is perfectly pleasant, and after only a few days of using the Nearer I found my rubber dome keyboard terrible in comparison.&lt;/p&gt;
&lt;p&gt;Ordering the caps was kind of weird. The AliExpress page has product variant buttons for "100 pcs", "200 pcs", etc., but the seller has their own pricing scheme outlined in the product description. You're supposed to tell them what keys you need and they'll calculate a price by considering 2u keys to be 2× the price of 1u keys, and so forth, and that's how many "pieces" you need. Well that's not what AliExpress's variant ordering system is designed for, so I'm really not sure how this was supposed to go down. I just chose the variant that was close to, although a little bit less than the value that I calculated, and they accepted the payment without question. So I guess I got a discount. Your results may vary if you need pieces that aren't close to any of the variants.&lt;/p&gt;
&lt;p&gt;Another thing I didn't expect is that the homing keys don't have raised bumps on them. Instead, the surface of the key has a deeper indent than the rest. Visually, it's very hard to distinguish, but in practice I'm not having any trouble with them.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/keycaps.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/keycaps.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I've got 22 switches left over, either to use as repairs or just to play around with. I also made sure to buy at least two extra of each cap size.&lt;/p&gt;
&lt;h2 id="microcontroller"&gt;Microcontroller&lt;a class="header_anchor_link" href="#microcontroller"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the controller I chose the 24-pin &lt;a href="https://keeb.io/products/elite-c-low-profile-version-usb-c-pro-micro-replacement-atmega32u4"&gt;Elite-C V4&lt;/a&gt; because I wanted to use USB-C. That's pretty much the only reason.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/controller_1.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/controller_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/controller_2.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/controller_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="designing_the_switchplate"&gt;Designing the switchplate&lt;a class="header_anchor_link" href="#designing_the_switchplate"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The switchplate is made by taking the output of keyboard-layout-editor and pasting it into &lt;a href="http://builder.swillkb.com"&gt;builder.swillkb&lt;/a&gt;. I chose an 8mm edge padding, 8mm edge radius, and a couple of 2.1mm holes so I can use 2M screws.&lt;/p&gt;
&lt;p&gt;If I were to do this again, I'd give myself a little more padding between the screw holes and the edge of the plate, and I'd add more intermediate screw holes. My switchplate has a very slight bend to it and I don't have any means of making it perfectly flat, so a few extra screws would help me get the plate to sit more flush against the base.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/switchplate.svg" /&gt;&lt;/p&gt;
&lt;p&gt;Because this layout would be totally new to me, I knew I needed to be comfortable with the design before having it cut in metal. I bought a foam posterboard, printed the layout on paper, and spent about four hours across a couple of &lt;a href="/writing/download_podcasts"&gt;podcast-listening&lt;/a&gt; sessions to cut out each switch hole with a craft knife.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_01.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_01.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_02.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_02.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_03.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_03.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_04.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_04.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_05.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_05.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_06.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_06.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_07.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_07.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_08.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_08.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_09.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_09.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I'm sure you're wondering about the gray spacebar. This was in fact the result of spontaneous inspiration, an experience not much different from true enlightenment, which came to me after an intense bout of deeply personal meditation and was fully crystalized when the keycap vendor said "We don't have 6u space in black, only in gray".&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_10.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_10.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This prototype was definitely worth the time, because this is the point where I changed Ctrl from 1.25u to 1.5u. If I hadn't bought extra 1.5u caps, that wouldn't have been possible.&lt;/p&gt;
&lt;p&gt;After generating the final switchplate on swillkb, I had it cut through LaserBoost. Swillkb has a partnership with LaserGist, but I found that LaserGist's price &lt;em&gt;before&lt;/em&gt; shipping was about $20 higher than LaserBoost's price &lt;em&gt;after&lt;/em&gt; shipping. I tried comparing a few other options, but most either don't have instant price quoting or the prices weren't any better than LB.&lt;/p&gt;
&lt;p&gt;Switchplates should be 1.5mm thick for Cherry-style switches, which includes the Gaterons.&lt;/p&gt;
&lt;h2 id="designing_the_wiring_matrix"&gt;Designing the wiring matrix&lt;a class="header_anchor_link" href="#designing_the_wiring_matrix"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While the plate was still in the mail, I started working on the wiring matrix. This was the first time I had ever put together a piece of electronics and it was a great learning experience.&lt;/p&gt;
&lt;p&gt;Each key switch has two posts on the underside. Keyboards are wired with one post connected to a "column" wire and the other connected to a "row" wire. The controller sends signals out via the column wires, one column at a time, and listens to the row wires to see where the signal comes back. In effect, every key has an X, Y coordinate which the firmware then maps to letters / keycodes.&lt;/p&gt;
&lt;p&gt;A simple matrix for my keyboard would look like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/wiring_singleplex.svg" /&gt;&lt;/p&gt;
&lt;p&gt;But, there's a problem. Each of these wires needs to be soldered onto a separate pin of the controller, which means the controller must have at least &lt;code&gt;rows+columns&lt;/code&gt; available pins. That means I'd need 27 pins, but the Elite-C only has 24.&lt;/p&gt;
&lt;p&gt;The solution is what's called a duplex layout, where two physical columns are wired together as a single virtual column. Here's mine:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/wiring_duplex.svg" /&gt;&lt;/p&gt;
&lt;p&gt;The duplex layout halves the number of columns in exchange for doubling the number of rows. That brings me to 10+14=24 exactly. This does mean I have no pins left for Caps Lock or Num Lock LEDs. Instead, Caps Lock is indicated by pushing a button and seeing a capital letter come out.&lt;/p&gt;
&lt;p&gt;If you're willing to sacrifice your sanity to achieve maximum keyboarditude, the maximum number of keys you can address is &lt;code&gt;(pins/2)²&lt;/code&gt; — a 12×12 matrix with 144 keys in this case. You might have to do some gymnastics with your wires, since the physical layout would be far removed from the virtual layout, but it would be possible. Keep in mind that the controller doesn't know or care about the physical positions of the switches, as long as each one has a unique X, Y coordinate via the row and column wires.&lt;/p&gt;
&lt;p&gt;Anyway, here's the wiring diagram as seen from the underside, which is how we'll actually work on it, and in more realistic detail:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/wiring_bottom.svg" /&gt;&lt;/p&gt;
&lt;p&gt;And the wires join up to the controller like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/controller.svg" /&gt;&lt;/p&gt;
&lt;p&gt;You're seeing the final versions of these images, but make no mistake — this took several iterations to get right. In fact, while I was soldering the wires I realized that I had imagined it differently and just changed the design right then. You can optimize your controller pin mapping to reduce the length and overlaps of your wires, but it's not a big deal. Whatever happens, you can adjust the firmware to match whatever you put together.&lt;/p&gt;
&lt;p&gt;If you've seen other keyboard builds, you'll know that the wiring matrix requires diodes. I bought &lt;a href="https://www.ebay.com/itm/303201293688"&gt;these 1N4148 diodes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/diodes.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/diodes.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I know what diodes do — they allow electricity to only flow in one direction — but it took me a bit of studying to figure out how that applies to keyboards. I put together this drawing to illustrate my findings. In this picture, the black keys are the ones being pressed. Remember that the controller sends a signal out through one column at a time and listens to the rows, so here's C7 being lit up:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/why_diodes.svg" /&gt;&lt;/p&gt;
&lt;p&gt;The signal from the column wire is transfered through the switch and diode to the row wire, telling the controller the row coordinate of the pressed keys in the C7 column. The controller sees R11 and R14, so the pressed keys are C7+R11 and C7+R14, 7 and 0.&lt;/p&gt;
&lt;p&gt;If your keyboard doesn't have diodes, certain combinations of keypresses can carry the signal to places that trick the controller into believing some other keys are being pressed when they actually aren't. Notice how the signal is flowing through the 9 key in the wrong direction, from a row to a column, which lights up the C9 column. Even though 6's actual coordinate is C9+R12, the controller only knows that it sent a signal on C7 and got a response back on R12, so it thinks C7+R12 is pressed, which is 4.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/why_diodes_anim.gif" /&gt;&lt;/p&gt;
&lt;h2 id="building_the_firmware"&gt;Building the firmware&lt;a class="header_anchor_link" href="#building_the_firmware"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The plate was still in the mail at this point, and I felt confident about the wiring design, so I began doing the firmware on &lt;a href="https://kbfirmware.com"&gt;kbfirmware.com&lt;/a&gt;. Once again, the keyboard community has done all the hard work. All I'm doing is plugging in my information.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/firmware_1.png" /&gt;&lt;/p&gt;
&lt;p&gt;Not so fast, kbfirmware. Don't you know we're doing a duplex layout?&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/firmware_2.png" /&gt;&lt;/p&gt;
&lt;p&gt;Yeah, that's right, that's just how I like my duplex layouts.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/firmware_3.png" /&gt;&lt;/p&gt;
&lt;p&gt;As it happens, kbfirmware uses the exact opposite red/blue color scheme that I chose, but you can see how those diagonal jumps represent the long bridges I drew, connecting two physical columns into a single virtual column.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/firmware_4.png" /&gt;&lt;/p&gt;
&lt;p&gt;I drew my controller diagram with row and column numbers starting from 1, but on kbfirmware they start from 0. Filling in this table is a good workout for your looking-back-and-forth muscles, often underrepresented in other workout routines.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/firmware_5.png" /&gt;&lt;/p&gt;
&lt;p&gt;After downloading the firmware as a .hex file, it is loaded onto the controller using &lt;a href="https://github.com/qmk/qmk_toolbox/releases"&gt;QMK Toolbox&lt;/a&gt;. It did everything perfectly on the first try. Just turn on auto-flash, and use a paperclip or piece of wire to short RESET to GROUND twice to reset the microcontroller.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/writing/kb_nearer/firmware_6.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/firmware_7.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/firmware_7.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then, you can simulate keypresses by shorting a column pin to a row pin. And you can just end the whole project here, really. The rest of the keyboard is basically just decoration. It's functional as is.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/firmware_8.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/firmware_8.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/firmware_9.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/firmware_9.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="arrival_of_the_switchplate"&gt;Arrival of the switchplate&lt;a class="header_anchor_link" href="#arrival_of_the_switchplate"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The switchplate came in the mail:&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/prototype_to_plate.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/prototype_to_plate.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But something must have been wrong with the laser that day, because the plate had some alignment defects to the tune of 1mm in a couple of places. Normally I'm not one to complain to support, but this was expensive and beyond their stated tolerances. I took some pictures demonstrating the alignment with a ruler and some MS Paint annotations:&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/laserboost_wrong_1.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/laserboost_wrong_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/laserboost_wrong_2.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/laserboost_wrong_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LaserBoost immediately offered to express-mail a replacement for free. So in the end everything turned out fine.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/metal_plate.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/metal_plate.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I wouldn't want to miss this opportunity to show off how I transfered the switches from the first plate to the new plate. The cap- and switch-pullers I ordered in the mail wouldn't arrive for at least another month.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/puller_1.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/puller_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/puller_2.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/puller_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/puller_3.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/puller_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That's right, everyone. When you buy cap- and switch-pullers for $1.00 on ebay, you're giving in to Big Puller's brainwashing. Paperclips and tweezers were all you needed all along.&lt;/p&gt;
&lt;h2 id="hot_glue_crimes_against_humanity"&gt;Hot glue crimes against humanity&lt;a class="header_anchor_link" href="#hot_glue_crimes_against_humanity"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you press the switches into the plate, they make an audible click. The plastic body of the switch is shaped so that it grips the plate from below. But I couldn't shake the feeling that someday I'd be pulling a cap off of a switch and have the whole switch rip out of the plate, bringing a spaghetti dish of wires along with it. As fun as it was to put this together, I really, &lt;strong&gt;really&lt;/strong&gt; don't want to have to do any repairs. I decided, for better or worse, to reinforce the switches with a whole bunch of hot glue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: You're supposed to put your stabilizers in the plate before your switches! My stabilizers were in transit for more than a month by this point, so I just went ahead without them. I was able to add them retroactively but it wasn't ideal. See &lt;a href="#Stabilizers"&gt;#Stabilizers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Speaking of firsts, this was my first time working with hot glue. It's interesting stuff. I think if you use it once you'll either instantly hate it for its awful, stringy messiness; or you'll instantly love it for its versatility and speed.&lt;/p&gt;
&lt;p&gt;In the following photos, you should be able to identify three distinct stages of how it went:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I'm doing terrible at this.&lt;/li&gt;
&lt;li&gt;I'm doing great at this!!&lt;/li&gt;
&lt;li&gt;I need to do a little less great or I'm going to run out of hot glue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/hotglue_1.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/hotglue_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/hotglue_2.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/hotglue_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/hotglue_3.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/hotglue_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/hotglue_4.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/hotglue_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I ran out of hot glue. But, this dark period of my life was short-lived. I bought more glue and got back to work making angels cry:&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/hotglue_5.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/hotglue_5.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="wiring"&gt;Wiring&lt;a class="header_anchor_link" href="#wiring"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Finally, with the plate ready and switches set, it was time to begin wiring.&lt;/p&gt;
&lt;h3 id="columns"&gt;Columns&lt;a class="header_anchor_link" href="#columns"&gt; (§)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I began with the columns. I was hoping I could use a single strand of wire to connect the whole column, stripping off little gaps in the wire near the posts for soldering, but at this time I couldn't figure out a good way to do that. So, the linkage between each switch was done individually. I wound a bit of wire around each post, and in fact did a dry fit of the whole board before soldering anything.&lt;/p&gt;
&lt;p&gt;This took a long time.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_01.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_01.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_02.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_02.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_03.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_03.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_04.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_04.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_05.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_05.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_06.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_06.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I linked the duplexed columns, and held them down with tape since these long wires tend not to want to sit still.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_07.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_07.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_08.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_08.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then I began soldering. This was my first time soldering, and I think I did pretty alright.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_09.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_09.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_columns_10.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_columns_10.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="diodes"&gt;Diodes&lt;a class="header_anchor_link" href="#diodes"&gt; (§)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, I did the diodes. As you can see from the diagram, the diodes are soldered to the other post and reach down to the row wire. The black ring around the diode indicates the front, and should point away from the switch towards the row wire.&lt;/p&gt;
&lt;p&gt;I found that using the plate itself as a bending guide for the diode tails was perfect for helping me wrap them around the posts.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_diodes_1.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_diodes_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_diodes_2.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_diodes_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Unlike the column wires, I didn't dry-fit the whole board, because the other tail of each diode reaches down to the next one. So, I did dry fits and solders for each row, bottom to top.&lt;/p&gt;
&lt;p&gt;This took a long time.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_diodes_3.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_diodes_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_diodes_4.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_diodes_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_diodes_5.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_diodes_5.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_diodes_6.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_diodes_6.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="rows"&gt;Rows&lt;a class="header_anchor_link" href="#rows"&gt; (§)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next up was the rows. Remember how I wanted to strip the middles of the wires when I was doing the columns? Well now I was extra motivated to find a solution, because doing individual wires for the rows would mean three-way joins between each row segment and the accompanying diode, and I didn't want to deal with that.&lt;/p&gt;
&lt;p&gt;I found that I could use the soldering iron to melt away the insulation from the wire. This was very effective, but in retrospect was a BAD IDEA because it made the tip very dirty and difficult to solder with afterwards. More crimes against humanity. Sorry. I was able to clean the tip by repeatedly burning solder onto it and wiping it off, which burned away the rubber residue.&lt;/p&gt;
&lt;p&gt;This took a long time.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_01.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_01.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_02.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_02.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_03.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_03.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_04.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_04.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_05.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_05.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_06.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_06.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_07.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_07.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_08.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_08.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_09.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_09.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_10.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_10.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The downside of the duplex layout is that R1-R7 need to reach &lt;em&gt;allll&lt;/em&gt; the way over to the controller, so I tried to route the wires between the keys and keep it looking nice.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_11.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_11.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The underlying ocean of hot glue proved its worth by helping me seat some discarded pieces of diode tails, which I used as guides to stop the wires from flying around all over the place. Seriously, it's impossible to totally straighten a wire like this, so they always fling themselves to one side at every chance they get.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_rows_12.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_rows_12.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="controller"&gt;Controller&lt;a class="header_anchor_link" href="#controller"&gt; (§)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I hope you're ready for more hot glue crime because I've got more hot glue crime.&lt;/p&gt;
&lt;p&gt;I didn't want to glue the controller directly to the plate for two reasons. First, if for any reason I ever need to remove the controller, I don't want to be prying it off the metal and breaking anything. And second, I want the plug to be approximately centered along the backside of the keyboard base — not pressed directly up against the top. So, I did a little bit of manual 3D printing with everyone's favorite polymer.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_01.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_01.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_02.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_02.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_03.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_03.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_04.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_04.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_05.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_05.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And finally, I could make the last solder joins, from all the rows and columns to the controller.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_06.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_06.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_07.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_07.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_08.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_08.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I had been trying to keep the wires nice and pretty up to this point, but some of these final linkages just required inconvenient wire paths. R7 had to get over and across the controller, and C1-C6 had to come down at an angle. Oh well.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_09.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_09.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_10.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_10.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_11.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_11.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/wiring_controller_12.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/wiring_controller_12.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It works! I made some firmware changes to accommodate the change I made during wiring, and also fixed some keycodes that I had chosen incorrectly (the &lt;code&gt;`/~&lt;/code&gt; key should be KC_GRAVE, because KC_TILDE always types &lt;code&gt;~&lt;/code&gt; even without shift, etc.)&lt;/p&gt;
&lt;h2 id="the_base"&gt;The base&lt;a class="header_anchor_link" href="#the_base"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wanted to make the base out of a dark piece of wood, or at least wood stained dark. My grandfather has a workshop, and in fact he's the one who built my desk, so I went over to his house and we put something together.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_01.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_01.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_02.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_02.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_03.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_03.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On my own time, I brought the height of the base down, drilled the screw holes, and added a hardware reset button to facilitate easier experimentation with the firmware. The plug and reset button holes required a couple rounds of adjustment, and as a result don't look very good, but I'll consider it a learning experience. The piece of wood which goes over the plug hole is dangerously thin as it is, so I can't experiment with the shape any more. There's nothing stopping me from unscrewing the plate and making a whole new base if I choose to. Except for the fact that this one was made with my grandfather and will someday be a family heirloom.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_04.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_04.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_05.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_05.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_06.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_06.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then I did the stain and other finishing touches, and screwed the plate down.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_07.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_07.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_08.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_08.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_09.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_09.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/base_10.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/base_10.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="stabilizers"&gt;Stabilizers&lt;a class="header_anchor_link" href="#stabilizers"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section shouldn't be last, but it is.&lt;/p&gt;
&lt;p&gt;My keyboard uses a 6u space, which is uncommon. The standard size is 6.25, which of course would not fit in the ortholinear design. There are fewer vendors selling 6u space stabilizers, and they are all in China / Taiwan. I found a set on ebay, ordered them, and waited. And waited. And waited. UPS had lost them, and they simply never arrived.&lt;/p&gt;
&lt;p&gt;After a month of waiting, that's when I put the rest of the keyboard together and figured I'd put the stabs in if they ever arrived. My family asked me "did you get a refund from the company?", and I just said "it's not really a company, it's just some dude in Taiwan, and it was UPS's fault...". I felt bad losing the money, but would also have felt bad taking the money back in case UPS eventually found it and sent it over. I should have been less timid, but oh well.&lt;/p&gt;
&lt;p&gt;For six months, I used the Nearer with a 1.5u key as my spacebar and no stabs under Shift, Enter, etc. It worked perfectly fine, and I adapted very well to a 1.5u space, but it was embarrassing to look at and I knew I couldn't publish this article without proper stabs. So I ordered &lt;a href="https://www.ebay.com/itm/313372624687"&gt;another set&lt;/a&gt; on ebay and they arrived about two weeks later.&lt;/p&gt;
&lt;p&gt;With a 6.25u space.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/stab_wrong.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/stab_wrong.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I argued with the vendor over the course of nine!! emails that a 6u stab should be 95mm instead of 100mm, and he eventually sent a replacement.&lt;/p&gt;
&lt;p&gt;&lt;a href="/writing/kb_nearer/stab_right.jpg"&gt;&lt;img alt="" src="/writing/kb_nearer/thumbs/stab_right.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On my previous, rubber dome keyboard, the stabilizer bars were fully accessible after simply removing the keycaps, so it didn't occur to me that Cherry stabilizers actually go underneath the plate, and are supposed to be installed before the switches are in place. But, I was able to add them retroactively, which I described in a video. This required cutting away some of my beautiful hot glue with a craft knife to make way for the bar.&lt;/p&gt;
&lt;p&gt;
&lt;center&gt;

&lt;/center&gt;
&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="header_anchor_link" href="#conclusion"&gt; (§)&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="price_breakdown"&gt;Price breakdown&lt;a class="header_anchor_link" href="#price_breakdown"&gt; (§)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If I had known these numbers before starting, I probably would have put the project off yet longer. But my technique of getting started before planning everything so I couldn't back down worked great.&lt;/p&gt;
&lt;p&gt;Prices are expressed as &lt;code&gt;$item+fees&lt;/code&gt; in USD, since shipping / tax will be different everywhere.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th style="text-align: left;"&gt;item&lt;/th&gt;
&lt;th style="text-align: right;"&gt;price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://kbdfans.com/products/switches-70"&gt;140×Gateron Yellow&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$29+18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://www.aliexpress.com/item/32850729893.html"&gt;129×keycaps&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$40+9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://www.ebay.com/itm/303201293688"&gt;150×1N4148 diodes&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$12+1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://www.laserboost.com/"&gt;Switchplate&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$60+20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://www.ebay.com/itm/313372624687"&gt;6u + 7×2u Stabilizers&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$16+4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://keeb.io/products/elite-c-low-profile-version-usb-c-pro-micro-replacement-atmega32u4"&gt;Elite-C controller&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$18+5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;a href="https://www.monoprice.com/product?c_id=301&amp;amp;cp_id=30107&amp;amp;cs_id=3010701&amp;amp;p_id=27930&amp;amp;seq=1&amp;amp;format=2"&gt;USB-A to -C cable&lt;/a&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;$6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;Odds and ends (hot glue)&lt;/td&gt;
&lt;td style="text-align: right;"&gt;a bit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Coming in at around or just under $250.&lt;/p&gt;
&lt;p&gt;And it would be wrong of me to ignore the benefit of materials I already had or borrowed from my family: Soldering iron, wire, hot glue gun, wood &amp;amp; woodworking tools, wood stain, sandpaper.&lt;/p&gt;
&lt;p&gt;My mom kept asking me "are you going to make and sell these?", but with $250 material cost and at least 24 hours of labor, I don't think I'd find many takers.&lt;/p&gt;
&lt;h3 id="what_id_do_differently"&gt;What I'd do differently&lt;a class="header_anchor_link" href="#what_id_do_differently"&gt; (§)&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Bring screw holes farther from the edge of the plate.&lt;/li&gt;
&lt;li&gt;Add more screw holes to deal with non-flat plate.&lt;/li&gt;
&lt;li&gt;Improve accuracy of base shape to fit more flush with the edges of the plate.&lt;/li&gt;
&lt;li&gt;Improve accuracy of drilled screw holes in the base, as some of the screws are not totally plumb.&lt;/li&gt;
&lt;li&gt;Improve shape of USB and reset button holes.&lt;/li&gt;
&lt;li&gt;Use something other than hot glue. I do want to protect against switches coming out of the plate, but hot glue actually doesn't adhere that well to the metal. My firmware reset button has come loose, and I taped a piece of felt to the inside of the case to give it a friction fit so I can still push it securely.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, I am satisfied. Typing on the Nearer feels cool, and I'm glad to have a unique piece of equipment that I put together myself.&lt;/p&gt;
&lt;p&gt;Thank you for reading.&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commits/master/voussoir.net/writing/kb_nearer/kb_nearer.md"&gt;View this document's history&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/3475cb0abf63172e6392487ffaf41755f71a18e3"&gt;&lt;time datetime="2023-09-18T21:01:09-07:00"&gt;2023-09-18&lt;/time&gt; Replace github links with git.voussoir.net.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/3fbf053cd70d073d7660243e16e7572b7a2b87c3"&gt;&lt;time datetime="2022-04-12T21:52:42-07:00"&gt;2022-04-12&lt;/time&gt; Replace demo video with a new 4k60 version.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/48a82e34ef672c81ba423dc657c2b99e2b8b4972"&gt;&lt;time datetime="2022-03-26T11:02:31-07:00"&gt;2022-03-26&lt;/time&gt; Fix typo "spantaneous", improve diode explanation.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/9bd35010ddaa118d8f24383742655bfc7ebd7524"&gt;&lt;time datetime="2021-11-07T19:37:35-08:00"&gt;2021-11-07&lt;/time&gt; Remove excessive and repetitive glamour shots.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/2dca1f73291d134cfe51b107cb4aa3af3f0e78fb"&gt;&lt;time datetime="2021-10-22T00:16:30-07:00"&gt;2021-10-22&lt;/time&gt; Add tag electronics.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/807f87a29cd2d034aca36e28c0b8effadfd86774"&gt;&lt;time datetime="2021-06-13T23:24:01-07:00"&gt;2021-06-13&lt;/time&gt; Replace youtube links with embeds.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/2aa9b67000d5b1353aeed7f9a40813a6481f332f"&gt;&lt;time datetime="2021-05-29T11:44:23-07:00"&gt;2021-05-29&lt;/time&gt; Fix typo "over to the controller".&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.voussoir.net/voussoir/voussoir.net/commit/7ed9280032617b4be9b2f8d4cf9ec05c22ed2b52"&gt;&lt;time datetime="2021-05-28T17:11:19-07:00"&gt;2021-05-28&lt;/time&gt; Add kb_nearer.md.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;address&gt;Contact me: writing@voussoir.net&lt;/address&gt;
&lt;p&gt;If you would like to subscribe for more, add this to your RSS reader: &lt;a href="/writing/writing.atom" rel="alternate" type="application/atom+xml"&gt;https://voussoir.net/writing/writing.atom&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;</description><author>voussoir.net/writing</author><pubDate>Sat, 29 May 2021 03:11:19 GMT</pubDate><guid isPermaLink="true">https://voussoir.net/writing/kb_nearer</guid></item><item><title>Porting Takua Renderer to 64-bit ARM- Part 1</title><link>https://blog.yiningkarlli.com/2021/05/porting-takua-to-arm-pt1.html</link><description>&lt;p&gt;&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;

&lt;div class="tableofcontents"&gt;
    &lt;div class="tableofcontents-row"&gt;
        &lt;div class="tableofcontents-column2"&gt;
            &lt;div class="tableofcontents-content"&gt;
                1. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-introduction"&gt;Introduction&lt;/a&gt;&lt;br /&gt;
                2. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-motivation"&gt;Motivation&lt;/a&gt;&lt;br /&gt;
                3. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-porting-to-arm64-linux"&gt;Porting to arm64 Linux&lt;/a&gt;&lt;br /&gt;
                4. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-floating-point-consistency"&gt;Floating Point Consistency (or lack thereof) on Different Systems   &lt;/a&gt;&lt;br /&gt;
                5. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-weak-memory-ordering"&gt;Weak Memory Ordering in arm64 and Atomic Bugs in Takua&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="tableofcontents-column2"&gt;
            &lt;div class="tableofcontents-content"&gt;
                6. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-deep-dive-on-x86-64-vs-arm64"&gt;A Deep Dive on x86-64 versus arm64 Through the Lens of Compiling &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic::compare_exchange_weak()&lt;/code&gt;&lt;/a&gt;&lt;br /&gt;
                7. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-performance-testing"&gt;Performance Testing&lt;/a&gt;&lt;br /&gt;
                8. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-conclusion-to-part-1"&gt;Conclusion to Part 1&lt;/a&gt;&lt;br /&gt;
                9. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-acknowledgements"&gt;Acknowledgements&lt;/a&gt;&lt;br /&gt;
                10. &lt;a href="/2021/05/porting-takua-to-arm-pt1#2021-05-29-references"&gt;References&lt;/a&gt;&lt;br /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div id="2021-05-29-introduction"&gt;&lt;/div&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;For almost its entire existence my hobby renderer, Takua Renderer, has built and run on Mac, Windows, and Linux on x86-64.
I maintain Takua on all three major desktop operating systems because I routinely run and use all three operating systems, and I’ve found that building with different compilers on different platforms is a good way for making sure that I don’t have code that is actually wrong but just happens to work because of the implementation quirks of a particular compiler and / or platform.
As of last year, Takua Renderer now also runs on 64-bit ARM, for both Linux and Mac!
64-bit ARM is often called either aarch64 or arm64; these two terms are interchangeable and mean the same thing (aarch64 is the official name for 64-bit ARM and is what Linux tends to use, while arm64 is the name that Apple and Microsoft’s tools tend to use).
For the sake of consistency, I’ll use the term arm64.&lt;/p&gt;

&lt;p&gt;This post is the first of a two-part writeup of the process I undertook to port Takua Renderer to run on arm64, along with interesting stuff that I learned along the way.
In this first part, I’ll write about motivation and the initial port I undertook in the spring to arm64 Linux (specifically Fedora).
I’ll also write about how arm64 and x86-64’s memory ordering guarantees differ and what that means for lock-free code, and I’ll also do some deeper dives into topics such as floating point differences between different processors and a case study examining how code compiles to x86-64 versus to arm64.
In the second part, I’ll write about porting to arm64-based Apple Silicon Macs and I’ll also write about getting Embree up and running on ARM, creating Universal Binaries, and some other miscellaneous topics.&lt;/p&gt;

&lt;div id="2021-05-29-motivation"&gt;&lt;/div&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;

&lt;p&gt;So first, a bit of a preamble: why port to arm64 at all?
Today, basically most, if not all, of the animation/VFX industry renders on x86-64 machines (and a vast majority of those machines are likely running Linux), so pretty much all contemporary production rendering development happens on x86-64.
However, this has not always been true!
A long long time ago, much of the computer graphics world was based on MIPS hardware running SGI’s IRIX Unix variant; in the early 2000s, as SGI’s custom hardware began to fall behind the performance-per-dollar, performance-per-watt, and even absolute performance that commodity x86-based machines could offer, the graphics world undertook a massive migration to the current x86 world that we live in today.
Apple undertook a massive migration from PowerPC to x86 in the mid/late 2000s for similar reasons.&lt;/p&gt;

&lt;p&gt;At this point, an ocean of text has been written about why it is that x86 (and by (literal) extension x86-64) became the dominant ISA in desktop computing and in the server space.
One common theory that I like is that x86’s dominance was a classic example of &lt;a href="https://en.wikipedia.org/wiki/Disruptive_innovation#Disruptive_technology"&gt;disruptive innovation&lt;/a&gt; from the low end.
A super short summary of disruptive innovation from the low end is that sometimes, a new player enters an existing market with a product that is much less capable but also much cheaper than existing competing products.
By being so much cheaper, the new product can generate a new, larger market that existing competing products can’t access due to their higher cost or different set of requirements or whatever.
As a result, the new product gets massive investment since the new product is the only thing that can capture this new larger market, and in turn this massive influx of investment allows the new player to iterate faster and rapidly grow its product in capabilities until the new player becomes capable of overtaking the old market as well.
This theory maps well to x86; x86-based desktop PCs started off being much cheaper but also much less capable than specialized hardware such as SGI machines, but the investment that poured into the desktop PC space allowed x86 chips to rapidly grow in absolute performance capability until they were able to overtake specialized hardware in basically every comparable metric.
At that point, moving to x86 became a no-brainer for many industries, including the computer graphics realm.&lt;/p&gt;

&lt;p&gt;I think that ARM is following the same disruptive innovation path that x86 did, only this time the starting “low end” point is smartphones and tablets, which is an even lower starting point than desktop PCs were.
More importantly, I think we’re now at a tipping point for ARM.
For many years now, ARM chips have offered better performance-per-dollar and performance-per-watt than any x86-64 chip from Intel or AMD, and the point where arm64 chips can overtake x86-64 chips in absolute performance seems plausibly within sight over the next few years.
Notably, Amazon’s in-house Graviton2 arm64 CPU and Apple’s M1 arm64-based Apple Silicon chip are both already highly competitive in absolute performance terms with high end consumer x86-64 CPUs, while consuming less power and costing less.
Actually, I think that this trend should have been obvious to anyone paying attention to Apple’s A-series chips since the A9 chip was released in 2015.&lt;/p&gt;

&lt;p&gt;In cases of disruptive innovation from the low end, the outer edge of the absolute high end is often the last place where the disruption reaches.
One of the interesting things about the high-end rendering field is that high-end rendering is one of a relatively small handful of applications that sits at the absolute outer edge of high end compute performance.
All of the major animation and VFX studios have render farms (either on-premises or in the cloud) with core counts somewhere in the tens of thousands of cores; these render farms have more similarities with supercomputers than they do with a regular consumer desktop or laptop.
I don’t know that anyone has actually tried this, but my guess is that if someone benchmarked any major animation or VFX studio’s render farm using the &lt;a href="https://en.wikipedia.org/wiki/LINPACK_benchmarks"&gt;LINPACK supercomputer benchmark&lt;/a&gt;, the score would sit very respectably somewhere in the upper half of the &lt;a href="https://www.top500.org"&gt;TOP500 supercomputer list&lt;/a&gt;.
With the above in mind, the fact that the fastest supercomputer in the world is now an arm64-based system should be an interesting indicator of where ARM is now in the process of catching up to x86-64 and how seriously all of us in high-end computer graphics should be when contemplating the possibility of an ARM-based future.&lt;/p&gt;

&lt;p&gt;So all of the above brings me to why I undertook porting Takua to arm64.
The reason is because I think we can now plausibly see a potential near future in which the fastest, most efficient, and most cost effective chips in the world are based on arm64 instead of x86-64, and the moment this potential future becomes reality, high-performance software that hasn’t already made the jump will face growing pressure to port to arm64.
With Apple’s in-progress shift to arm64-based Apple Silicon Macs, we may already be at this point.
I can’t speak for any animation or VFX studio in particular; everything I have written here is purely personal opinion and personal conjecture, but I’d like to be ready in the event that a move to arm64 becomes something we have to face as an industry, and what better way is there to prepare than to try with my own hobby renderer first!
Also, for several years now I’ve thought that Apple eventually moving Macs to arm64 was obvious given the progress the A-series Apple chips were making, and since macOS is my primary personal daily use platform, I figured I’d have to port Takua to arm64 eventually anyway.&lt;/p&gt;

&lt;div id="2021-05-29-porting-to-arm64-linux"&gt;&lt;/div&gt;
&lt;h2 id="porting-to-arm64-linux"&gt;Porting to arm64 Linux&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/May/takua-on-arm-pt1/takua_fedora_arm64.jpg"&gt;&lt;img alt="Figure 1: Takua Renderer running on arm64 Fedora 32, on a Raspberry Pi 4B." src="https://blog.yiningkarlli.com/content/images/2021/May/takua-on-arm-pt1/takua_fedora_arm64.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I actually first attempted an ARM port of Takua several years ago, when Fedora 27 became the first version of Fedora to support arm64 single-board computers (SBCs) such as the Raspberry Pi 3B or the Pine A64.
I’ve been a big fan of the Raspberry Pi basically since the original first came out, and the thought of porting Takua to run on a Raspberry Pi as an experiment has been with me basically since 2012.
However, Takua is written very much with 64-bit in mind, and the first two generations of Raspberry Pis only had 32-bit ARMv7 processors.
I actually backed the original Pine A64 on Kickstarter in 2015 precisely because it was one of the very first 64-bit ARMv8 boards on the market, and if I remember correctly, I also ordered the Raspberry Pi 3B the week it was announced in 2016 because it was the first 64-bit ARMv8 Raspberry Pi.
However, my Pine A64 and Raspberry Pi 3B mostly just sat around not doing much because I was working on a bunch of other stuff, but that actually wound up working out because by the time I got back around to tinkering with SBCs in late 2017, Fedora 27 had just been released.
Thanks to a ton of work from &lt;a href="https://nullr0ute.com/"&gt;Peter Robinson&lt;/a&gt; at Red Hat, Fedora 27 added native arm64 support that basically worked out-of-the-box on both the Raspberry Pi 3B and the Pine A64, which was ideal for me since my Linux distribution of choice for personal hobby projects is Fedora.
Since I already had Takua building and running on Fedora on x86-64, being able to use Fedora as the target distribution for arm64 as well meant that I could eliminate different compiler and system library versions as a variable factor; I “just” had to move everything in my Fedora x86-64 build over to Fedora arm64.
However, back in 2017, I found that a lot of the foundational libraries that Takua depends on just weren’t quite ready on arm64 yet.
The problem usually wasn’t with the actual source code itself, since anything written in pure C++ without any intrinsics or inline assembly should just compile directly on any platform with a supported compiler; instead, the problem was usually just in build scripts not knowing how to handle small differences in where system libraries were located or stuff like that.
At the time I was focused on other stuff, so I didn’t try particularly hard to diagnose and work around the problems I ran into; I kind of just shrugged and put it all aside to revisit some other day.&lt;/p&gt;

&lt;p&gt;Fast forward to early 2020, when rumors started circulating of a potential macOS transition to 64-bit ARM.
As the rumors grew, I figured that this was a good time to return to porting Takua to arm64 Fedora in preparation for if a macOS transition actually happened.
I had also recently bought a Raspberry Pi 4B with 4 GB of RAM; the 4 GB of RAM made actually building and running complex code on-device a lot easier than with the Raspberry Pi 3B/3B+’s 1 GB of RAM.
By this point, the arm64 build support level for Takua’s dependencies had improved dramatically.
I think that as arm64 devices like the iPhone and iPad Pro have gotten more and more powerful processors over the last few years and enabled more and more advanced and complex iOS / iPadOS apps (and similarly with Android devices and Android apps), more and more open source libraries have seen adoption on ARM-based platforms and have seen ARM support improve as a result.
Almost everything just built and worked out-of-the-box on arm64, including (to my enormous surprise) Intel’s TBB library!
I had assumed that TBB would be x86-64-only since TBB is an Intel project, but it turns out that over the years, the community has contributed support for ARMv7 and arm64 and even PowerPC to TBB.
The only library that didn’t work out-of-the-box or with minor changes was Embree, which relies heavily on SSE and AVX intrinsics and has small amounts of inline x86-64 assembly.
To get things up and running initially, I just disabled Takua’s Embree-based traversal backend and fell back to my own custom BVH traversal backend.
My own custom BVH traversal backend isn’t nearly as fast as Embree and is instead meant to serves as a reference implementation and fallback for when Embree isn’t available, but for the time being since the goal was just to get Takua working at all, losing performance due to not having Embree was fine.
As you can see by the “Traverser: Embree” label in Takua Renderer’s UI in Figure 1, I later got Embree up and running on arm64 using Syoyo Fujita’s embree-aarch64 port, but I’ll write more about that in the next post.
To be honest, the biggest challenge with getting everything compiled and running was just the amount of patience that was required.
I never seem to be able to get cross-compilation for a different architecture right because I always forget something, so instead of cross-compiling for arm64 from my nice big powerful x86-64 Fedora workstation, I just compiled for arm64 directly on the Raspberry Pi 4B.
While the Raspberry Pi 4B is much faster than the Raspberry Pi 3B, it’s still nowhere near as fast as a big fancy dual-Xeon workstation, so some libraries took forever to compile locally (especially Boost, which I wish I didn’t have to have a dependency on, but I have to since OpenVDB depends on Boost).
Overall getting a working build of Takua up and running on arm64 was very fast; from deciding to undertake the port to getting a first image back took only about a day’s worth of work, and most of that time was just waiting for stuff to compile.&lt;/p&gt;

&lt;p&gt;However, getting code to &lt;em&gt;build&lt;/em&gt; is a completely different question from getting code to &lt;em&gt;run correctly&lt;/em&gt; (unless you’re using one of those fancy proof-solver languages I guess).
The first test renders I did with Takua on arm64 Fedora looked fine to my eye, but when I diff’d them against reference images rendered on x86-64, I found some subtle differences; the source of these differences took me a good amount of digging to understand!
Chasing this problem down led down some interesting rabbit holes exploring important differences between x86-64 and arm64 that need to be considered when porting code between the two platforms; just because code is written in portable C++ does not necessarily mean that it is always actually as portable as one might think!&lt;/p&gt;

&lt;div id="2021-05-29-floating-point-consistency"&gt;&lt;/div&gt;
&lt;h2 id="floating-point-consistency-or-lack-thereof-on-different-systems"&gt;Floating Point Consistency (or lack thereof) on Different Systems&lt;/h2&gt;

&lt;p&gt;Takua has two different types of image comparison based regression tests: the first type of test renders out to high samples-per-pixel numbers and does comparisons with near-converged images, while the second type of test renders out and does comparisons using a single sample-per-pixel.
The reason for these two different types of tests is because of how difficult getting floating point calculations to match across different compilers / platforms / processors is.
Takua’s single-sample-per-pixel tests are only meant to catch regressions on the same compiler / platform / processor, while Takua’s longer tests are meant to test overall correctness of converged renders.
Because of differences in how floating point operations come out on different compilers / platforms / processors, Takua’s convergence tests don’t require an exact match; instead, the tests use small, predefined difference thresholds that comparisons must stay within to pass.
The difference thresholds are basically completely ad-hoc; I picked them to be at a level where I can’t perceive any difference when flipping between the images, since I put together my testing system before image differencing systems that formally factor in perception &lt;a href="https://doi.org/10.1145/3406183"&gt;[Andersson et al. 2020]&lt;/a&gt; were published.
A large part of the differences between Takua’s test results on x86-64 versus arm64 come from these problems with floating point reproducibility across different systems.
Because of how commonplace this issue is and how often this issue is misunderstood by programmers who haven’t had to deal with it, I want to spend a few paragraphs talking about floating point numbers.&lt;/p&gt;

&lt;p&gt;A lot of programmers that don’t have to routinely deal with floating point calculations might not realize that even though floating point numbers are standardized through the &lt;a href="https://en.wikipedia.org/wiki/IEEE_754"&gt;IEEE754 standard&lt;/a&gt;, in practice reproducibility is not at all guaranteed when carrying out the same set of floating point calculations using different compilers / platforms / processors!
In fact, starting with the same C++ floating point code, determinism is only really guaranteed for successive runs using binaries generated using the same compiler, with the same optimizations enabled, on the same processor family; sometimes running on the same operating system is also a requirement for guaranteed determinism.
There are three main reasons &lt;a href="http://yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html"&gt;[Kreinin 2008]&lt;/a&gt; why reproducing exactly the same results from the same set of floating point calculations across different systems is so inconsistent: compiler optimizations, processor implementation details, and different implementations of built-in “complex” functions like sine and cosine .&lt;/p&gt;

&lt;p&gt;The first reason above is pretty easy to understand: operations like addition and multiplication are commutative, meaning they can be done in any order, and often a compiler in an optimization pass may choose to reorder commutative math operations.
However, as anyone who has dealt extensively with floating point numbers knows, due to how floating point numbers are represented &lt;a href="https://doi.org/10.1145/103162.103163"&gt;[Goldberg 1991]&lt;/a&gt; the commutative and associative properties of addition and multiplication do not actually hold true for floating point numbers; not even for IEEE754 floating point numbers!
Sometimes reordering floating point math is expressly permitted by the language, and sometimes doing this is not actually allowed by the language but happens anyway in the compiler because the user has specified flags like &lt;code class="language-plaintext highlighter-rouge"&gt;-ffast-math&lt;/code&gt;, which tells the compiler that it is allowed to sacrifice strict IEEE754 and language math requirements in exchange for additional optimization opportunities.
Sometimes the compiler can just have implementation bugs too; &lt;a href="https://lists.llvm.org/pipermail/llvm-dev/2020-June/142697.html"&gt;here is an example&lt;/a&gt; that I found on the llvm-dev mailing lists describing a bug with loop vectorization that impacts floating point consistency!
The end result of all of the above is that the same floating point source code can produce subtly different results depending on which compiler is used and which compiler optimizations are enabled within that compiler.
Also, while some compiler optimization passes operate purely on the AST built from the parser or operate purely on the compiler’s intermediate representation, there can also be optimization passes that take into account the underlying target instruction set and choose to carry out different optimizations depending on the what’s available in the target processor architecture.
These architecture-specific optimizations mean that even the same floating point source code compiled using the same compiler can still produce different results on different processor architectures!
Architecture-specific optimizations are one reason why floating point results on x86-64 versus arm64 can be subtly different.
Also, another fun fact: the C++ specification doesn’t actually specify a binary representation for floating point numbers, so in principle a C++ compiler could outright ignore IEEE754 and use something else entirely, although in practice this is basically never the case since all modern compilers like GCC, Clang, and MSVC use IEEE754 floats.&lt;/p&gt;

&lt;p&gt;The second reason floating point math is so hard to reproduce exactly across different systems is in how floating point math is implemented in the processor itself.
Differences at this level is a huge source of floating point differences between x86-64 and arm64.
In both x86-64 and arm64, at the assembly level individual arithmetic instructions such as add, subtract, multiple, divide, etc all adhere strictly to the IEEE754 standard.
However, the IEEE754 standard is itself… surprisingly loosely specified in some areas!
For example, the IEEE754 standard specifies that intermediate results should be as precise as possible, but this means that two different implementations of a floating point addition instructions both adhering to IEEE754 can actually produce different results for the same input &lt;em&gt;if they use different levels of increased precision internally&lt;/em&gt;.
Here’s a bit of a deprecated example that is still useful to know for historical reasons: everyone knows that an IEEE754 floating point number is 32 bits, but older 32-bit x86 specifies that internal calculations be done using &lt;em&gt;80-bit precision&lt;/em&gt;, which is a holdover from the &lt;a href="https://en.wikipedia.org/wiki/Intel_8087"&gt;Intel 8087&lt;/a&gt; math coprocessor.
Every x86 (and by extension x86-64) processor when using x87 FPU instructions actually does floating point math using 80 bit internal precision and then rounds back down to 32 bit floats in hardware; the 80 bit internal representation is known as the &lt;a href="https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format"&gt;x86 extended precision format&lt;/a&gt;.
But even within &lt;em&gt;the same&lt;/em&gt; x86 processor, we can still get difference floating point results depending on if the compiler has output x87 FPU instructions or SSE instructions; SSE stays within 32 bits at all times, which means SSE and x87 on the same processor doing the same floating point math isn’t guaranteed to produce the exact same answer.
Of course, modern x86-64 generally uses SSE for floating point math instead of x87, but different amounts of precision truncation can still happen depending on what order values are loaded into SSE registers and back into other non-SSE registers.
Furthermore, SSE is sufficiently under-specified that the actual implementation details can differ, which is why the same SSE floating point instructions can produce different results on Intel versus AMD processors.
Similarly, the ARM architecture doesn’t actually specify a particular FPU implementation at all; the internals of the FPU are left up to each processor designer; for example, the VFP/NEON floating point units that ship on the Raspberry Pi 4B’s Cortex-A72-based CPU use up to 64 bits of internal precision &lt;a href="https://embeddedartistry.com/blog/2017/10/11/demystifying-arm-floating-point-compiler-options/"&gt;[Johnston 2020]&lt;/a&gt;.
So, while the x87, SSE on Intel, SSE on AMD, and VFP/NEON FPU implementations are IEEE754-compliant, because of their internal maximum precision differences they can still all produce different results from each other.
There are many more examples of areas where IEEE754 leaves in wiggle room for different implementations to do different things &lt;a href="https://www.appinf.com/download/FPIssues.pdf"&gt;[Obiltschnig 2006]&lt;/a&gt;, and in practice different CPUs do use this wiggle room to do things differently from each other.
For example, this wiggle room is why for floating point operations at the extreme ends of the IEEE754 float range, Intel’s x86-64 versus AMD’s x86-64 versus arm64 can produce results with minor differences from each other in the end of the mantissa.&lt;/p&gt;

&lt;p&gt;Finally, the third reason floating point math can vary across different systems is because of  transcendental functions such as sine and cosine.
Transcendental functions like sine and cosine have exact, precise mathematical definitions, but unfortunately these precise mathematical definitions can’t be implemented exactly in hardware.
Think back to high school trigonometry; the exact answer for a given input to functions like sine and cosine have to be determined using a &lt;a href="https://en.wikipedia.org/wiki/Taylor_series"&gt;Taylor series&lt;/a&gt;, but actually implementing a Taylor series in hardware is not at all practical nor performant.
Instead, modern processors typically use some form of a &lt;a href="https://en.wikipedia.org/wiki/CORDIC"&gt;CORDIC algorithm&lt;/a&gt; to approximate functions like sine and cosine, often to reasonably high levels of accuracy.
However, the level of precision to which any given processor approximates sine and cosine is completely unspecified by either IEEE754 or any language standard; as a result, these approximations can and do vary widely between different hardware implementations on different processors!
However, how much this reason actually matters in practice is complicated and compiler/language dependent.
As an example using cosine, the standard library could choose to implement cosine in software using a variety of different methods, or the standard library could choose to just pass through to the hardware cosine implementation.
To illustrate how much the actual execution path depends on the compiler: I originally wanted to include a simple small example using cosine that you, the reader, could go and compile and run yourself on an x86-64 machine and then on an arm64 machine to see the difference, but I wound up having so much difficulty convincing different compilers on different platforms to actually compile the cosine function (even using intrinsics like &lt;code class="language-plaintext highlighter-rouge"&gt;__builtin_cos&lt;/code&gt;!) down to a hardware instruction reliably that I wound up having to abandon the idea.&lt;/p&gt;

&lt;p&gt;One of the things that makes all of the above even more difficult to reason about is that which specific factors are applicable at any given moment depends heavily on what the compiler is doing, what compiler flags are in use, and what the compiler’s defaults are.
Actually getting floating point determinism across different systems is a notoriously difficult problem &lt;a href="https://gafferongames.com/post/floating_point_determinism/"&gt;[Fiedler 2010]&lt;/a&gt; that volumes of stuff has been written about!
On top of that, while in principle getting floating point code to produce consistent results across many different systems is possible (hard, but possible) by disabling compiler optimizations and by relying entirely on software implementations of floating point operations to ensure strict, identical IEEE754 compliance on all systems, actually doing all of the above comes with major trade-offs.
The biggest trade-off is simply performance: all of the changes necessary to make floating point code consistent across different systems (and especially across different processor architectures like x86-64 versus arm64) also likely will make the floating point considerably slower too.&lt;/p&gt;

&lt;p&gt;All of the above reasons mean that modern usage of floating point code basically falls into three categories.
The first category is: just don’t use floating point code at all.
Included in this first category are applications that require absolute precision and absolute consistency and determinism across all implementations; examples are banking and financial industry code, which tend to store monetary values entirely using only integers.
The second category are applications that absolutely must use floats but also must ensure absolute consistency; a good example of applications in this category are high-end scientific simulations that run on supercomputers.
For applications in this second category, the difficult work and the performance sacrifices that have to be made in favor of consistency are absolutely worthwhile.
Also, tools do exist that can help with ensuring floating point consistency; for example, &lt;a href="https://herbie.uwplse.org"&gt;Herbie&lt;/a&gt; is a tool that can detect potentially inaccurate floating point expressions and suggest more accurate replacements.
The last category are applications where the requirement for consistency is not necessarily absolute, and the requirement for performance may weigh heavier.
This is the space that things like game engines and renderers and stuff live in, and here the trade-offs become more nuanced and situation-dependent.
A single-player game may choose absolute performance over any kind of cross-platform guaranteed floating point consistency, whereas a multi-player multi-platform game may choose to sacrifice some performance in order to guarantee that physics and gameplay calculations produce the same result for all players regardless of platform.&lt;/p&gt;

&lt;p&gt;Takua Renderer lives squarely in the third category, and historically the point in the trade-off space that I’ve chosen for Takua Renderer is to favor performance over cross-platform floating point consistency.
I have a couple of reasons for choosing this trade-off, some of which are good and some of which are… just laziness, I guess!
As a hobby renderer, I’ve never had shipping Takua as a public release in any form in mind, and so consistency across many platforms has never really mattered to me.
I know exactly which systems Takua will be run on, because I’m the only one running Takua on anything, and to me having Takua run slightly faster at the cost of minor noise differences on different platforms seems worthwhile.
As long as Takua is converging to the correct image, I’m happy, and for my purposes, I consider converged images that are perceptually indistinguishable when compared with a known correct reference to also be correct.
I do keep determinism within the same platform as a major priority though, since determinism within each platform is important for being able to reliably reproduce bugs and is important for being able to reason about what’s going on in the renderer.&lt;/p&gt;

&lt;p&gt;Here is a concrete example of the noise differences I get on x86-64 versus on arm64.
This scene is the iced tea scene I originally created for my &lt;a href="https://blog.yiningkarlli.com/2019/05/nested-dielectrics.html"&gt;Nested Dielectrics&lt;/a&gt; post; I picked this scene for this comparison purely because it is has a small memory footprint and therefore fits in the relatively constrained 4 GB memory footprint of my Raspberry Pi 4B, while also being slightly more interesting than a Cornell Box.
Here is a comparison of a single sample-per-pixel render using bidirectional path tracing on a dual-socket Xeon E5-2680 x86-64 system versus on a Raspberry Pi 4B with a Cortex-A72 based arm64 processor.
The scene actually appears somewhat noisier than it normally would be coming out of Takua renderer because for this demonstration, I disabled low-discrepancy sampling and had the renderer fall back to purely random &lt;a href="https://www.pcg-random.org/index.html"&gt;PCG-based&lt;/a&gt; sample sequences, with the goal of trying to produce more noticeable noise differences:&lt;/p&gt;

&lt;div class="embed-container" id="noisecomparison"&gt;
&lt;/div&gt;
&lt;div class="figcaption"&gt;&lt;span&gt;Figure 2: A single-spp render demonstrating noise pattern differences between x86-64 (left) versus arm64 (right). Differences are most noticeable on rim of the cup, especially on the left near the handle. For a full screen comparison, &lt;a href="/content/images/2021/May/takua-on-arm-pt1/comparisons/noisecomparison.html"&gt;click here.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The noise differences are actually relatively minimal!
The most noticeable noise differences are on the rim of the cup; note the left of the rim near the handle.
Since the noise differences can be fairly difficult to see in the full render on a small screen, here is a 2x zoomed-in crop:&lt;/p&gt;

&lt;div class="embed-container" id="noisecomparison_crop"&gt;
&lt;/div&gt;
&lt;div class="figcaption"&gt;&lt;span&gt;Figure 3: A zoomed-in crop of Figure 2 showing noise pattern differences between x86-64 (left) versus arm64 (right). For a full screen comparison, &lt;a href="/content/images/2021/May/takua-on-arm-pt1/comparisons/noisecomparison_crop.html"&gt;click here.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The differences are still kind of hard to see even in the zoomed-in crop!
So, here’s the absolute difference between the x86-64 and arm64 renders, created by just subtracting the images from each other and taking the absolute value of the difference at each pixel.
Black pixels indicate pixels where the absolute difference is zero (or at least, so close to zero so as to be completely imperceptible).
Brighter pixels indicate greater differences between the x86-64 and arm64 renders; from where the bright pixels are, we can see that most of the differences occur on the rim of the cup, on ice cubes in the cup, and in random places mostly in the caustics cast by the cup.
There’s also a faint horizontal line of small differences across the background; that area lines up with where the seamless white cyclorama backdrop starts to curve upwards:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/May/takua-on-arm-pt1/noise_difference.png"&gt;&lt;img alt="Figure 4: Absolute difference between the x86-64 and arm64 renders from Figure 2. Black indicates identical pixels, while brighter values indicate greater differences in pixel values between x86-64 and arm64." src="https://blog.yiningkarlli.com/content/images/2021/May/takua-on-arm-pt1/noise_difference.png" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understanding why the areas with the highest differences are where they are requires thinking about how light transport is functioning in this specific scene and how differences in floating point calculations impact that light transport.
This scene is lit fairly simply; the only light sources are two rect lights and a skydome.
Basically everything is illuminated through direct lighting, meaning that for most areas of the scene, a ray starting from the camera is directly hitting the diffuse background cyclorama and then sampling a light source, and a ray starting from the light is directly hitting the diffuse background cyclorama and then immediately sampling the camera lens.
So, even with bidirectional path tracing, the total path lengths for a lot of the scene is just two path segments, or one bounce.
That’s not a whole lot of path for differences in floating point calculations to accumulate during.
On the flip side, most of the areas with the greatest differences are areas where a lot of paths pass through the glass tea cup.
For paths that go through the glass tea cup, the path lengths can be very long, especially if a path gets caught in total internal reflection within the glass walls of the cup.
As the path lengths get longer, the floating point calculation differences at each bounce accumulate until the entire path begins to diverge significantly between the x86-64 and arm64 versions of the render.
Fortunately, these differences basically eventually “integrate out” thanks to the magic of Monte Carlo integration; by the time the renders are near converged, the x86-64 and arm64 results are basically perceptually indistinguishable from each other:&lt;/p&gt;

&lt;div class="embed-container" id="noisecomparison_nearconverged"&gt;
&lt;/div&gt;
&lt;div class="figcaption"&gt;&lt;span&gt;Figure 5: The same cup scene from Figure 1, but now much closer to convergence (2048 spp), rendered using x86-64 (left) and arm64 (right). Note how differences between the x86-64 and arm64 renders are now basically imperceptible to the eye; these are in fact two different images! For a full screen comparison, &lt;a href="/content/images/2021/May/takua-on-arm-pt1/comparisons/noisecomparison_nearconverged.html"&gt;click here.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Below is the absolute difference between the two images above.
To the naked eye the absolute difference image looks completely black, because the differences between the two images are so small that they’re basically below the threshold of normal perception.
So, to confirm that there are in fact differences, I’ve also included below a version of the absolute difference exposed up 10 stops, or made 1024 times brighter.
Much like in the single spp renders in Figure 1, the areas of greatest difference are in the areas where the path lengths are the longest, which in this scene are areas where paths refract through the glass cup, the tea, and the ice cubes.
Just, the differences between individual paths for the same sample across x86-64 and arm64 become tiny to the point of insignificance once averaged across 2048 samples-per-pixel:&lt;/p&gt;

&lt;div class="embed-container" id="noisecomparison_diff_nearconverged"&gt;
&lt;/div&gt;
&lt;div class="figcaption"&gt;&lt;span&gt;Figure 6: Left: Absolute difference between the x86-64 and arm64 renders from Figure 2. Right: Since the absolute difference image basically looks completely black to the eye, I've also included a version of the absolute difference exposed up 10 stops (made 1024 times brighter) to make the differences more visible. For a full screen comparison, &lt;a href="/content/images/2021/May/takua-on-arm-pt1/comparisons/noisecomparison_diff_nearconverged.html"&gt;click here.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;For many extremely precise scientific applications, the level of differences above would still likely be unacceptable, but for our purposes in just making pretty pictures, I’ll call this good enough!
In fact, many rendering teams only target perceptually indistinguishable for the purposes of calling things deterministic enough, as opposed to aiming for absolute binary-level determinism; great examples include Pixar’s RenderMan XPU, Disney Animation’s Hyperion, and DreamWorks Animation’s MoonRay.&lt;/p&gt;

&lt;p&gt;Eventually maybe I’ll get around to putting more work into trying to get Takua Renderer’s per-path results to be completely consistent even across different systems and processor architectures and compilers, but for the time being I’m fine with keeping that goal as a fairly low priority relative to everything else I want to work on, because as you can see, once the renders are converged, the difference doesn’t really matter!
Floating point calculations accounted for most of the differences I was finding when comparing renders on x86-64 versus renders on arm64, but only most.
The remaining source of differences turned out… to be an actual bug!&lt;/p&gt;

&lt;div id="2021-05-29-weak-memory-ordering"&gt;&lt;/div&gt;
&lt;h2 id="weak-memory-ordering-in-arm64-and-atomic-bugs-in-takua"&gt;Weak Memory Ordering in arm64 and Atomic Bugs in Takua&lt;/h2&gt;

&lt;p&gt;Multithreaded programming with atomics and locks has a reputation for being one of the relatively more challenging skills for programmers to master, and for good reason.
Since different processor architectures often have different semantics and guarantees and rules around multithreading-related things like memory reordering, porting between different architectures is often a great way to expose subtle multithreading bugs.
The remaining source of major differences between the x86-64 and arm64 renders I was getting turned out to be caused by a memory reordering-related bug in some old multithreading code that I wrote a long time ago and forgot about.&lt;/p&gt;

&lt;p&gt;In addition to outputing the main render, Takua Renderer is also able to generate some additional render outputs, including some useful diagnostic images.
One of the diagnostic render outputs is a sample heatmap, which shows how many pixel samples were used for each pixel in the image.
I originally added the sample heatmap render output to Takua when I was &lt;a href="https://blog.yiningkarlli.com/2015/03/adaptive-sampling.html"&gt;implementing adaptive sampling&lt;/a&gt;, and since then the sample heatmap render output has been a useful tool for understanding how much time Takua is spending on different parts of the image.
One of the other things the sample heatmap render output has served as though is as a simple sanity check that Takua’s multithreaded work dispatching system is functioning correctly.
For a render where the adaptive sampler is disabled, the sample heatmap should contain exactly the same value for every single pixel in the entire image, since without adaptive sampling, every pixel is just being rendered to the target samples-per-pixel of the entire render.
So, in some of my tests, I have the renderer scripted to always output the sample heatmap, and the test system checks that the sample heatmap is completely uniform after the render as a sanity check to make sure that the renderer has rendered everything that it was supposed to.
To my surprise, sometimes on arm64, a test would fail because the sample heatmap for a render without adaptive sampling would come back as nonuniform!
Specifically, the sample heatmap would come back indicating that some pixels had received one fewer sample than the total target sample-per-pixel count across the whole render.
These pixels were always in square blocks corresponding to a specific tile, or multithreaded work dispatch unit.
The specific bug was in how Takua Renderer dispatches rendering work to each thread; to provide the relevant context and explain what I mean by a “tile”, I’ll first have to quickly describe how Takua Renderer is multithreaded.&lt;/p&gt;

&lt;p&gt;In university computer graphics courses, path tracing is often taught as being trivially simple to parallelize: since a path tracer traces individual paths in a depth-first fashion, individual paths don’t have dependencies on other paths, so just assign each path that has to be traced to a separate thread.
The easiest way to implement this simple parallelization scheme is to just run a &lt;code class="language-plaintext highlighter-rouge"&gt;parallel_for&lt;/code&gt; loop over all of the paths that need to be traced for a given set of samples, and to just repeat this for each set of samples until the render is complete.
However, in reality, parallelizing a modern production-grade path tracing renderer is often not as simple as the classic “embarrassingly parallel” approach.
Modern advanced path tracers often are written to take into account factors such as cache coherency, memory access patterns and memory locality, NUMA awareness, optimal SIMD utilization, and more.
Also, advanced path tracers often make use of various complex data structures such as out-of-core texture caches, photon maps, path guiding trees, and more.
Making sure that these data structures can be built, updated, and accessed on-the-fly by multiple threads simultaneously and efficiently often introduces complex lock-free data structure design problems.
On top of that, path tracers that use a wavefront or breadth-first architecture instead of a depth-first approach are far from trivial to parallelize, since various sorting and batching operations and synchronization points need to be accounted for.&lt;/p&gt;

&lt;p&gt;Even for relatively straightforward depth-first architectures like the one Takua has used for the past six years, the direct &lt;code class="language-plaintext highlighter-rouge"&gt;parallel_for&lt;/code&gt; approach can be improved upon in some simple ways.
Before progressive rendering became the standard modern approach, many renderers used an approach called “bucket” rendering &lt;a href="https://www.racoon-artworks.de/cgbasics/bucket_progressive.php"&gt;[Geupel 2018]&lt;/a&gt;, where the image plane was divided up into a bunch of small tiles, or buckets.
Each thread would be assigned a single bucket, and each thread would be responsible for rendering that bucket to completion before being assigned another bucket.
For offline, non-interactive rendering, bucket rendering often ends up being faster than just a simple &lt;code class="language-plaintext highlighter-rouge"&gt;parallel_for&lt;/code&gt; because bucket rendering allows for a higher degree of memory access coherency and cache coherency within each thread since each thread is always working in roughly the same area of space (at least for the first few bounces).
Even with progressive rendering as the standard approach for renderers running in an interactive mode today, many (if not most) renderers still use a bucketed approach when dispatched to a renderfarm today.
For CPU path tracers today, the number of pixels that need to be rendered for a typical image is much much larger than the number of hardware threads available on the CPU.
As a result, the basic locality idea that bucket rendering utilizes also ends up being applicable to progressive, interactive rendering in CPU path tracers (for GPU path tracing though, the GPU’s completely different, wavefront-based SIMT threading model means a bit of a different approach is necessary).
RenderMan, Arnold, and Vray in interactive progressive mode all still render pixels in a bucket-like order, although instead of having each thread render all samples-per-pixel to completion in each bucket all at once, each thread just renders a single sample-per-pixel for each bucket and then the renderer loops over the entire image plane for each sample-per-pixel number.
To differentiate using buckets in a progressive mode from using buckets in a batch mode, I will refer to buckets in progressive mode as “tiles” for the rest of this post.&lt;/p&gt;

&lt;p&gt;Takua Renderer also supports using a tiled approach for assigning work to individual threads.
At renderer startup, Takua precalculates a work assignment order, which can be in a tiled fashion, or can use a more naive &lt;code class="language-plaintext highlighter-rouge"&gt;parallel_for&lt;/code&gt; approach; the tiled mode is the default.
When using a tiled work assignment order, the specific order of tiles supports several different options; the default is a spiral starting from the center of the image.
Here’s a short screen recording demonstrating what this tiling work assignment looks like:&lt;/p&gt;

&lt;video loop=""&gt;
    &lt;source src="https://blog.yiningkarlli.com/content/images/2021/May/takua-on-arm-pt1/buckets.mp4" type="video/mp4" /&gt;
    Your browser does not support the video tag.
&lt;/video&gt;
&lt;div class="figcaption"&gt;&lt;span&gt;Figure 7: A short video showing Takua Renderer's tile assignment system running in spiral mode; each red outlined square represents a single tile. This video was captured on an arm64 M1 Mac Mini running macOS Big Sur instead of on a Raspberry Pi 4B because trying to screen record on a Raspberry Pi 4B while also running the renderer was not a good time. To see this video in a full window, &lt;a href="/content/images/2021/May/takua-on-arm-pt1/buckets.mp4"&gt;click here.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;As threads free up, the work assignment system hands each free thread a tile to render; each thread then renders a single sample-per-pixel for every pixel in its assigned tile and then goes back to the work assignment system to request more work.
Once the number of remaining tiles for the current samples-per-pixel number drops below the number of available threads, the work assignment system starts allowing multiple threads to team up on a single tile.
In general, the additional cache coherency and more localizes memory access patterns from using a tiled approach gives Takua Renderer a minimum 3% speed improvement compared to using a naive &lt;code class="language-plaintext highlighter-rouge"&gt;parallel_for&lt;/code&gt; to assign work to each thread; sometimes the speed improvement can be even higher if the scene is heavily dependent on things like texture cache access or reading from a photon map.&lt;/p&gt;

&lt;p&gt;The reason the work assignment system actually hands out tiles one by one upon request instead of just running a &lt;code class="language-plaintext highlighter-rouge"&gt;parallel_for&lt;/code&gt; loop over all of the tiles is because using something like &lt;code class="language-plaintext highlighter-rouge"&gt;tbb::parallel_for&lt;/code&gt; means that the tiles won’t actually be rendered in the correct specified order.
Actually, Takua does have a “I don’t care what order the tiles are in” mode, which does in fact just run a &lt;code class="language-plaintext highlighter-rouge"&gt;tbb::parallel_for&lt;/code&gt; over all of the tiles and lets &lt;code class="language-plaintext highlighter-rouge"&gt;tbb&lt;/code&gt;’s underlying scheduler decide what order the tiles are dispatched in; rendering tiles in a specific order doesn’t actually matter for correctness.
However, maintaining a specific tile ordering does make user feedback a bit nicer.&lt;/p&gt;

&lt;p&gt;Implementing a work dispatcher that can still maintain a specific tile ordering requires some mechanism internally to track what the next tile that should be dispatched is; Takua does so using an atomic integer inside of the work dispatcher.
This atomic is where the memory-reordering bug comes in that led to Takua occasionally dropping a single spp for a single tile on arm64.
Here’s some pesudo-code for how threads are launched and how they ask the work dispatcher for tiles to render; this is highly simplified and condensed from how the actual code in Takua is written (specifically, I’ve inlined together code from both individual threads and from the work dispatcher and removed a bunch of other unrelated stuff), but preserves all of the important details necessary to illustrate the bug:&lt;/p&gt;

&lt;div id="listing1"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;int nextTileIndex = 0;
std::atomic&amp;lt;bool&amp;gt; nextTileSoftLock(false);
tbb::parallel_for(int(0), numberOfTilesToRender, [&amp;amp;](int /*i*/) {
    bool gotNewTile = false;
    int tile = -1;
    while (!gotNewTile) {
        bool expected = false;
        if (nextTileSoftLock.compare_exchange_strong(expected, true, std::memory_order_relaxed)) {
            tile = nextTileIndex++;
            nextTileSoftLock.store(false, std::memory_order_relaxed);
            gotNewTile = true;
        }
    }
    if (tileIsInRange(tile)) {
        renderTile(tile);
    }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 1: Simplified pseudocode for the not-very-good work scheduling mechanism Takua used to assign tiles to threads. This version of the scheduler resulted in tiles occasionally being missed on arm64, but not on x64-64.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;If you remember your memory ordering rules, you already know what’s wrong with the code above; this code is really really bad!
In my defense, this code is an ancient part of Takua’s codebase; I wrote it back in college and haven’t really revisited it since, and back when I wrote it, I didn’t have the strongest grasp of memory ordering rules and how they apply to concurrent programming yet.
First off, why does this code use an atomic bool as a makeshift mutex so that multiple threads can increment a non-atomic integer, as opposed to just using an atomic integer?
Looking through the commit history, the earliest version of this code that I first prototyped (some eight years ago!) actually relied on a full-blown &lt;code class="language-plaintext highlighter-rouge"&gt;std::mutex&lt;/code&gt; to protect from race conditions around incrementing &lt;code class="language-plaintext highlighter-rouge"&gt;nextTileIndex&lt;/code&gt;; I must have prototyped this code completely single-threaded originally and then done a quick-and-dirty multithreading adaptation by just wrapping a mutex around everything, and then replaced the mutex with a cheaper atomic bool as an incredibly lazy port to a lock-free implementation instead of properly rewriting things.
I haven’t had to modify it since then because it worked well enough, so over time I must have just completely forgotten about how awful this code is.&lt;/p&gt;

&lt;p&gt;Anyhow, the fix for the code above is simple enough: just replace the first &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; in line 8 with &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_acquire&lt;/code&gt; and replace the second &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; in line 10 with &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_release&lt;/code&gt;.
An even better fix though is to just outright replace the combination of an atomic bool and non-atomic integer incremented with a single atomic integer incrementer, which is what I actually did.
But, going back to the original code, why exactly does using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; produce correctly functioning code on x86-64, but produces code that occasionally drops tiles on arm64?
Well, first, why did I use &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; in the first place?
My commit comments from eight years ago indicate that I chose &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; because I thought it would compile down to something cheaper than if I had chosen some other memory ordering flag; I really didn’t understand this stuff back then!
I wasn’t entirely wrong, although not for the reasons that I thought at the time.
On x86-64, different memory order flags don’t actually do anything, since x86-64 has a guaranteed strong memory model.
On arm64, using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_acquire&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_release&lt;/code&gt; does indeed produce simpler and faster arm64 assembly, but the simpler and faster arm64 assembly is also &lt;em&gt;wrong&lt;/em&gt; for what the code is supposed to do.
Understanding why the above happens on arm64 but not on x86-64 requires understanding what a &lt;em&gt;weakly ordered&lt;/em&gt; CPU is versus what a &lt;em&gt;strong ordered&lt;/em&gt; CPU is; arm64 is a weakly ordered architecture, whereas x86-64 is a strongly ordered architecture.&lt;/p&gt;

&lt;p&gt;One of the best resources on diving deep into weak versus strong memory orderings is the well-known series of articles &lt;a href="https://preshing.com"&gt;by Jeff Preshing&lt;/a&gt; on the topic (parts &lt;a href="https://preshing.com/20120515/memory-reordering-caught-in-the-act/"&gt;1&lt;/a&gt;, &lt;a href="https://preshing.com/20120612/an-introduction-to-lock-free-programming/"&gt;2&lt;/a&gt;, &lt;a href="https://preshing.com/20120625/memory-ordering-at-compile-time/"&gt;3&lt;/a&gt;, &lt;a href="https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/"&gt;4&lt;/a&gt;, &lt;a href="https://preshing.com/20120913/acquire-and-release-semantics/"&gt;5&lt;/a&gt;, &lt;a href="https://preshing.com/20120930/weak-vs-strong-memory-models/"&gt;6&lt;/a&gt;, and &lt;a href="https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/"&gt;7&lt;/a&gt;).
Actually, while I was going back through the Preshing on Programming series in preparation to write this post, I noticed that by hilarious coincidence the older code in Takua represented by Listing 1, once boiled down to what it is fundamentally doing, is extremely similar to the canonical example used in Preshing on Programming’s “&lt;a href="https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/"&gt;This Is Why They Call It a Weakly-Ordered CPU&lt;/a&gt;” article.
If only I had read the Preshing on Programming series a year before implementing Takua’s work assignment system instead of a few years after!
I’ll do my best to quickly recap what the Preshing on Programming series covers about weak versus strong memory orderings here, but if you have not read Jeff Preshing’s articles before, I’d recommend taking some time later to do so.&lt;/p&gt;

&lt;p&gt;One of the single most important things that lock-free multithreaded code needs to take into account is the potential for memory reordering.
Memory reordering is when the compiler and/or the processor decides to optimize code by changing the ordering of instructions that access and modify memory.
Memory reordering is always carried out in such a way that the behavior of a single-threaded program never changes, and multithreaded code using locks such as mutexes forces the compiler and processor to not reorder instructions across the boundaries defined by locks.
However, lock-free multithreaded code is basically free range for the compiler and processor to do whatever they want; even though memory reordering is carried out for each individual thread in such a way that keeps the apparent behavior of that specific thread the same as before, this rule does not take into account the interactions &lt;em&gt;between&lt;/em&gt; threads, so different reorderings in different threads that keep behavior the same in each thread isolated can still result in very different behavior in the overall multithreaded behavior.&lt;/p&gt;

&lt;p&gt;The easiest way to disable any kind of memory reordering at compile time is to just… disable all compiler optimizations.
However, in practice we never actually want to do this, because disabling compiler optimizations means all of our code will run slower (sometimes a lot slower).
Instruction selection to lower from IR to assembly also means that even disabling all compiler optimizations may not be enough to ensure no memory reordering, because we still need to contend with potential memory reordering at runtime from the CPU.&lt;/p&gt;

&lt;p&gt;Memory reordering in multithreaded code happens on the CPU because of how CPUs access memory: modern processors have a series of caches (L1, L2, sometimes L3, etc) sitting between the actual registers in each CPU core and main memory.
Some of these cache levels (usually L1) are per-CPU-core, and some of these cache levels (usually L2 and higher) are shared across some or all cores.
The lower the cache level number, the faster and also smaller that cache level typically is, and the higher the cache level number, the slower and larger that cache level is.
When a CPU wants to read a particular piece of data, it will check for it in cache first, and if the value is not in cache, then the CPU must make a fetch request to main memory for the value; fetching from main memory is obviously much slower than fetching from cache.
Where these caches get tricky is how data is propagated from a given CPU core’s registers and caches back to main memory and then eventually up again into the L1 caches for other CPU cores.
This propagation can happen… whenever!
A variety of different possible implementation strategies exist for &lt;a href="https://en.wikipedia.org/wiki/CPU_cache#Policies"&gt;when caches update from and write back to main memory&lt;/a&gt;, with the end result being that by default we as programmers have no reliable way of guessing when data transfers between cache and main memory will happen.&lt;/p&gt;

&lt;p&gt;Imagine that we have some multithreaded code written such that one thread writes, or stores, to a value, and then a little while later, another thread reads, or loads, that same value.
We would expect the store on the first thread to always precede the load on the second thread, so the second thread should always pick up whatever value the first thread read.
However, if we implement this code just using a normal int or float or bool or whatever, what can actually happen at runtime is our first thread writes the value to L1 cache, and then eventually the value in L1 cache gets written back to main memory.
However, before the value manages to get propagated from L1 cache back to main memory, the second thread reads the value out of main memory.
In this case, from the perspective of main memory, the second thread’s load out of main memory takes place &lt;em&gt;before&lt;/em&gt; the first thread’s store has rippled back down to main memory.
This case is an example of &lt;em&gt;StoreLoad&lt;/em&gt; reordering, so named because a store has been reordered with a later load.
There are also &lt;em&gt;LoadStore&lt;/em&gt;, &lt;em&gt;LoadLoad&lt;/em&gt;, and &lt;em&gt;StoreStore&lt;/em&gt; reorderings that are possible.
Jeff Preshing’s “&lt;a href="https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/"&gt;Memory Barriers are Like Source Control&lt;/a&gt;” article does a great job of describing these four possible reordering scenarios in detail.&lt;/p&gt;

&lt;p&gt;Different CPU architectures make different guarantees about which types of memory reordering can and can’t happen on that particular architecture at the hardware level.
A processor that guarantees absolutely no memory reordering of any kind is said to have a &lt;em&gt;sequentially consistent&lt;/em&gt; memory model.
Few, if any modern processor architecture provide a guaranteed sequentially consistent memory model.
Some processors don’t guarantee absolutely sequential consistency, but do guarantee that at least when a CPU core makes a series of writes, other CPU cores will see those writes in the same sequence that they were made; CPUs that make this guarantee have a &lt;em&gt;strong&lt;/em&gt; memory model.
Strong memory models effectively guarantee that StoreLoad reordering is the only type of reordering allowed; x86-64 has a strong memory model.
Finally, CPUs that allow for any type of memory reordering at all are said to have a &lt;em&gt;weak&lt;/em&gt; memory model.
The arm64 architecture uses a weak memory model, although arm64 at least guarantees that if we read a value through a pointer, the value read will be at least as new as the pointer itself.&lt;/p&gt;

&lt;p&gt;So, how can we possibly hope to be able to reason about multithreaded code when both the compiler and the processor can happily reorder our memory access instructions between threads whenever they want for whatever reason they want?
The answer is in memory barriers and fence instructions; these tools allow us to specify boundaries that the compiler cannot reorder memory access instructions across and allow us to force the CPU to make sure that values are flushed to main memory before being read.
In C++, specifying barriers and fences can be done by using compiler intrinsics that map to specific underlying assembly instructions, but the easier and more common way of doing this is by using &lt;a href="https://en.cppreference.com/w/cpp/atomic/memory_order"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order&lt;/code&gt;&lt;/a&gt; flags in combination with atomics.
Other languages have similar concepts; for example, &lt;a href="https://doc.rust-lang.org/nomicon/atomics.html"&gt;Rust’s atomic access flags&lt;/a&gt; are very similar to the C++ memory ordering flags.&lt;/p&gt;

&lt;p&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order&lt;/code&gt; flags specify how memory accesses for all operations surrounding an atomic are to be ordered; the impacted surrounding operations include all non-atomics.
There are a whole bunch of &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order&lt;/code&gt; flags; we’ll examine the few that are relevant to the specific example in Listing 1.
The heaviest hammer of all of the flags is &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt;, which enforces absolute sequential consistency at the cost of potentially being more expensive due to potentially needing more loads and/or stores.
For example, on x86-64, &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt; is often implemented using slower &lt;code class="language-plaintext highlighter-rouge"&gt;xchg&lt;/code&gt; or paired &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;mfence&lt;/code&gt; instructions instead of a single &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instruction, and on arm64, the overhead is even greater due to arm64’s weak memory model.
Using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt; also potentially disallows the CPU from reordering unrelated, longer running instructions to starting (and therefore finish) earlier, potentially causing even more slowdowns.
In C++, atomic operations default to using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt; if no memory ordering flag is explicitly specified.
Contrast with &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt;, which is the exact opposite of &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt;.
&lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; enforces no synchronization or ordering constraints whatsoever; on an architecture like x86-64, using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; can be faster than using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt; if your memory ordering requirements are already met in hardware by x86-64’s strong memory model.
However, being sloppy with &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_relaxed&lt;/code&gt; can result in some nasty nondeterministic bugs on arm64 if your code requires specific memory ordering guarantees, due to arm64’s weak memory model.
The above is the exact reason why the code in Listing 1 occasionally resulted in dropped tiles in Takua on arm64!&lt;/p&gt;

&lt;p&gt;Without any kind of memory ordering constraints, with arm64’s weak memory ordering, the code in Listing 1 can sometimes execute in such a way that one thread sets &lt;code class="language-plaintext highlighter-rouge"&gt;nextTileSoftLock&lt;/code&gt; to true, but another thread attempts to check &lt;code class="language-plaintext highlighter-rouge"&gt;nextTileSoftLock&lt;/code&gt; before the first thread’s new value propagates back to main memory and to all of the other threads.
As a result, two threads can end up in a race condition, trying to both increment the non-atomic &lt;code class="language-plaintext highlighter-rouge"&gt;nextTileIndex&lt;/code&gt; at the same time.
When this happens, two threads can end up working on the same tile at the same time or a tile can get skipped!
We could fix this problem by just removing the memory ordering flags entirely from Listing 1, allowing everything to default back to &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt;, which would fix the problem.
However, as just mentioned above, we can do better than using &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_seq_cst&lt;/code&gt; if we know specifically what memory ordering requirements we need for the code to work correctly.&lt;/p&gt;

&lt;p&gt;Enter &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_acquire&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_release&lt;/code&gt;, which represent &lt;em&gt;acquire&lt;/em&gt; semantics and &lt;em&gt;release&lt;/em&gt; semantics respectively and, when used correctly, always come in a pair.
Acquire semantics apply to load (read) operations and prevent memory ordering of the load operation
with any subsequent read or write operation.
Release semantics apply to store (write) operations and prevent memory reordering of the store operation with any preceding read or write operation.
In other words, &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_acquire&lt;/code&gt; tells the compiler to issue instructions that prevent LoadLoad and LoadStore reordering from happening, and &lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order_release&lt;/code&gt; tells the compiler to issue instructions that prevent LoadStore and StoreStore reordering from happening.
Using acquire and release semantics allows Listing 1 to work correctly on arm64, while being ever so slightly cheaper compared with enforcing absolute sequential consistency everywhere.&lt;/p&gt;

&lt;p&gt;What is the takeaway from this long tour through memory reordering and weak and strong memory models and memory ordering constraints?
The takeaway is that when writing multithreaded code that needs to be portable across architectures with different memory ordering guarantees, such as x86-64 versus arm64, we need to be very careful with thinking about how each architecture’s memory ordering guarantees (or lack thereof) impact any lock-free cross-thread communication we need to do!
Atomic code often can be written more sloppily on x86-64 than on arm64 and still have a good chance of working, whereas arm64’s weak memory model means there’s much less room for being sloppy.
If you want a good way to smoke out potential bugs in your lock-free atomic code, porting to arm64 is a good way to find out!&lt;/p&gt;

&lt;div id="2021-05-29-deep-dive-on-x86-64-vs-arm64"&gt;&lt;/div&gt;
&lt;h2 id="a-deep-dive-on-x86-64-versus-arm64-through-the-lens-of-compiling-stdatomiccompare_exchange_weak"&gt;A Deep Dive on x86-64 versus arm64 Through the Lens of Compiling &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic::compare_exchange_weak()&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;While I was looking for the source of the memory reordering bug, I found a separate interesting bug in Takua’s atomic framebuffer… or at least, I thought it was a bug.
The thing I found turned out to not be a bug at all in the end, but at the time I thought that there was a bug in the form of a race condition in an atomic compare-and-exchange loop.
I figured that the renderer must be just running correctly &lt;em&gt;most&lt;/em&gt; of the time instead of &lt;em&gt;all&lt;/em&gt; of the time, but as I’ll explain in a little bit, the renderer actually provably runs correctly 100% of the time.
Understanding what was going on here led me to dive into the compiler’s assembly output, and wound up being an interesting case study in comparing how the same exact C++ source code compiles to x86-64 versus arm64.
In order to provide the context for the not-a-bug and what I learned about arm64 from it, I need to first briefly describe what Takua’s atomic framebuffer is and how it is used.&lt;/p&gt;

&lt;p&gt;Takua supports multiple threads writing to the same pixel in the framebuffer at the same time.
There are two major uses cases for this capability: first, integration techniques that use light tracing will connect back to the camera completely arbitrarily, resulting in splats to the framebuffer that are completely unpredictable and possibly overlapping on the same pixels.
Second, adaptive sampling techniques that redistribute sample allocation within a single iteration (meaning launching a single set of pixel samples) can result in multiple samples for the same pixel in the same iteration, which means multiple threads can be calculating paths starting from the same pixel and therefore multiple threads need to write to the same framebuffer pixel.
In order to support multiple threads writing simultaneously to the same pixel in the framebuffer, there are three possible implementation options.
The first option is to just keep a separate framebuffer per thread and merge afterwards, but this approach obviously requires potentially a huge amount of memory.
The second option is to never write to the framebuffer directly, but instead keep queues of framebuffer write requests that occasionally get flushed to the framebuffer by a dedicated worker thread (or some variation thereof).
The third option is to just make each pixel in the framebuffer support exclusive operations through atomics (a mutex per pixel works too, but obviously this would involve much more overhead and might be slower); this option is the atomic framebuffer.
I actually implemented the second option in Takua a long time ago, but the added complexity and performance impact of needing to flush the queue led me to eventually replace the whole thing with an atomic framebuffer.&lt;/p&gt;

&lt;p&gt;The tricky part of implementing an atomic framebuffer in C++ is the need for atomic floats.
Obviously each pixel in the framebuffer has to store at the very least accumulated radiance values for the base RGB primaries, along with potentially other AOV values, and accumulated radiance values and many common AOVs all have to be represented with floats.
Modern C++ has standard library support for atomic types through std::atomic, and std::atomic works with floats.
However, pre-C++20, std::atomic only provides atomic arithmetic operations for integer types.
C++20 adds &lt;code class="language-plaintext highlighter-rouge"&gt;fetch_add()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;fetch_sub()&lt;/code&gt; implementations for &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;float&amp;gt;&lt;/code&gt;, but I wrote Takua’s atomic framebuffer way back when C++11 was still the latest standard.
So, pre-C++20, if you want atomic arithmetic operations for &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;float&amp;gt;&lt;/code&gt;, you have to implement it yourself.
Fortunately, pre-C++20 does provide &lt;code class="language-plaintext highlighter-rouge"&gt;compare_and_exchange()&lt;/code&gt; implementations for all atomic types, and that’s all we need to implement everything else we need ourselves.&lt;/p&gt;

&lt;p&gt;Implementing &lt;code class="language-plaintext highlighter-rouge"&gt;fetch_add()&lt;/code&gt; for atomic floats is fairly straightforward.
Let’s say we want to add a value &lt;code class="language-plaintext highlighter-rouge"&gt;f1&lt;/code&gt; to an atomic float &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt;.
The basic idea is to do an atomic load from &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; into some temporary variable &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt;.
A standard &lt;code class="language-plaintext highlighter-rouge"&gt;compare_and_exchange()&lt;/code&gt; implementation compares some input value with the current value of the atomic float, and if the two are equal, replaces the current value of the atomic float with a second input value; C++ provides an implementations in the form of &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_strong()&lt;/code&gt;.
So, all we need to do is run &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; on &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; where the value we use for the comparison test is &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt; and the replacement value is &lt;code class="language-plaintext highlighter-rouge"&gt;oldval + f1&lt;/code&gt;; if &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; succeeds, we return &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt;, otherwise, loop and repeat until &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; succeeds.
Here’s an example implementation:&lt;/p&gt;

&lt;div id="listing2"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;float addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp; f0, const float f1) {
    do {
        float oldval = f0.load();
        float newval = oldval + f1;
        if (f0.compare_exchange_weak(oldval, newval)) {
            return oldval;
        }
    } while (true);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 2: Example implementation of atomic float addition.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Seeing why the above implementation works should be very straightforward: imagine two threads are calling the above implementation at the same time.
We want each thread to reload the atomic float on each iteration because we never want a situation where a first thread loads from &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt;, a second thread succeeds in adding to &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt;, and then the first thread also succeeds in writing its value to &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt;, because upon the first thread writing, the value of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; that the first thread used for the addition operation is out of date!&lt;/p&gt;

&lt;p&gt;Well, here’s the implementation that has actually been in Takua’s atomic framebuffer implementation for most of the past decade.
This implementation is very similar to Listing 2, but compared with Listing 2, Lines 2 and 3 are swapped from where they should be; I likely swapped these two lines through a simple copy/paste error or something when I originally wrote it.
This is the implementation that I suspected was a bug upon revisiting it during the arm64 porting process:&lt;/p&gt;

&lt;div id="listing3"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;float addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp; f0, const float f1) {
    float oldval = f0.load();
    do {
        float newval = oldval + f1;
        if (f0.compare_exchange_weak(oldval, newval)) {
            return oldval;
        }
    } while (true);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 3: What I thought was an incorrect implementation of atomic float addition.&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;In the Listing 3 implementation, note how the atomic load of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; only ever happens once outside of the loop.
The following is what I thought was going on and why at the moment I thought this implementation was wrong:
Think about what happens if a first thread loads from &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; and then a second thread’s call to &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; succeeds before the first thread gets to &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt;; in this race condition scenario, the first thread should get stuck in an infinite loop.
Since the value of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; has now been updated by the second thread, but the first thread never reloads the value of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; inside of the loop, the first thread &lt;em&gt;should have no way of ever succeeding at the&lt;/em&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; &lt;em&gt;call&lt;/em&gt;!
However, in reality, with the Listing 3 implementation, Takua never actually gets stuck in an infinite loop, even when multiple threads are writing to the same pixel in the atomic framebuffer.
I initially thought that I must have just been getting really lucky every time and multiple threads, while attempting to accumulate to the same pixel, just never happened to produce the specific &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; call ordering that would cause the race condition and infinite loop.
But then I repeatedly tried a simple test where I had 32 threads simultaneously call &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; for the same atomic float a million times per thread, and… still an infinite loop never occurred.
So, the situation appeared to be that what I thought was &lt;em&gt;incorrect code&lt;/em&gt; was always behaving as if it had been written &lt;em&gt;correctly&lt;/em&gt;, and furthermore, this held true on both x86-64 &lt;em&gt;and&lt;/em&gt; on arm64, across both compiling with Clang on macOS and compiling with GCC on Linux.&lt;/p&gt;

&lt;p&gt;If you are well-versed in the C++ specifications, you already know which crucial detail I had forgotten that explains why Listing 3 is actually completely correct and functionally equivalent to Listing 2.
Under the hood, &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak(T&amp;amp; expected, T desired)&lt;/code&gt; requires doing an atomic load of the target value in order to compare the target value with &lt;code class="language-plaintext highlighter-rouge"&gt;expected&lt;/code&gt;.
What I had forgotten was that if the comparison fails, &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak()&lt;/code&gt; doesn’t just return a false bool; the function &lt;em&gt;also replaces&lt;/em&gt; &lt;code class="language-plaintext highlighter-rouge"&gt;expected&lt;/code&gt; with the result of the atomic load on the target value!
So really, there isn’t only a single atomic load of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; in Listing 3; there’s actually an atomic load of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; in every loop as part of &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt;, and in the event that the comparison fails, the equivalent of &lt;code class="language-plaintext highlighter-rouge"&gt;oldval = f0.load()&lt;/code&gt; happens.
Of course, I didn’t actually correctly remember what &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; does in the comparison failure case, and I stupidly didn’t double check &lt;a href="https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange"&gt;cppreference&lt;/a&gt;, so it took me much longer to figure out what was going on.&lt;/p&gt;

&lt;p&gt;So, still missing the key piece of knowledge that I had forgotten and assuming that &lt;code class="language-plaintext highlighter-rouge"&gt;compare_exchange_weak()&lt;/code&gt; didn’t modify any inputs upon comparison failure, my initial guess was that perhaps the compiler was inlining &lt;code class="language-plaintext highlighter-rouge"&gt;f0.load()&lt;/code&gt; wherever &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt; was being used as an optimization, which would produce a result that should prevent the race condition from ever happening.
However, after a bit more thought, I concluded that this optimization was very unlikely, since it both changes the written semantics of what the code should be doing by effectively moving an operation from outside a loop to the inside of the loop, and also inlining &lt;code class="language-plaintext highlighter-rouge"&gt;f0.load()&lt;/code&gt; wherever &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt; is used is not actually a safe code transformation and can produce a different result from the originally written code, since having two atomic loads from &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; introduces the possibility that another thread can do an atomic write to &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; in between the current thread’s two atomic loads.&lt;/p&gt;

&lt;p&gt;Things got even more interesting when I tried adding in an additional bit of indirection around the atomic load of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; into &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt;.
Here is an actually incorrect implementation that I thought should be functionally equivalent to the implementation in Listing 3:&lt;/p&gt;

&lt;div id="listing4"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;float addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp; f0, const float f1) {
    const float oldvaltemp = f0.load();
    do {
        float oldval = oldvaltemp;
        float newval = oldval + f1;
        if (f0.compare_exchange_weak(oldval, newval)) {
            return oldval;
        }
    } while (true);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 4: An actually incorrect implementation of atomic float addition that might appear to be semantically identical to the implementation in Listing 3 if you've forgotten a certain very important detail about std::compare_exchange_weak().&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Creating the race condition and subsequent infinite loop is extremely easy and reliable with Listing 4.
So, to summarize where I was at this point: Listing 2 is a correctly written implementation that produces a correct result in reality, Listing 4 is an incorrectly written implementation that, as expected, produces an incorrect result in reality, and Listing 3 is what I thought was an incorrectly written implementation that I thought was &lt;em&gt;semantically identical&lt;/em&gt; to Listing 4, but actually produces the same correct result in reality as Listing 2!&lt;/p&gt;

&lt;p&gt;So, left with no better ideas, I decided to just go look directly at the compiler’s output assembly.
To make things a bit easier, we’ll look at and compare the x86-64 assembly for the Listing 2 and Listing 3 C++ implementations first, and explain what important detail I had missed that led me down this wild goose chase.
Then, we’ll look at and compare the arm64 assembly, and we’ll discuss some interesting things I learned along the way by comparing the x86-64 and arm64 assembly for the same C++ function.&lt;/p&gt;

&lt;p&gt;Here is the corresponding x86-64 assembly for the correct C++ implementation in Listing 2, compiled with Clang 10.0.0 using -O3.
For readers who are not very used to reading assembly, I’ve included annotations as comments in the assembly code to describe what the assembly code is doing and how it corresponds back to the original C++ code:&lt;/p&gt;

&lt;div id="listing5"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):  # f0 is dword ptr [rdi], f1 is xmm0
.LBB0_1:
        mov           eax, dword ptr [rdi]   # eax = *arg0 = f0.load()
        movd          xmm1, eax              # xmm1 = eax = f0.load()
        movdqa        xmm2, xmm1             # xmm2 = xmm1 = eax = f0.load()
        addss         xmm2, xmm0             # xmm2 = (xmm2 + xmm0) = (f0 + f1)
        movd          ecx, xmm2              # ecx = xmm2 = (f0 + f1)
        lock cmpxchg  dword ptr [rdi], ecx   # if eax == *arg0 { ZF = 1; *arg0 = arg1 }
                                             #    else { ZF = 0; eax = *arg0 };
                                             #    "lock" means all done exclusively
        jne           .LBB0_1                # if ZF == 0 goto .LBB0_1
        movdqa        xmm0, xmm1             # return f0 value from before cmpxchg
        ret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 5: x86-64 assembly corresponding to the implementation in Listing 2, with my annotations in the comments. Compiled using armv8-a Clang 10.0.0 using -O3. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:9,endLineNumber:5,positionColumn:9,positionLineNumber:5,selectionStartColumn:9,selectionStartLineNumber:5,startColumn:9,startLineNumber:5),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++do+%7B%0A++++++++float+oldval+%3D+f0.load()%3B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_weak(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Here is the corresponding x86-64 assembly for the C++ implementation in Listing 3; again, this is the version that produces the same correct result as Listing 2.
Just like with Listing 5, this was compiled using Clang 10.0.0 using -O3, and descriptive annotations are in the comments:&lt;/p&gt;

&lt;div id="listing6"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):  # f0 is dword ptr [rdi], f1 is xmm0
        mov           eax, dword ptr [rdi]   # eax = *arg0 = f0.load()
.LBB0_1:
        movd          xmm1, eax              # xmm1 = eax = f0.load()
        movdqa        xmm2, xmm1             # xmm2 = xmm1 = eax = f0.load()
        addss         xmm2, xmm0             # xmm2 = (xmm2 + xmm0) = (f0 + f1)
        movd          ecx, xmm2              # ecx = xmm2 = (f0 + f1)
        lock cmpxchg  dword ptr [rdi], ecx   # if eax == *arg0 { ZF = 1; *arg0 = arg1 }
                                             #    else { ZF = 0; eax = *arg0 };
                                             #    "lock" means all done exclusively
        jne           .LBB0_1                # if ZF == 0 goto .LBB0_1
        movdqa        xmm0, xmm1             # return f0 value from before cmpxchg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 6: x86-64 assembly corresponding to the implementation in Listing 3, with my annotations in the comments. Compiled using armv8-a Clang 10.0.0 using -O3. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:1,endLineNumber:3,positionColumn:1,positionLineNumber:3,selectionStartColumn:1,selectionStartLineNumber:3,startColumn:1,startLineNumber:3),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++float+oldval+%3D+f0.load()%3B%0A++++do+%7B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_weak(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'x86-64+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;The compiled x86-64 assembly in Listing 5 and Listing 6 is almost identical; the only difference is that in Listing 5, copying data from the address stored in register &lt;code class="language-plaintext highlighter-rouge"&gt;rdi&lt;/code&gt; to register &lt;code class="language-plaintext highlighter-rouge"&gt;eax&lt;/code&gt; happens after label &lt;code class="language-plaintext highlighter-rouge"&gt;.LBB0_1&lt;/code&gt; and in Listing 6 the copy happens before label &lt;code class="language-plaintext highlighter-rouge"&gt;.LBB0_1&lt;/code&gt;.
Comparing the x86-64 assembly with the C++ code, we can see that this difference corresponds directly to where &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt;’s value is atomically loaded into &lt;code class="language-plaintext highlighter-rouge"&gt;oldval&lt;/code&gt;.
We can also see that &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;float&amp;gt;::compare_exchange_weak()&lt;/code&gt; compiles down to a single &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; instruction, which as the instruction name suggests, is a compare and exchange operation.
The &lt;code class="language-plaintext highlighter-rouge"&gt;lock&lt;/code&gt; instruction prefix in front of &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; ensures that the current CPU core has exclusive ownership of the corresponding cache line for the duration of the &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; operation, which is how the operation is made atomic.&lt;/p&gt;

&lt;p&gt;This is the point where I eventually realized what I had missed.
I actually didn’t notice immediately; figuring out what I had missed didn’t actually occur to me until several days later!
The thing that finally made me realize what I had missed and made me understand why Listing 3 / Listing 6 don’t actually result in an infinite loop and instead match the behavior of Listing 2 / Listing 5 lies in &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt;.
Let’s take a look at the official &lt;a href="https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html"&gt;Intel 64 and IA-32 Architectures Software Developer’s Manual&lt;/a&gt;’s description &lt;a href="https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html"&gt;[Intel 2021]&lt;/a&gt; of what &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; does:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Compares the value in the AL, AX, EAX, or RAX register with the first operand (destination operand). If the two values are equal, the second operand (source operand) is loaded into the destination operand. Otherwise, the destination operand is loaded into the AL, AX, EAX or RAX register. RAX register is available only in 64-bit mode.&lt;/p&gt;

  &lt;p&gt;This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the compare part of &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; fails, &lt;em&gt;the first operand is loaded into the EAX register&lt;/em&gt;!
After thinking about this property of &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; for a bit, I finally had my head-smack moment and remembered that &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak(T&amp;amp; expected, T desired)&lt;/code&gt; replaces &lt;code class="language-plaintext highlighter-rouge"&gt;expected&lt;/code&gt; with the result of the atomic load in the event of comparison failure.
This property of &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak()&lt;/code&gt; is why &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak()&lt;/code&gt; can be compiled down to a single &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; instruction on x86-64 in the first place.
We can actually see the compiler being clever here in Listing 6 and exploiting the fact that &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; comparison failure mode writes into the &lt;code class="language-plaintext highlighter-rouge"&gt;eax&lt;/code&gt; register: the compiler chooses to use &lt;code class="language-plaintext highlighter-rouge"&gt;eax&lt;/code&gt; as the target for the &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instruction in Line 1 instead of using some other register so that a second move from &lt;code class="language-plaintext highlighter-rouge"&gt;eax&lt;/code&gt; into some other register isn’t necessary after &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt;.
If anything, the implementation in Listing 3 / Listing 6 is actually slightly &lt;em&gt;more&lt;/em&gt; efficient than the implementation in Listing 2 / Listing 5, since there is one fewer &lt;code class="language-plaintext highlighter-rouge"&gt;mov&lt;/code&gt; instruction needed in the loop.&lt;/p&gt;

&lt;p&gt;So what does this have to do with learning about arm64?
Well, while I was in the process of looking at the x86-64 assembly to try to understand what was going on, I also tried the implementation in Listing 3 on my Raspberry Pi 4B just to sanity check if things worked the same on arm64.
At that point I hadn’t realized that the code in Listing 3 was actually correct yet, so I was beginning to consider possibilities like a compiler bug or weird platform-specific considerations that I hadn’t thought of, so to rule those more exotic explanations out, I decided to see if the code worked the same on x86-64 and arm64.
Of course the code worked exactly the same on both, so the next step was to also examine the arm64 assembly in addition to the x86-64 assembly.
Comparing the same code’s corresponding assembly for x86-64 and arm64 at the same time proved to be a very interesting exercise in getting to better understand some low-level and general differences between the two instruction sets.&lt;/p&gt;

&lt;p&gt;Here is the corresponding arm64 assembly for the implementation in Listing 2; this is the arm64 assembly that is the direct counterpart to the x86-64 assembly in Listing 5.
This arm64 assembly was also compiled with Clang 10.0.0 using -O3.
I’ve included annotations here as well, although admittedly my arm64 assembly comprehension is not as good as my x86-64 assembly comprehension, since I’m relatively new to compiling for arm64.
If you’re well versed in arm64 assembly and see a mistake in my annotations, feel free to send me a correction!&lt;/p&gt;

&lt;div id="listing7"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):
        b       .LBB0_2              // goto .LBB0_2
.LBB0_1:
        clrex                        // clear this thread's record of exclusive lock
.LBB0_2:
        ldar    w8, [x0]             // w8 = *arg0 = f0, non-atomically loaded
        ldaxr   w9, [x0]             // w9 = *arg0 = f0.load(), atomically
                                     //    loaded (get exclusive lock on x0), with
                                     //    implicit synchronization
        fmov    s1, w8               // s1 = w8 = f0
        fadd    s2, s1, s0           // s2 = s1 + s0 = (f0 + f1)
        cmp     w9, w8               // compare non-atomically loaded f0 with atomically
                                     //    loaded f0 and store result in N
        b.ne    .LBB0_1              // if N==0 { goto .LBB0_1 }
        fmov    w8, s2               // w8 = s2 = (f0 + f1)
        stlxr   w9, w8, [x0]         // if this thread has the exclusive lock,
                                     //    { *arg0 = w8 = (f0 + f1), release lock },
                                     //    store whether or not succeeded in w9
        cbnz    w9, .LBB0_2          // if w9 says exclusive lock failed { goto .LBB0_2}
        mov     v0.16b, v1.16b       // return f0 value from ldaxr
        ret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 7: arm64 assembly corresponding to Listing 2, with my annotations in the comments. Compiled using arm64 Clang 10.0.0 using -O3. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:9,endLineNumber:5,positionColumn:9,positionLineNumber:5,selectionStartColumn:9,selectionStartLineNumber:5,startColumn:9,startLineNumber:5),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++do+%7B%0A++++++++float+oldval+%3D+f0.load()%3B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_weak(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:armv8-clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'armv8-a+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;I should note here that the specific version of arm64 that Listing 7 was compiled for is &lt;a href="https://developer.arm.com/documentation/ddi0487/ga"&gt;ARMv8.0-A&lt;/a&gt;, which is what Clang and GCC both default to when compiling for arm64; this detail will become important a little bit later in this post.
When we compare Listing 7 with Listing 5, we can immediately see some major differences between the arm64 and x86-64 instruction sets, aside from superficial stuff like how registers are named.
The arm64 version is just under twice as long as the x86-64 version, and examining the code, we can see that most of the additional length comes from how the atomic compare-and-exchange is implemented.
Actually, the rest of the code is very similar; the rest of the code is just moving stuff around to support the addition operation and to deal with setting up and jumping to the top of the loop.
In the compare and exchange code, we can see that the arm64 version does not have a single instruction to implement the atomic compare-and-exchange!
While the x86-64 version can compile &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;float&amp;gt;::compare_exchange_weak()&lt;/code&gt; down into a single &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; instruction, ARMv8.0-A has no equivalent instruction, so the arm64 version instead must use three separate instructions to implement the complete functionality: &lt;code class="language-plaintext highlighter-rouge"&gt;ldaxr&lt;/code&gt; to do an exclusive load, &lt;code class="language-plaintext highlighter-rouge"&gt;stlxr&lt;/code&gt; to do an exclusive store, and &lt;code class="language-plaintext highlighter-rouge"&gt;clrex&lt;/code&gt; to reset the current thread’s record of exclusive access requests.&lt;/p&gt;

&lt;p&gt;This difference speaks directly towards x86-84 being a &lt;a href="https://en.wikipedia.org/wiki/Complex_instruction_set_computer"&gt;CISC architecture&lt;/a&gt; and arm64 being a &lt;a href="https://en.wikipedia.org/wiki/Reduced_instruction_set_computer"&gt;RISC architecture&lt;/a&gt;.
x86-64’s CISC nature calls for the ISA to have a large number of instructions carrying out complex often-multistep operations, and this design philosophy is what allows x86-64 to encode complex multi-step operations like a compare-and-exchange as a single instruction.
Conversely, arm64’s RISC nature means a design consisting of fewer, simpler operations &lt;a href="https://doi.org/10.1145/641914.641917"&gt;[Patterson and Ditzel 1980]&lt;/a&gt;; for example, the RISC design philosophy mandates that memory access be done through specific single-cycle instructions instead of as part of a more complex instruction such as compare-and-exchange.
These differing design philosophies mean that in arm64 assembly, we will often see many instructions used to implement what would be a single instruction in x86_64; given this difference, compiling Listing 2 produces surprisingly structurally similarities in the output x86_64 (Listing 5) and arm64 (Listing 7) assembly.
However, if we take the implementation of &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; in Listing 3 and compile it for arm64’s ARMv8.0-A revision, structural differences between the x86-64 and arm64 output become far more apparent:&lt;/p&gt;

&lt;div id="listing8"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):
        ldar    w9, [x0]             // w9 = *arg0 = f0, non-atomically loaded
        ldaxr   w8, [x0]             // w8 = *arg0 = f0.load(), atomically
                                     // loaded (get exclusive lock on x0), with
                                     // implicit synchronization
        fmov    s1, w9               // s1 = s9 = f0
        cmp     w8, w9               // compare non-atomically loaded f0 with atomically
                                     // loaded f0 and store result in N
        b.ne    .LBB0_3              // if N==0 { goto .LBB0_3 }
        fadd    s2, s1, s0           // s2 = s1 + s0 = (f0 + f1)
        fmov    w9, s2               // w9 = s2 = (f0 + f1)
        stlxr   w10, w9, [x0]        // if this thread has the exclusive lock,
                                     //    { *arg0 = w9 = (f0 + f1), release lock },
                                     //    store whether or not succeeded in w10
        cbnz    w10, .LBB0_4.        // if w10 says exclusive lock failed { goto .LBBO_4 }
        mov     w9, #1.              // w9 = 1 (???)
        tbz     w9, #0, .LBB0_8.     // if bit 0 of w9 == 0 { goto .LBB0_8 }
        b       .LBB0_5              // goto .LBB0_5
.LBB0_3:
        clrex.                       // clear this thread's record of exclusive lock
.LBB0_4:
        mov     w9, wzr              // w9 = 0
        tbz     w9, #0, .LBB0_8      // if bit 0 of w9 == 0 { goto .LBBO_8 }
.LBB0_5:
        mov     v0.16b, v1.16b.      // return f0 value from ldaxr
        ret
.LBB0_6:
        clrex                        // clear this thread's record of exclusive lock
.LBB0_7:
        mov     w10, wzr             // w10 = 0
        mov     w8, w9               // w8 = w9
        cbnz    w10, .LBB0_5         // if w10 is not zero { goto .LBB0_5 }
.LBB0_8:
        ldaxr   w9, [x0]             // w9 = *arg0 = f0.load(), atomically
                                     //    loaded (get exclusive lock on x0), with
                                     //    implicit synchronization
        fmov    s1, w8               // s1 = w0 = f0
        cmp     w9, w8               // compare non-atomically loaded f0 with atomically
                                     // loaded f0 and store result in N
        b.ne    .LBB0_6              // if N==0 { goto .LBBO_6 }
        fadd    s2, s1, s0           // s2 = s1 + s0 = (f0 + f1)
        fmov    w8, s2               // w2 = s2 = (f0 + f1)
        stlxr   w10, w8, [x0]        // if this thread has the exclusive lock,
                                     //    { *arg0 = w8 = (f0 + f1), release lock },
                                     //    store whether or not succeeded in w10
        cbnz    w10, .LBB0_7         // if w10 says exclusive lock failed { goto .LBB0_7 }
        mov     w10, #1              // w10 = 1
        mov     w8, w9               // w8 = w9 = f0.load()
        cbz     w10, .LBB0_8         // if w10==0 { goto .LBB0_8 }
        b       .LBB0_5              // goto .LBB0_5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 8: arm64 assembly corresponding to Listing 3, with my annotations in the comments. Compiled using arm64 Clang 10.0.0 using -O3. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:1,endLineNumber:12,positionColumn:1,positionLineNumber:12,selectionStartColumn:1,selectionStartLineNumber:12,startColumn:1,startLineNumber:12),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++float+oldval+%3D+f0.load()%3B%0A++++do+%7B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_weak(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:armv8-clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'armv8-a+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Moving the atomic load out of the loop in Listing 3 resulted in a single line change between Listing 5 and Listing 6’s x86-64 assembly, but causes the arm64 version to explode in size and radically change in structure between Listing 7 and Listing 8!
The key difference between Listing 7 and Listing 8 is that in Listing 8, the entire first iteration of the while loop is lifted out into it’s own code segment, which can then either directly return out of the function or go into the main body of the loop afterwards.
I initially thought that Clang’s decision to lift out the first iteration of the loop was surprising, but it turns out that GCC 10.3 and MSVC v19.28’s respective arm64 backends also similarly decide to lift the first iteration of the loop out as well.
The need to lift the entire first iteration out of the loop likely comes from the need to use an &lt;code class="language-plaintext highlighter-rouge"&gt;ldaxr&lt;/code&gt; instruction to carry out the initial atomic load of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt;.
Compared with GCC 10.3 and MSVC v19.28 though, Clang 10.0.0’s arm64 output does seem to do a bit more jumping around (see &lt;code class="language-plaintext highlighter-rouge"&gt;.LBB0_4&lt;/code&gt; through &lt;code class="language-plaintext highlighter-rouge"&gt;.LBBO_7&lt;/code&gt;) though.
Also, admittedly I’m not entirely sure why register &lt;code class="language-plaintext highlighter-rouge"&gt;w9&lt;/code&gt; gets set to 1 and then immediately compared with 0 in lines 16/17 and lines 47/49; maybe that’s just a convenient way to clear the &lt;code class="language-plaintext highlighter-rouge"&gt;z&lt;/code&gt; bit of the &lt;code class="language-plaintext highlighter-rouge"&gt;CPSR&lt;/code&gt; (Current Program Status Register; this is analogous to &lt;code class="language-plaintext highlighter-rouge"&gt;EFLAG&lt;/code&gt; on x86-64)?
But anyhow, compared with Listing 7, the arm64 assembly in Listing 8 is much longer in terms of code length, but actually is only slightly more inefficient in terms of total instructions executed.
The slight additional inefficiency comes from some of the additional setup work needed to manage all of the jumping and the split loop.
However, the fact that Listing 8 is less efficient compared with Listing 7 is interesting when we compare with what Listing 3 does to the x86-64 assembly; in the case of x86-64, pulling the initial atomic load out of the loop makes the output x86-64 assembly slightly &lt;em&gt;more&lt;/em&gt; efficient, as opposed to slightly &lt;em&gt;less&lt;/em&gt; efficient as we have here with arm64.&lt;/p&gt;

&lt;p&gt;As a very loose general rule of thumb, arm64 assembly tends to be longer than the equivalent x86-64 assembly for the same high-level code because CISC architectures simply tend to encode a lot more &lt;em&gt;stuff&lt;/em&gt; per instruction compared with RISC architectures &lt;a href="https://doi.org/10.1109/ICCD.2009.5413117"&gt;[Weaver and McKee 2009]&lt;/a&gt;.
However, compiled x86-64 binaries having fewer instructions doesn’t actually mean x86-64 binaries necessarily runs faster than equivalent, less “instruction-dense” compiled arm64 binary.
x86-64 instructions are variable length, requiring more complex logic in the processor’s &lt;a href="https://en.wikibooks.org/wiki/Microprocessor_Design/Instruction_Decoder"&gt;instruction decoder&lt;/a&gt;, and also since x86-64 instructions are more complex, they can take many more cycles per instruction to execute.
Contrast with arm64, in which instructions are fixed length.
Generally RISC architectures usually feature fixed length instructions, although this generalization isn’t a hard rule; the &lt;a href="https://en.wikipedia.org/wiki/SuperH"&gt;SuperH&lt;/a&gt; architecture (famously used in the Sega Saturn and Sega Dreamcast) is notably a RISC architecture with variable length instructions.
Fixed length instructions allow for arm64 chips to have simpler logic in decoding, and arm64 also tends to take many many fewer instructions per cycle (often, but not always, as low as one or two cycles per instruction).
The end result is that even though compiled arm64 binaries have lower instruction-density than compiled x86-64 binaries, arm64 processors tend to be able to retire more instructions per cycle than comparable x86-64 processors, allowing arm64 as an architecture to make up for the difference in code density.&lt;/p&gt;

&lt;p&gt;…except, of course, all of the above is only loosely true today!
While the x86-64 instruction set is still definitively a CISC instruction set today and the arm64 instruction set is still clearly a RISC instruction set today, a lot of the details have gotten fuzzier over time.
Processors today rarely directly implement the instruction set that they run; basically all modern x86-64 processors today feed x86-64 instructions into a huge hardware decoder block that breaks down individual x86-64 instructions into lower-level &lt;a href="https://en.m.wikipedia.org/wiki/Micro-operation"&gt;micro-operations, or μops&lt;/a&gt;.
Compared with older x86 processors from decades ago that directly implemented x86, these modern micro-operation-based x86-64 implementations are often much more RISC-like internally.
In fact, if you were to examine all of the parts of a modern Intel and AMD x86-64 processor that take place after the instruction decoding phase, without knowing what processor you were looking at beforehand, you likely would not be able to determine if the processor implemented a CISC or a RISC ISA &lt;a href="https://www.researchgate.net/publication/235960679_The_Architecture_of_the_Nehalem_Processor_and_Nehalem-EP_SMP_Platforms"&gt;[Thomadakis 2011]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The same is true going the other way; while modern x86-64 is a CISC architecture that in practical implementation is often more RISC-like, modern arm64 is a RISC architecture that sometimes has surprisingly CISC-like elements if you look closely.
Modern arm64 processors often &lt;em&gt;also&lt;/em&gt; decode individual instructions into smaller micro-operations &lt;a href="https://developer.arm.com/documentation/uan0015/b/"&gt;[ARM 2016]&lt;/a&gt;, although the extent to which modern arm64 processors do this is a lot less intensive than what modern x86-64 does &lt;a href="https://superuser.com/a/934755"&gt;[Castellano 2015]&lt;/a&gt;.
Modern arm64 instruction decoders usually rely on simple &lt;a href="https://en.wikipedia.org/wiki/Control_unit#Hardwired_control_unit"&gt;hardwired control&lt;/a&gt; to break instructions down into micro-operations, whereas modern x86-64 must use a &lt;a href="https://en.wikipedia.org/wiki/Microcode"&gt;programmable ROM containing advanced microcode&lt;/a&gt; to store mappings from x86-64 instructions to micro-instructions.&lt;/p&gt;

&lt;p&gt;Another way that arm64 has slowly gained some CISC-like characteristics is that arm64 over time has gained some surprisingly specialized complex instructions!
Remember the important note I made earlier about Listing 7 and Listing 8 being generated specifically for the ARMv8.0-A revision of arm64?
Well, the specific &lt;code class="language-plaintext highlighter-rouge"&gt;ldaxr&lt;/code&gt;/&lt;code class="language-plaintext highlighter-rouge"&gt;stlxr&lt;/code&gt; combination in Listings 6 and 7 that is needed to implement an atomic compare-and-exchange (and generally any kind of atomic load-and-conditional-store operation) is a specific area where a more complex single-instruction implementation generally can perform better than an implementation using several instructions.
As discussed earlier, one complex instruction is not necessarily always faster than several simpler instructions due to how the instructions actually have to be decoded and executed, but in this case, one atomic instruction allows for a faster implementation than several instructions combined since a single atomic instruction can take advantage of more available information at once &lt;a href="https://cpufun.substack.com/p/atomics-in-aarch64"&gt;[Cownie 2021]&lt;/a&gt;.
Accordingly, the &lt;a href="https://developer.arm.com/documentation/ddi0557/"&gt;ARMv8.1-A revision&lt;/a&gt; of arm64 introduces a collection of new single-instruction atomic operations.
Of interest to our particular example here is the new &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; instruction, which performs a compare-and-exchange to memory with acquire and release semantics; this new instruction is a direct analog to the x86_64 &lt;code class="language-plaintext highlighter-rouge"&gt;cmpxchg&lt;/code&gt; instruction with the &lt;code class="language-plaintext highlighter-rouge"&gt;lock&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;We can actually use these new ARMv8.1-A single-instruction atomic operations today; while GCC and Clang both target ARMv8.0-A by default today, ARMv8.1-A support can be enabled using the &lt;code class="language-plaintext highlighter-rouge"&gt;-march=armv8.1-a&lt;/code&gt; flag starting in GCC 10.1 and starting in Clang 9.0.0.
Actually, Clang’s support might go back even earlier; Clang 9.0.0 was the furthest back I was able to test.
Here’s what Listing 2 compiles to using the &lt;code class="language-plaintext highlighter-rouge"&gt;-march=armv8.1-a&lt;/code&gt; flag to enable the &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; instruction:&lt;/p&gt;

&lt;div id="listing9"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):
.LBB0_1:
        ldar    w8, [x0]             // w8 = *arg0 = f0, non-atomically loaded
        fmov    s1, w8               // s1 = w8 = f0
        fadd    s2, s1, s0           // s2 = s1 + s0 = (f0 + f1)
        mov     w9, w8               // w9 = w8 = f0
        fmov    w10, s2              // w10 = s2 = (f0 + f1)
        casal   w9, w10, [x0]        // atomically read the contents of the address stored
                                     //    in x0 (*arg0 = f0) and compare with w9;
                                     //    if [x0] == w9:
                                     //       atomically set the contents of the
                                     //       [x0] to the value in w10
                                     //    else:
                                     //       w9 = value loaded from [x0]
        cmp     w9, w8               // compare w9 and w8 and store result in N
        cset    w8, eq               // if previous instruction's compare was true,
                                     //    set w8 = 1
        cmp     w8, #1               // compare if w8 == 1 and store result in N
        b.ne    .LBB0_1              // if N==0 { goto .LBB0_1 }
        mov     v0.16b, v1.16b       // return f0 value from ldar
        ret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 9: arm64 revision ARMv8.1-A assembly corresponding to Listing 2, with my annotations in the comments. Compiled using arm64 Clang 10.0.0 using -O3 and also -march=armv8.1-a. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:34,endLineNumber:5,positionColumn:34,positionLineNumber:5,selectionStartColumn:34,selectionStartLineNumber:5,startColumn:34,startLineNumber:5),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++do+%7B%0A++++++++float+oldval+%3D+f0.load()%3B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_weak(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:armv8-clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3+-march%3Darmv8.1-a',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'armv8-a+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;If we compare Listing 9 with the ARMv8.0-A version in Listing 7, we can see that Listing 9 is only slightly shorted in terms of total instructions used, but the need for separate &lt;code class="language-plaintext highlighter-rouge"&gt;ldaxr&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;stlxr&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;clrex&lt;/code&gt; instructions has been completely replaced with a single &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; instruction.
Interestingly, Listing 9 is now structurally very very similar to it’s x86-64 counterpart in Listing 5.
My guess is that if someone was familiar with x86-64 assembly but had never seen arm64 assembly before, and that person was given Listing 5 and Listing 9 to compare side-by-side, they’d be able to figure out almost immediately what each line in Listing 9 does.&lt;/p&gt;

&lt;p&gt;Now let’s see what Listing 3 compiles to using the &lt;code class="language-plaintext highlighter-rouge"&gt;-march=armv8.1-a&lt;/code&gt; flag:&lt;/p&gt;

&lt;div id="listing10"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):
        ldar    w9, [x0]             // w9 = *arg0 = f0, non-atomically loaded
        fmov    s1, w9               // s1 = w9 = f0
        fadd    s2, s1, s0           // s2 = s1 + s0 = (f0 + f1)
        mov     w8, w9               // w8 = w9 = f0
        fmov    w10, s2              // w10 = s2 = (f0 + f1)
        casal   w8, w10, [x0]        // atomically read the contents of the address stored
                                     //    in x0 (*arg0 = f0) and compare with w8;
                                     //    if [x0] == w8:
                                     //       atomically set the contents of the
                                     //       [x0] to the value in w10
                                     //    else:
                                     //       w8 = value loaded from [x0]
        cmp     w8, w9               // compare w8 and w9 and store result in N
        b.eq    .LBB0_3              // if N==1 { goto .LBB0_3 }
        mov     w9, w8
.LBB0_2:
        fmov    s1, w8               // s1 = w8 = value previously loaded from [x0] = f0
        fadd    s2, s1, s0           // s2 = s1 + s0 = (f0 + f1)
        fmov    w10, s2              // w10 = s2 = (f0 + f1)
        casal   w9, w10, [x0]        // atomically read the contents of the address stored
                                     //    in x0 (*arg0 = f0) and compare with w9;
                                     //    if [x0] == w9:
                                     //       atomically set the contents of the
                                     //       [x0] to the value in w10
                                     //    else:
                                     //       w9 = value loaded from [x0]
        cmp     w9, w8               // compare w9 and w8 and store result in N
        cset    w8, eq               // if previous instruction's compare was true,
                                     //    set w8 = 1
        cmp     w8, #1               // compare if w8 == 1 and store result in N
        mov     w8, w9               // w8 = w9 = value previously loaded from [x0] = f0
        b.ne    .LBB0_2              // if N==0 { goto .LBB0_2 }
.LBB0_3:
        mov     v0.16b, v1.16b       // return f0 value from ldar
        ret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 10: arm64 revision ARMv8.1-A assembly corresponding to Listing 3, with my annotations in the comments. Compiled using arm64 Clang 10.0.0 using -O3 and also -march=armv8.1-a. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:30,endLineNumber:4,positionColumn:30,positionLineNumber:4,selectionStartColumn:30,selectionStartLineNumber:4,startColumn:30,startLineNumber:4),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++float+oldval+%3D+f0.load()%3B%0A++++do+%7B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_weak(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:armv8-clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3+-march%3Darmv8.1-a',selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'armv8-a+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;Here, the availability of the &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; instruction makes a huge difference in the compactness of the output assembly!
Listing 10 is nearly half the length of Listing 8, and more importantly, Listing 10 is also structurally much simpler than Listing 8.
In Listing 10, the compiler still decided to unroll the first iteration of the loop, but the amount of setup and jumping around in between iterations of the loop is significantly reduced, which should make Listing 10 a bit more performant than Listing 8 even before we take into account the performance improvements from using &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By the way, remember our discussion of weak versus strong memory models in the previous section?
As you may have noticed, Takua’s implementation of &lt;code class="language-plaintext highlighter-rouge"&gt;addAtomicFloat()&lt;/code&gt; uses &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak()&lt;/code&gt; instead of &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_strong()&lt;/code&gt;.
The difference between the weak and strong versions of &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_*()&lt;/code&gt; is that the weak version is allowed to sometimes report a failed comparison even if the values are actually equal (that is, the weak version is allowed to spuriously report a false negative), while the strong version guarantees always accurately reporting the outcome of the comparison.
On x86-64, there is no difference between using the weak and strong versions of because x86-64 always provides strong memory ordering (in other words, on x86-64 the weak version is allowed to report a false negative by the spec but never actually does).
However, on arm64, the weak version actually does report false negatives in practice.
The reason I chose to use the weak version is because when the compare-and-exchange is attempted repeatedly in a loop, if the underlying processor actually has weak memory ordering, using the weak version is usually faster than the strong version.
To see why, let’s take a look at the arm64 ARMv8.0-A assembly corresponding to Listing 2, but with &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_strong()&lt;/code&gt; swapped in instead of &lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak()&lt;/code&gt;:&lt;/p&gt;

&lt;div id="listing11"&gt;&lt;/div&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;addAtomicFloat(std::atomic&amp;lt;float&amp;gt;&amp;amp;, float):
.LBB0_1:
        ldar    w8, [x0]       // w8 = *arg0 = f0, non-atomically loaded
        fmov    s1, w8         // s1 = w8 = f0
        fadd    s2, s1, s0     // s2 = s1 + s0 = (f0 + f1)
        fmov    w9, s2         // w9 = s2 = (f0 + f1)
.LBB0_2:
        ldaxr   w10, [x0]      // w10 = *arg0 = f0.load(), atomically
                               //    loaded (get exclusive lock on x0), with
                               //    implicit synchronization
        cmp     w10, w8        // compare non-atomically loaded f0 with atomically
                               //    loaded f0 and store result in N
        b.ne    .LBB0_4        // if N==0 { goto .LBB0_4 }
        stlxr   w10, w9, [x0]  // if this thread has the exclusive lock,
                               //    { *arg0 = w9 = (f0 + f1), release lock },
                               //    store whether or not succeeded in w10
        cbnz    w10, .LBB0_2   // if w10 says exclusive lock failed { goto .LBB0_2}
        b       .LBB0_5        // goto .LBB0_5
.LBB0_4:
        clrex                  // clear this thread's record of exclusive lock
        b       .LBB0_1        // goto .LBB0_1
.LBB0_5:
        mov     v0.16b, v1.16b // return f0 value from ldaxr
        ret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class="codecaption"&gt;&lt;span&gt;Listing 11: arm64 revision ARMv8.0-A assembly corresponding to Listing 2 but using std::atomic::compare_exchange_strong() instead of std::atomic::compare_exchange_weak(), with my annotations in the comments. Compiled using arm64 Clang 10.0.0 using -O3 and also -march=armv8.1-a. &lt;a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:39,endLineNumber:7,positionColumn:39,positionLineNumber:7,selectionStartColumn:39,selectionStartLineNumber:7,startColumn:39,startLineNumber:7),source:'%23include+%3Catomic%3E%0A%0Afloat+addAtomicFloat(std::atomic%3Cfloat%3E%26+f0,+const+float+f1)+%7B%0A++++do+%7B%0A++++++++float+oldval+%3D+f0.load()%3B%0A++++++++float+newval+%3D+oldval+%2B+f1%3B%0A++++++++if+(f0.compare_exchange_strong(oldval,+newval))+%7B%0A++++++++++++return+oldval%3B%0A++++++++%7D%0A++++%7D+while+(true)%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50.32967032967033,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:armv8-clang1000,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O3+',selection:(endColumn:12,endLineNumber:19,positionColumn:12,positionLineNumber:19,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'armv8-a+clang+10.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:49.67032967032967,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4"&gt;See on Godbolt Compiler Explorer&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;If we compare Listing 11 with Listing 7, we can see that just changing the compare and exchange to a strong version instead of a weak version causes a major restructuring of the arm64 assembly and the addition of a bunch more jumps.
In Listing 7, loads from &lt;code class="language-plaintext highlighter-rouge"&gt;[x0]&lt;/code&gt; (corresponding to reads of &lt;code class="language-plaintext highlighter-rouge"&gt;f0&lt;/code&gt; in the C++ code) happen together at the top of the loop and the loaded values are reused through the rest of the loop.
However, Listing 11 is restructured such that loads from &lt;code class="language-plaintext highlighter-rouge"&gt;[x0]&lt;/code&gt; happen immediately before the instruction that uses the loaded value from &lt;code class="language-plaintext highlighter-rouge"&gt;[x0]&lt;/code&gt; to do a comparison or other operation.
This change means that there is less time for another thread to change the value at &lt;code class="language-plaintext highlighter-rouge"&gt;[x0]&lt;/code&gt; while this thread is still doing stuff.
Interestingly, if we compile using ARMv8.1-A, the availability of single-instruction atomic operations means that just like on x86-64, the difference between the strong and weak versions of the compare and exchange go away and end up compiling to the same arm64 assembly.&lt;/p&gt;

&lt;p&gt;At this point in process of porting Takua to arm64, I only had a couple of Raspberry Pis, as Apple Silicon Macs hadn’t even been announced yet.
Unfortunately, the Raspberry Pi 3B’s Cortex-A53-based CPU and the Raspberry Pi 4B’s Cortex-A72-based CPU only implement ARMv8.0-A, which means I couldn’t actually test and compare the versions of the compiled assembly with and without &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt;.
Fortunately though, we can still compile the code such that if the processor the code is running on implements ARMv8.1-A, the code will use &lt;code class="language-plaintext highlighter-rouge"&gt;casal&lt;/code&gt; and other ARMv8.1-A single-instruction atomic operations, and otherwise if only ARMv8.0-A is implemented, then the code will fall back to using &lt;code class="language-plaintext highlighter-rouge"&gt;ldaxr&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;stlxr&lt;/code&gt;, and &lt;code class="language-plaintext highlighter-rouge"&gt;clrex&lt;/code&gt;.
We can get the compiler to automatically do the above by using the &lt;code class="language-plaintext highlighter-rouge"&gt;-moutline-atomics&lt;/code&gt; compiler flag, which Richard Henderson of Linaro contributed into GCC 10.1 &lt;a href="https://community.arm.com/developer/tools-software/tools/b/tools-software-ides-blog/posts/making-the-most-of-the-arm-architecture-in-gcc-10"&gt;[Tkachov 2020]&lt;/a&gt; and which also recently was added to Clang 12.0.0 in April 2021.
The &lt;code class="language-plaintext highlighter-rouge"&gt;-moutline-atomics&lt;/code&gt; flag tells the compiler to generate a runtime helper function and stub the runtime helper function into the atomic operation call-site instead of directly generating atomic instructions; this helper function then does a runtime check for what atomic instructions are available on the current processor and dispatches to the best possible implementation given the available instructions.
This runtime check is cached to make subsequent calls to the helper function faster.
Using this flag means that if a future Raspberry Pi 5 or something comes out hopefully with support for something newer than ARMv8.0-A, Takua should be able to automatically take advantage of faster single-instruction atomics without me having to reconfigure Takua’s builds per processor.&lt;/p&gt;

&lt;div id="2021-05-29-performance-testing"&gt;&lt;/div&gt;
&lt;h2 id="performance-testing"&gt;Performance Testing&lt;/h2&gt;

&lt;p&gt;So, now that I have Takua up and running on arm64 on Linux, how does it actually perform?
Here are some comparisons, although there are some important caveats.
First, at this stage in the porting process, the only arm64 hardware I had that could actually run reasonably sized scenes on was a Raspberry Pi 4B with 4 GB of memory.
The Raspberry Pi 4B’s CPU is a Broadcom BCM2711, which has 4 Cortex-A72 cores; these cores aren’t exactly fast, and even though the Raspberry Pi 4B came out in 2019, the Cortex-A72 core actually dates back to 2015.
So, for the x86-64 comparison point, I’m using my early 2015 MacBook Air, which also has only 4 GB of memory and has an Intel Core i5-5250U CPU with 2 cores / 4 threads.
Also, as an extremely unfair comparison point, I also ran the comparisons on my workstation, which has 128 GB of memory and dual Intel Xeon E5-2680 CPUs with 8 cores / 16 threads each, for 16 cores / 32 threads in total.
The three scenes I used were the Cornell Box seen in Figure 1, the glass teacup seen in Figure 2, and the bedroom scene from my &lt;a href="http://blog.yiningkarlli.com/2020/02/shadow-terminator-in-takua.html"&gt;shadow terminator blog post&lt;/a&gt;; these scenes were chosen because they fit in under 4 GB of memory.
All scenes were rendered to 16 samples-per-pixel, because I didn’t want to wait forever.
The Cornell Box and Bedroom scenes are rendered using unidirectional path tracing, while the tea cup scene is rendered using VCM.
The Cornell Box scene is rendered at 1024x1024 resolution, while the Tea Cup and Bedroom scenes are rendered at 1920x1080 resolution.&lt;/p&gt;

&lt;p&gt;Here are the results:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CORNELL BOX&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1024x1024, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;440.627 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1762.51 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;272.053 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1088.21 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;36.6183 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 1139.79 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;TEA CUP&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, VCM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2205.072 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 8820.32 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;2237.136 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 8948.56 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;174.872 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 5593.60 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;BEDROOM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Wall Time:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Core-Seconds:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;5653.66 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 22614.64 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4900.54 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 19602.18 s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;310.35 s&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;approx 9931.52 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;In the results above, “wall time” refers to how long the render took to complete in real-world time as if measured by a clock on the wall, while “core-seconds” is a measure of how long the render would have taken completely single-threaded.
Both values are separately tracked by the renderer; “wall time” is just a timer that starts when the renderer begins working on its first sample and stops when the very last sample is finished, while “core-seconds” is tracked by using a separate timer per thread and adding up how much time each thread has spent rendering.&lt;/p&gt;

&lt;p&gt;The results are interesting!
The Raspberry Pi 4B and 2015 MacBook Air are both just completely outclassed by the dual-Xeon workstation in absolute wall time, but that should come as a surprise to absolutely nobody.
What’s more surprising is that the multiplier by which the dual-Xeon workstation is faster than the Raspberry Pi 4B in wall time is much higher than the multiplier in core-seconds.
For the Cornell Box scene, the dual-Xeon is 12.033x faster than the Raspberry Pi 4B in wall time, but is only 1.546x faster in core-seconds.
For the Tea Cup scene, the dual-Xeon is 12.61x faster than the Raspberry Pi 4B in wall time, but is only 1.577x faster in core-seconds.
For the Bedroom scene, the dual-Xeon is 18.217x faster than the Raspberry Pi 4B in wall time, but is only 2.277x faster in core-seconds.
This difference in wall time multiplier versus core-seconds multiplier indicates that the Raspberry Pi 4B and dual-Xeon workstation are shockingly close in &lt;em&gt;single-threaded&lt;/em&gt; performance; the dual-Xeon workstation only has such a crushing lead in wall clock time because it just has way more cores and threads available than the Raspberry Pi 4B.&lt;/p&gt;

&lt;p&gt;When we compare the Raspberry Pi 4B to the 2015 MacBook Air, the results are even more interesting.
Between these two machines, the times are actually relatively close; for the Cornell Box and Bedroom scenes, the Raspberry Pi 4B is within striking distance of the 2015 MacBook Air, and for the Tea Cup scene, the Raspberry Pi 4B is &lt;em&gt;actually faster&lt;/em&gt; than the 2015 MacBook Air.
The reason the Raspberry Pi 4B is likely faster than the 2014 MacBook Air at the Tea Cup scene is likely because the Tea Cup scene was rendered using VCM; VCM requires the construction of a photon map, and from previous profiling I know that Takua’s photon map builder works better with more actual physical cores.
The Raspberry Pi 4B has four physical cores, whereas the 2014 MacBook Air only has two physical cores and gets to four threads using hyperthreading; my photon map builder doesn’t scale well with hyperthreading.&lt;/p&gt;

&lt;p&gt;So, overall, the Raspberry Pi 4B’s arm64 processor intended for phones got handily beat by a dual-Xeon workstation but came very close to a 2015 MacBook Air.
The thing here to remember though, is that the Raspberry Pi 4B’s arm64-based processor has a TDP of just 4 watts!
Contrast with the MacBook Air’s Intel Core i5-5250U, which has a 15 watt TDP, and with the dual Xeon E5-2680 in my workstation, which have a 130 watt TDP each for a combined &lt;em&gt;260 watt TDP&lt;/em&gt;.
For this comparison, I think using the max TDP of each processor is a relatively fair thing to do, since Takua Renderer pushes each CPU to 100% utilization for sustained periods of time.
So, the real story here from an energy perspective is that the Raspberry Pi 4B was between 12 to 18 times slower than the dual-Xeon workstation, but the Raspberry Pi 4B also has a TDP that is &lt;em&gt;65x lower&lt;/em&gt; than the dual-Xeon workstation.
Similarly, the Raspberry Pi 4B nearly matches the 2015 MacBook Air, but with a TDP that is 3.75x lower!&lt;/p&gt;

&lt;p&gt;When factoring in energy utilization, the numbers get even more interesting once we look at total energy used across the whole render.
We can get the total energy used for each render by multiplying the wall clock render time with the TDP of each processor (again, we’re assuming 100% processor utilization during each render); this gives us total energy used in watt-seconds, which we divide by 3600 seconds per hour to get watt-hours:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;CORNELL BOX&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1024x1024, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;0.4895 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;1.1336 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;2.6450 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;TEA CUP&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, VCM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;2.4500 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;9.3214 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;12.6297 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;BEDROOM&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt; &lt;/th&gt;
      &lt;th style="text-align: center;"&gt;1920x1080, PT&lt;/th&gt;
      &lt;th style="text-align: left;"&gt; &lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th style="text-align: right;"&gt;Processor:&lt;/th&gt;
      &lt;th style="text-align: center;"&gt;Max TDP:&lt;/th&gt;
      &lt;th style="text-align: left;"&gt;Total Energy Used:&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Broadcom BCM2711:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;4 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;6.2819 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Core i5-5250U:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;15 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;20.4189 Wh&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: right;"&gt;Intel Xeon E5-2680 x2:&lt;/td&gt;
      &lt;td style="text-align: center;"&gt;260 W&lt;/td&gt;
      &lt;td style="text-align: left;"&gt;22.4142 Wh&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;From the numbers above, we can see that even though the Raspberry Pi 4B is a lot slower than the dual-Xeon workstation in wall clock time, the Raspberry Pi 4B absolutely crushes both the 2015 MacBook Air and the dual-Xeon workstation in terms of energy efficiency.
To render the same image, the Raspberry Pi 4B used between approximately 3.5x to 5.5x &lt;em&gt;less&lt;/em&gt; energy overall than the dual-Xeon workstation, and used between approximately 2.3x to 3.8x less energy than the 2015 MacBook Air.
It’s also worth noting that the 2015 MacBook Air cost &lt;span&gt;$&lt;/span&gt;899 when it first launched (and the processor had a recommended price from Intel of &lt;span&gt;$&lt;/span&gt;315), and the dual-Xeon workstation cost… I don’t actually know.
I bought the dual-Xeon workstation used for a pittance when my employer retired it, so I don’t know how much it actually cost new.
But, I do know that the processors in the dual-Xeon had a recommended price from Intel of &lt;span&gt;$&lt;/span&gt;1723… &lt;em&gt;each&lt;/em&gt;, for a total of &lt;span&gt;$&lt;/span&gt;3446 when they were new.
In comparison, the Raspberry Pi 4B with 4 GB of RAM costs about &lt;span&gt;$&lt;/span&gt;55 for the entire computer, and the processor cost… well, the actual price for most ARM processors is not ever publicly disclosed, but since a baseline Raspberry Pi 4B costs only &lt;span&gt;$&lt;/span&gt;35, the processor can’t have cost more than a few dollars at most, possibly even under a dollar.&lt;/p&gt;

&lt;p&gt;I think the main takeaway from these performance comparisons is that even back with 2015 technology, even though most arm64 processors were slower in absolute terms compared to their x86-64 counterparts, the single-threaded performance was already shockingly close, and arm64 energy usage per compute unit and price already were leaving x86-64 in the dust.
Fast forward to the present day in 2021, where we have seen Apple’s arm64-based M1 chip take the absolute performance crown in its category from all x86-64 competitors, at both a lower energy utilization level and a lower price.
The even wilder thing is: the M1 is likely the slowest desktop arm64 chip that Apple will ever ship, and arm64 processors from NVIDIA and Samsung and Qualcomm and Broadcom won’t be far behind in the consumer space while Amazon and Ampere and other companies are also introducing enormous, extremely powerful arm64 chips in the high end server space.
Intel and (especially) AMD aren’t sitting still in the x86-64 space either though.
The next few years are going to be very interesting; no matter what happens, on x86-64 or on arm64, Takua Renderer is now ready to be there!&lt;/p&gt;

&lt;div id="2021-05-29-conclusion-to-part-1"&gt;&lt;/div&gt;
&lt;h2 id="conclusion-to-part-1"&gt;Conclusion to Part 1&lt;/h2&gt;

&lt;p&gt;Through the process of porting to arm64 on Linux, I learned a lot about the arm64 architecture and how it differs from x86-64, and I also found a couple of good reminders about topics like memory ordering and how floating point works.
Originally I thought that my post on porting Takua to arm64 would be a nice, short, and fast to write, but instead here we are some 17,000 words later and I have not even gotten to porting Takua to arm64 on macOS and Apple Silicon yet!
So, I think we will stop here for now and save the rest for an upcoming Part 2.
In Part 2, I’ll write about the process to port to arm64, about how to create Universal Binaries, and examine Apple’s Rosetta 2 system for running x86-64 binaries on arm64.
Also, in Part 2 we’ll examine how Embree works on arm64 and compare arm64’s NEON vector extensions with x86-64’s SSE vector extensions, and we’ll finish with some additional miscellaneous differences between x86-64 and arm64 that need to be considered when writing C++ code for both architectures.&lt;/p&gt;

&lt;div id="2021-05-29-acknowledgements"&gt;&lt;/div&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;

&lt;p&gt;Thanks so much to &lt;a href="http://rgba32.blogspot.com"&gt;Mark Lee&lt;/a&gt; and &lt;a href="http://rendering-memo.blogspot.com"&gt;Wei-Feng Wayne Huang&lt;/a&gt; for puzzling through some of the &lt;code class="language-plaintext highlighter-rouge"&gt;std::compare_exchange_weak()&lt;/code&gt; stuff with me.
Thanks a ton to &lt;a href="https://twitter.com/superfunc"&gt;Josh Filstrup&lt;/a&gt; for proofreading and giving feedback and suggestions on this post pre-release!
Josh was the one who told me about the &lt;a href="https://herbie.uwplse.org"&gt;Herbie&lt;/a&gt; tool mentioned in the floating point section, and he made an interesting suggestion about using &lt;a href="https://egraphs-good.github.io"&gt;e-graph analysis&lt;/a&gt; to better understand floating point behavior.
Also Josh pointed out SuperH as an example of a variable width RISC architecture, which of course he would because he knows all there is to know about the Sega Dreamcast.
Finally, thanks to my wife, &lt;a href="http://harmonymli.com"&gt;Harmony Li&lt;/a&gt;, for being patient with me while I wrote up this monster of a blog post and for also puzzling through some of the technical details with me.&lt;/p&gt;

&lt;div id="2021-05-29-references"&gt;&lt;/div&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;

&lt;p&gt;Pontus Andersson, Jim Nilsson, Tomas Akenine-Möller, Magnus Oskarsson, Kalle Åström, and Mark D. Fairchild. 2020. &lt;a href="https://doi.org/10.1145/3406183"&gt;FLIP: A Difference Evaluator for Alternating Images&lt;/a&gt;. &lt;em&gt;Proc. of the ACM on Computer Graphics and Interactive Techniques (Proc. of High Performance Graphics)&lt;/em&gt; 3, 2 (Jul. 2020), Article 15.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2016. &lt;a href="https://developer.arm.com/documentation/uan0015/b/"&gt;Cortex-A57 Software Optimization Guide&lt;/a&gt;. Retrieved May 12, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2021. &lt;a href="https://developer.arm.com/documentation/ddi0487/ga"&gt;Arm Architecture Reference Manual Armv8, for Armv8-A Architecture Profile, Version G.a&lt;/a&gt;. Retrieved May 14, 2021.&lt;/p&gt;

&lt;p&gt;ARM Holdings. 2021. &lt;a href="https://developer.arm.com/documentation/ddi0557/latest/"&gt;Arm Architecture Reference Manual Supplement ARMv8.1, for ARMv8-A Architecture Profile, Version: A.b&lt;/a&gt;. Retrieved May 14, 2021.&lt;/p&gt;

&lt;p&gt;Brandon Castellano. 2015. &lt;a href="https://superuser.com/a/934755"&gt;SuperUser Answer to “Do ARM Processors like Cortex-A9 Use Microcode?”&lt;/a&gt;. Retrieved May 12, 2021.&lt;/p&gt;

&lt;p&gt;Jim Cownie. 2021. &lt;a href="https://cpufun.substack.com/p/atomics-in-aarch64"&gt;Atomics in AArch64&lt;/a&gt;. In &lt;em&gt;CPU Fun&lt;/em&gt;. Retrieved May 14, 2021.&lt;/p&gt;

&lt;p&gt;CppReference. 2021. &lt;a href="https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;std::atomic&amp;lt;T&amp;gt;::compare_exchange_weak&lt;/code&gt;&lt;/a&gt;. Retrieved April 02, 2021.&lt;/p&gt;

&lt;p&gt;CppReference. 2021. &lt;a href="https://en.cppreference.com/w/cpp/atomic/memory_order"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;std::memory_order&lt;/code&gt;&lt;/a&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Intel Corporation. 2021. &lt;a href="https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4.html"&gt;Intel 64 and IA-32 Architectures Software Developer’s Manual&lt;/a&gt;.  Retrieved April 02, 2021.&lt;/p&gt;

&lt;p&gt;Bruce Dawson. 2020. &lt;a href="https://randomascii.wordpress.com/2020/11/29/arm-and-lock-free-programming/"&gt;ARM and Lock-Free Programming&lt;/a&gt;. In &lt;em&gt;Random ASCII&lt;/em&gt;. Retrieved April 15, 2021.&lt;/p&gt;

&lt;p&gt;Glenn Fiedler. 2008. &lt;a href="https://gafferongames.com/post/floating_point_determinism/"&gt;Floating Point Determinism&lt;/a&gt;. In &lt;em&gt;Gaffer on Games&lt;/em&gt;. Retrieved April 20, 2021.&lt;/p&gt;

&lt;p&gt;David Goldbery. 1991. &lt;a href="https://doi.org/10.1145/103162.103163"&gt;What Every Computer Scientist Should Know About Floating-Point Arithmetic&lt;/a&gt;. &lt;em&gt;ACM Computing Surveys&lt;/em&gt; 32, 1 (Mar. 1991), 5-48.&lt;/p&gt;

&lt;p&gt;Martin Geupel. 2018. &lt;a href="https://www.racoon-artworks.de/cgbasics/bucket_progressive.php"&gt;Bucket and Progressive Rendering&lt;/a&gt;. In &lt;em&gt;CG Basics&lt;/em&gt;. Retrieved May 12, 2021.&lt;/p&gt;

&lt;p&gt;Phillip Johnston. 2020. &lt;a href="https://embeddedartistry.com/blog/2017/10/11/demystifying-arm-floating-point-compiler-options/"&gt;Demystifying ARM Floating Point Compiler Options&lt;/a&gt;. In &lt;em&gt;Embedded Artistry&lt;/em&gt;. Retrieved April 20, 2021.&lt;/p&gt;

&lt;p&gt;Yossi Kreinin. 2008. &lt;a href="http://yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html"&gt;Consistency: How to Defeat the Purpose of IEEE Floating Point&lt;/a&gt;. In &lt;em&gt;Proper Fixation&lt;/em&gt;. Retrieved April 20, 2021.&lt;/p&gt;

&lt;p&gt;Günter Obiltschnig. 2006. &lt;a href="https://www.appinf.com/download/FPIssues.pdf"&gt;Cross-Platform Issues with Floating-Point Arithmetics in C++&lt;/a&gt;. In &lt;em&gt;ACCU Conference 2006&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;David A. Patterson and David R. Ditzel. 1980. &lt;a href="https://doi.org/10.1145/641914.641917"&gt;The Case for the Reduced Instruction Set Computer&lt;/a&gt;. &lt;em&gt;ACM SIGARCH Computer Architecture News&lt;/em&gt; 8, 6 (Oct. 1980), 25-33.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20120515/memory-reordering-caught-in-the-act/"&gt;Memory Reordering Caught in the Act&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20120612/an-introduction-to-lock-free-programming/"&gt;An Introduction to Lock-Free Programming&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20120625/memory-ordering-at-compile-time/"&gt;Memory Ordering at Compile Time&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/"&gt;Memory Barriers Are Like Source Control Operations&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20120913/acquire-and-release-semantics/"&gt;Acquire and Release Semantics&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20120930/weak-vs-strong-memory-models/"&gt;Weak vs. Strong Memory Models&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Jeff Preshing. 2012. &lt;a href="https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/"&gt;This Is Why They Call It a Weakly-Ordered CPU&lt;/a&gt;. In &lt;em&gt;Preshing on Programming&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;The Rust Team. 2021. &lt;a href="https://doc.rust-lang.org/nomicon/atomics.html"&gt;Atomics&lt;/a&gt;. In &lt;em&gt;The Rustonomicon&lt;/em&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Michael E. Thomadakis. 2011. &lt;a href="https://www.researchgate.net/publication/235960679_The_Architecture_of_the_Nehalem_Processor_and_Nehalem-EP_SMP_Platforms"&gt;The Architecture of the Nehalem Processor and Nehalem-EP SMP Platforms&lt;/a&gt;. JFE Technical Report. Texas A&amp;amp;M University.&lt;/p&gt;

&lt;p&gt;Kyrylo Tkachov. 2020. &lt;a href="https://community.arm.com/developer/tools-software/tools/b/tools-software-ides-blog/posts/making-the-most-of-the-arm-architecture-in-GCC-10"&gt;Making the Most of the Arm Architecture with GCC 10&lt;/a&gt;. In &lt;em&gt;ARM Tools, Software, and IDEs Blog&lt;/em&gt;. Retrieved May 14, 2021.&lt;/p&gt;

&lt;p&gt;Vincent M. Weaver and Sally A. McKee. 2009. &lt;a href="https://doi.org/10.1109/ICCD.2009.5413117"&gt;Code Density Concerns for New Architectures&lt;/a&gt;. In &lt;em&gt;Proc. of IEEE International Conference on Computer Design (ICCD 2009)&lt;/em&gt;. 459-464.&lt;/p&gt;

&lt;p&gt;WikiBooks. 2021. &lt;a href="https://en.wikibooks.org/wiki/Microprocessor_Design/Instruction_Decoder"&gt;Microprocessor Design: Instruction Decoder&lt;/a&gt;. Retrieved May 12, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Complex_instruction_set_computer"&gt;Complex Instruction Set Computer&lt;/a&gt;. Retrieved April 05, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/CPU_cache#Policies"&gt;CPU Cache&lt;/a&gt;. Retrieved March 20, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format"&gt;Extended Precision&lt;/a&gt;. Retrieved April 20, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Control_unit#Hardwired_control_unit"&gt;Hardwired Control Unit&lt;/a&gt;. Retrieved May 12, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/IEEE_754"&gt;IEEE 754&lt;/a&gt;. Retrieved April 20, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Intel_8087"&gt;Intel 8087&lt;/a&gt;. Retrieved April 20, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Microcode"&gt;Micro-Code&lt;/a&gt;. Retrieved May 12, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.m.wikipedia.org/wiki/Micro-operation"&gt;Micro-Operation&lt;/a&gt;. Retrieved May 10, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/Reduced_instruction_set_computer"&gt;Reduced Instruction Set Computer&lt;/a&gt;. Retrieved April 05, 2021.&lt;/p&gt;

&lt;p&gt;Wikipedia. 2021. &lt;a href="https://en.wikipedia.org/wiki/SuperH"&gt;SuperH&lt;/a&gt;. Retrieved June 02, 2021.&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Sat, 29 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/05/porting-takua-to-arm-pt1.html</guid></item><item><title>Debug woes 1: multiple substitutions on the same line</title><link>https://learnbyexample.github.io/mini/debug-woes-1/</link><description>&lt;p&gt;While answering &lt;a href="https://stackoverflow.com/q/67703405/4082052"&gt;this SO question&lt;/a&gt;, I ran into a debug misery. It took me an embarrassing amount of time and experiments to understand why.&lt;/p&gt;
&lt;p&gt;Here's a simplified version of the problem. Can you spot the issue?&lt;/p&gt;
&lt;pre class="language-ruby " style="background-color: #f5f5f5; color: #1f1f1f;"&gt;&lt;code class="language-ruby"&gt;&lt;span&gt;$ cat ip.txt
&lt;/span&gt;&lt;span&gt;a b c d
&lt;/span&gt;&lt;span&gt;i j k l
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# Change only first two occurrences of spaces with tabs
&lt;/span&gt;&lt;span&gt;$ perl &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt;pe &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'$c=2; s/ /\t/ while $c--'&lt;/span&gt;&lt;span&gt; ip.txt
&lt;/span&gt;&lt;span&gt;a       b       c d
&lt;/span&gt;&lt;span&gt;i       j       k l
&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# Wanted to generalize the solution to match one-or-more whitespaces
&lt;/span&gt;&lt;span style="color: #7f8989;"&gt;# But it doesn't work!!!
&lt;/span&gt;&lt;span&gt;$ perl &lt;/span&gt;&lt;span style="color: #72ab00;"&gt;-&lt;/span&gt;&lt;span&gt;pe &lt;/span&gt;&lt;span style="color: #d07711;"&gt;'$c=2; s/\s+/\t/ while $c--'&lt;/span&gt;&lt;span&gt; ip.txt
&lt;/span&gt;&lt;span&gt;a       b c d
&lt;/span&gt;&lt;span&gt;i       j k l
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;details&gt;Click to view answer
The substitution works from start of the line for every iteration of the while loop. Tab is one of the whitespace characters, so after the first substitution, the tab gets matched for rest of the iterations.
&lt;/details&gt;</description><author>learnbyexample</author><pubDate>Sat, 29 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/mini/debug-woes-1/</guid></item><item><title>Random digits and Benford's law</title><link>https://bytepawn.com/random-digits-and-benfords-law.html</link><description>&lt;p&gt;The post explores the distribution of digits of random and non-random numbers from receipts, verifying Benford's law of first digit distribution.&lt;br /&gt;&lt;br /&gt; &lt;img alt="Early stopping" src="/images/digits11.png" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 29 May 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/random-digits-and-benfords-law.html</guid></item><item><title>Digital discrimination in Uganda</title><link>https://stop.zona-m.net/2021/05/digital-discrimination-in-uganda/</link><description>&lt;p&gt;It is the same as in any other place. Or maybe not?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 28 May 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/digital-discrimination-in-uganda/</guid></item><item><title>Create a crypto Telegram bot in Python using Yahoo Finance API  A step-by-step guide creating a Telegram bot in Python.</title><link>https://blog.adnansiddiqi.me/create-crypto-telegram-bot-in-python-using-yahoo-finance-api/</link><description>&lt;p&gt;So I was exploring Telegram APIs for a project someone asked me to work on it. The script was actually a cron job that would be sending messages on daily basis. While working on it I found that you could come up with your own commands that can pull data from some remote API and display the results to Telegram users. I found this an opportunity for my next blog post which I am writing here 🙂 Telegram is very much similar to WhatsApp for communication and it is quite popular among Crypto lovers. Many Crypto traders use both Telegram and Discord to send crypto and stock signals to their group members. Telegram apps are called Bots. You create a bot and get an API/Token key used in your program to connect with Telegram servers.  You can either create a bot that keeps sending messages itself without any user intervention or the one that takes command as input and responds accordingly. I am going to make a later one. Creating a bot The very first thing you have to do is to send a /start message to the BotFather bot. This bot has a verified tick with it that separates it from other fake bots. When you send a /start command it sends you a big menu of commands to manage your bot. You will the /newbot command and it will ask you to follow a few steps to generate your bot. In the end, it will also send you a message like the below: Done! Congratulations on your new bot. You will find it at t.me/mybot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you&amp;#8217;ve finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this. Use this token to access the HTTP API: 2815346687:AAHZIr1IOcWSkEmg21-7Ilal4gH78jML3_4 Keep your token secure and store it safely, it can be used by anyone to control your bot. Save this token key somewhere because this is the only way you could interact with your bot. In order to make your bot available for invitation from groups, you will have to send /setjoingroups command. It will ask you to mention your bot name with @ sign. For instance, if my bot name is mybot then I will write @mybot and hit the ENTER key. It will show a dialog-like interface in which you will pick the Enable option. Now you can easily invite your new bot to the group Development OK, so your bot is ready. Now you will have to write the code that&amp;#8217;d automate this bot. In order to do that you will have to install the python library for telegram. The very first thing I will do is setting the TOKEN KEY in a separate file. You may use a text/xml/JSON, whatever you want. I just created config.py and set the token in TOKEN constant. Now I am going to create the main script file, called crypto.py which will serve as a bot controller. In the file import things like the below: from config import TOKEN from telegram.ext import Updater, CommandHandler USAGE = '/ticker &amp;#60;SYMBOL&amp;#62;: For example: /ticker BTC or /ticker ETH' def start(update, context): update.message.reply_text(USAGE) def main(): updater = Updater(TOKEN, use_context=True) dp = updater.dispatcher # on different commands - answer in Telegram dp.add_handler(CommandHandler("start", start)) # Start the Bot updater.start_polling() updater.idle() When you run this script it will run in a loop.  Go to your group where you added your bot and it sends the text set in the USAGE variable. Pretty straightforward. You create a dispatch which is an object of Updater class and then create CommandHandler to deal with your custom commands. The first parameter of add_handler is the command you want to send and the second is the function that will be invoked after receiving the command. Unknown commands are discarded by the script. start_polling  will start the script in a loop to accept commands from the bot. Try the /start command and it should print the text you set in USAGE variable. OK, so we have tested our bot, now it is time to write the real code to fetch Crypto prices. I am using Yahoo Finance APIs for that purpose since they are free to use and pretty comprehensive. I create another file, called yf.py which contains the following code: Pretty straightforward. Now I am calling this function by doing an import like the below: from yf import get_ticker_info I am going to create a new function called ticker_command which contains the following code: Now the main() function , ticker handler command will look like the below: That&amp;#8217;s it. If all goes well, this is how your bot will respond: Cool, No? Before I conclude, you should notice this very command: update.message.reply_text(msg, parse_mode='html', disable_web_page_preview=True) Here I set the message format to HTML. By default it is text. You may also use Markdown for text formatting. I also disabled the link preview  I am also sending the URL for the main source for verification of the coin price Conclusion So you learned how simple it is to write a basic Telegram bot in Python. You may make it more complex by logging things in a database or other stuff. The basic structure will be the same. The code is available on Github. PS: If you loved this post then you might show some love by supporting this blog. Your kind gesture will just help me to learn and share new stuff with awesome people like you. If you like this post then you should subscribe to my blog for future updates. * indicates required Email Address *&lt;/p&gt;
The post &lt;a href="https://blog.adnansiddiqi.me/create-crypto-telegram-bot-in-python-using-yahoo-finance-api/"&gt;Create a crypto Telegram bot in Python using Yahoo Finance API &lt;br /&gt; &lt;span style="color: grey; font-size: 25px; font-weight: 300;"&gt;A step-by-step guide creating a Telegram bot in Python.&lt;/span&gt;&lt;/a&gt; first appeared on &lt;a href="https://blog.adnansiddiqi.me"&gt;Adnan's Random bytes&lt;/a&gt;.</description><author>Adnan's Random bytes</author><pubDate>Fri, 28 May 2021 16:09:38 GMT</pubDate><guid isPermaLink="true">https://blog.adnansiddiqi.me/create-crypto-telegram-bot-in-python-using-yahoo-finance-api/</guid></item><item><title>TIL that there is a else-clause in a Python for-loop</title><link>https://www.zufallsheld.de/2021/05/28/til-that-there-is-a-else-clause-in-a-python-for-loop/</link><description>&lt;p&gt;In Python if you want to execute something after a for-loop has finished, you can use a&amp;nbsp;else-clause.&lt;/p&gt;
&lt;p&gt;This&amp;nbsp;code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for i in range(5):
    print(i)
else:
    print("Finished")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0
1
2
3
4
Finished
&lt;/code&gt;&lt;/pre&gt;</description><author>zufallsheld</author><pubDate>Fri, 28 May 2021 13:48:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/05/28/til-that-there-is-a-else-clause-in-a-python-for-loop/</guid></item><item><title>Three Plus Some Lovely Kafka Trends</title><link>https://www.morling.dev/blog/three-plus-some-lovely-kafka-trends/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_cambrian_explosion_of_connectors"&gt;Cambrian Explosion of Connectors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_democratization_of_data_pipelines"&gt;Democratization of Data Pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_stream_processing_for_everyone"&gt;Stream Processing for Everyone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_honorable_mentions"&gt;Honorable Mentions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Over the course of the last few months, I’ve had the pleasure to serve on the &lt;a href="https://www.kafka-summit.org/"&gt;Kafka Summit&lt;/a&gt; program committee and review several hundred session abstracts for the three Summits happening this year (Europe, APAC, Americas).
That’s not only a big honour, but also a unique opportunity to learn what excites people currently in the Kafka eco-system
(and yes, it’s a fair amount of work, too ;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;While voting on the proposals, and also generally aspiring to stay informed of what’s going on in the Kafka community at large, I noticed a few repeating themes and topics which I thought would be interesting to share
(without touching on any specific talks of course).
At first I meant to put this out via a Twitter thread, but then it became a bit too long for that, so I decided to write this quick blog post instead.
Here it goes!&lt;/p&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Fri, 28 May 2021 11:30:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/three-plus-some-lovely-kafka-trends/</guid></item><item><title>The Power of Personal Podcasting</title><link>https://www.swyx.io/personal-podcasting</link><description>Celebrating following 100 episodes of my mixtape, and reflecting on why this works</description><author>swyx's site RSS Feed</author><pubDate>Fri, 28 May 2021 05:09:23 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/personal-podcasting</guid></item><item><title>Emacs link scraping (2021 edition)</title><link>https://xenodium.com/emacs-link-scraping-2021-edition</link><description>&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-link-scraping-2021-edition/scrape.png" /&gt;&lt;/p&gt;
&lt;p&gt;A recent Hacker News post, &lt;a href="https://news.ycombinator.com/item?id=27302195"&gt;Ask HN: Favorite Blogs by Individuals&lt;/a&gt;, led me to dust off my oldie but trusty &lt;a href="https://github.com/xenodium/dotsies/blob/92ef8259f016cdd4f67caf0e520096f6da4f7a18/emacs/ar/ar-url.el#L42"&gt;command to extract comment links&lt;/a&gt;. I use it to dissect these wonderful references more effectively.&lt;/p&gt;
&lt;p&gt;You see, I wrote this command &lt;a href="https://xenodium.com/get-emacs-to-gather-links-in-posts/"&gt;back in 2015&lt;/a&gt;. We can likely revisit and improve. The &lt;a href="https://github.com/zweifisch/enlive"&gt;enlive&lt;/a&gt; package continues to do a fine job &lt;a href="https://github.com/zweifisch/enlive/blob/604a8ca272b6889f114e2b5a13adb5b1dc4bae86/enlive.el#L39"&gt;fetching&lt;/a&gt;, parsing, and &lt;a href="https://github.com/zweifisch/enlive/blob/604a8ca272b6889f114e2b5a13adb5b1dc4bae86/enlive.el#L142"&gt;querying&lt;/a&gt; HTML. Let's improve my code instead… we can shed a few redundant bits and maybe use &lt;a href="https://xenodium.com/modern-elisp-libraries/"&gt;newer libraries and features&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most importantly, let's improve the user experience by sanitizing and filtering URLs a little better.&lt;/p&gt;
&lt;p&gt;We start by writing a function that looks for a URL in the clipboard and subsequently fetches, parses, and extracts all links found in the target page.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(require 'enlive)
(require 'seq)

(defun ar/scrape-links-from-clipboard-url ()
  &amp;quot;Scrape links from clipboard URL and return as a list. Fails if no URL in clipboard.&amp;quot;
  (unless (string-prefix-p &amp;quot;http&amp;quot; (current-kill 0))
    (user-error &amp;quot;no URL in clipboard&amp;quot;))
  (thread-last (enlive-query-all (enlive-fetch (current-kill 0)) [a])
    (mapcar (lambda (element)
              (string-remove-suffix &amp;quot;/&amp;quot; (enlive-attr element 'href))))
    (seq-filter (lambda (link)
                  (string-prefix-p &amp;quot;http&amp;quot; link)))
    (seq-uniq)
    (seq-sort (lambda (l1 l2)
                (string-lessp (replace-regexp-in-string &amp;quot;^http\\(s\\)*://&amp;quot; &amp;quot;&amp;quot; l1)
                              (replace-regexp-in-string &amp;quot;^http\\(s\\)*://&amp;quot; &amp;quot;&amp;quot; l2))))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's chat &lt;em&gt;(current-kill 0)&lt;/em&gt; for a sec. No improvement from my previous usage, but let's just say building interactive commands that work with your current clipboard (or &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Kill-Ring.html"&gt;kill ring&lt;/a&gt; in Emacs terminology) is super handy (see &lt;a href="https://xenodium.com/emacs-clone-git-repo-from-clipboard/"&gt;clone git repo from clipboard&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Moving on to sanitizing and filtering URLs… Links often have trailing slashes. Let's flush them. &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/subr-x.el#L261"&gt;string-remove-suffix&lt;/a&gt; to the rescue. This and other handy string-manipulating functions are built into Emacs since 24.4 as part of &lt;a href="https://github.com/emacs-mirror/emacs/blob/master/lisp/emacs-lisp/subr-x.el"&gt;subr-x.el&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, we can keep http(s) links and ditch everything else. The end-goal is to extract links posted by users, so these are typically fully qualified external URLs. &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/seq.el"&gt;seq-filter&lt;/a&gt; steps up to the task, included in Emacs since 25.1 as part of the &lt;a href="https://github.com/emacs-mirror/emacs/blob/master/lisp/emacs-lisp/seq.el"&gt;seq.el family&lt;/a&gt;. We remove duplicate links using &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/seq.el#L431"&gt;seq-uniq&lt;/a&gt; and sort them via &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/seq.el#L255"&gt;seq-sort&lt;/a&gt;. All part of the same package.&lt;/p&gt;
&lt;p&gt;When sorting, we could straight up use &lt;em&gt;seq-sort&lt;/em&gt; and &lt;em&gt;string-lessp&lt;/em&gt; and nothing else, but it would separate http and https links. Let's not do that, so we drop &lt;em&gt;http(s)&lt;/em&gt; prior to comparing strings in &lt;em&gt;seq-sort&lt;/em&gt;'s predicate. &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/subr.el#L4468"&gt;replace-regexp-in-string&lt;/a&gt; does the job here, but if you'd like to skip regular expressions, &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/subr-x.el#L255"&gt;string-remove-prefix&lt;/a&gt; works just as well.&lt;/p&gt;
&lt;p&gt;Yay, sorting no longer cares about http vs https:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://andymatuschak.org
http://antirez.com
https://apenwarr.ca/log
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all that in mind, let's flatten list processing using &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/subr-x.el#L69"&gt;thread-last&lt;/a&gt;. This isn't strictly necessary, but since this is the 2021 edition, we'll throw in this macro added to Emacs in 2016 as part of 25.1. Arthur Malabarba has a &lt;a href="https://endlessparentheses.com/new-in-emacs-25-1-more-flow-control-macros.html"&gt;great post on thread-last&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that we've built out &lt;em&gt;ar/scrape-links-from-clipboard-url&lt;/em&gt; function, let's make its content consumable!&lt;/p&gt;
&lt;h2&gt;The completing frameworks way&lt;/h2&gt;
&lt;p&gt;This is the 2021 edition, so power up your completion framework du jour and feed the output of &lt;em&gt;ar/scrape-links-from-clipboard-url&lt;/em&gt; to our completion robots…&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-link-scraping-2021-edition/scrape_complete.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I'm heavily vested in &lt;a href="https://github.com/abo-abo/swiper"&gt;ivy&lt;/a&gt;, but since we're using the built-in &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html"&gt;completing-read&lt;/a&gt; function, any completion framework like &lt;a href="https://github.com/minad/vertico"&gt;vertico&lt;/a&gt;, &lt;a href="https://github.com/raxod502/selectrum/"&gt;selectrum&lt;/a&gt;, &lt;a href="https://github.com/emacs-helm/helm"&gt;helm&lt;/a&gt;, or &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/ido/index.html"&gt;ido&lt;/a&gt; should kick right in to give you extra powers.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(defun ar/view-completing-links-at-clipboard-url ()
  &amp;quot;Scrape links from clipboard URL and open all in external browser.&amp;quot;
  (interactive)
  (browse-url (completing-read &amp;quot;links: &amp;quot;
                               (ar/scrape-links-from-clipboard-url))))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The auto-open way (use with caution)&lt;/h2&gt;
&lt;p&gt;Sometimes you just want to open every link posted in the comments and use your browser to discard, closing tabs as needed. The recent HN news instance wasn't one of these cases, with a whopping 398 links returned by our &lt;em&gt;ar/scrape-links-from-clipboard-url&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-link-scraping-2021-edition/scrape_browse.gif" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: I capped the results to 5 in this gif/demo to prevent a Firefox tragedy (see &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/emacs-lisp/seq.el#L231"&gt;seq-take&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In a case like Hacker News's, we don't want to surprise-attack the user and bomb their browser by opening a gazillion tabs, so let's give a little heads-up using &lt;a href="https://github.com/emacs-mirror/emacs/blob/3af9e84ff59811734dcbb5d55e04e1fdb7051e77/lisp/subr.el#L2869"&gt;y-or-n-p&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(defun ar/browse-links-at-clipboard-url ()
  (interactive)
  (let ((links (ar/scrape-links-from-clipboard-url)))
    (when (y-or-n-p (format &amp;quot;Open all %d links? &amp;quot; (length links)))
      (mapc (lambda (link)
              (browse-url link))
            links))))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The org way&lt;/h2&gt;
&lt;p&gt;My &lt;a href="https://xenodium.com/get-emacs-to-gather-links-in-posts/"&gt;2015 solution&lt;/a&gt; leveraged an &lt;a href="https://orgmode.org/"&gt;org mode&lt;/a&gt; buffer to dump the fetched links. The org way is still my favorite. You can use whatever existing Emacs super powers you already have on top of the org buffer, including searching and filtering fueled by your favourite completion framework. I'm a fan of &lt;a href="https://oremacs.com/"&gt;Oleh&lt;/a&gt;'s &lt;a href="https://github.com/abo-abo/swiper"&gt;swiper&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-link-scraping-2021-edition/scrape_org.gif" /&gt;&lt;/p&gt;
&lt;p&gt;The 2021 implementation is mostly a tidy-up, removing some cruft, but also uses our new &lt;em&gt;ar/scrape-links-from-clipboard-url&lt;/em&gt; function to filter and sort accordingly.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.commonlisp"&gt;(require 'org)

(defun ar/view-links-at-clipboard-url ()
  &amp;quot;Scrape links from clipboard URL and dump to an org buffer.&amp;quot;
  (interactive)
  (with-current-buffer (get-buffer-create &amp;quot;*links*&amp;quot;)
    (org-mode)
    (erase-buffer)
    (mapc (lambda (link)
            (insert (org-make-link-string link) &amp;quot;\n&amp;quot;))
          (ar/scrape-links-from-clipboard-url))
    (goto-char (point-min))
    (switch-to-buffer (current-buffer))))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Emacs + community + packages + your own glue = awesome&lt;/h2&gt;
&lt;p&gt;To power our 2021 link scraper, we've used newer libraries included in more recent versions of Emacs, leveraged an older but solid HTML-parsing package, pulled in org mode (the epicenter of Emacs note-taking), dragged in our favorite completion framework, and tickled our handy browser all by smothering the lot with some elisp glue to make Emacs do exactly what we want. &lt;a href="http://emacsrocks.com/"&gt;Emacs does rock&lt;/a&gt;.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Fri, 28 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/emacs-link-scraping-2021-edition</guid></item><item><title>Rating Cyber Security Practitioners</title><link>https://blog.eutopian.io/rating-cyber-security-practitioners/</link><description>1330 words, 5 minutes.
Jean-Léon Gérôme, The Gladiators (Bronze, c.1878).
Do you like hyper-masculine, gladiatorial confrontations where losers suffer not just the ignominy of defeat but significant even permanent loss of status in a rigid hierarchical system? You do, do you? Well then you&amp;rsquo;ll like this.
Or do you think that sounds detestable? If so you&amp;rsquo;ll want to defeat this barbarous idea before it takes hold. Keep reading.
The World As It Is The number of security qualifications has exploded.</description><author>Privacy, Power, &amp;amp; Protection In The Cyber Century</author><pubDate>Fri, 28 May 2021 00:39:30 GMT</pubDate><guid isPermaLink="true">https://blog.eutopian.io/rating-cyber-security-practitioners/</guid></item><item><title>The solar panel factory that may solve all problems...</title><link>https://stop.zona-m.net/2021/05/the-solar-panel-factory-that-may-solve-all-problems.../</link><description>&lt;p&gt;Except one, maybe.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 27 May 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/the-solar-panel-factory-that-may-solve-all-problems.../</guid></item><item><title>Camera Resolution</title><link>https://bastibe.de/2021-05-27-camera-resolution.html</link><description>&lt;p&gt;Some of my photographic lenses are reknown for their outstanding sharpness, others are said to be mediocre. But somehow I never quite saw a big difference in sharpness between them. Do older lenses have less resolution than the newer ones? Can a dedicated prime lens resolve more detail than a multi-purpose zoom lens? Let's find out.&lt;/p&gt;
&lt;p&gt;These questions recently came to a head for me, in the choice between two compact cameras: &lt;a href="https://camerasize.com/compact/#566,819,ha,f"&gt;A Fujifilm X100T, 16 MP, with a 23 mm lens, and a Ricoh GR III, 24 MP, with an 18 mm lens&lt;/a&gt;. In the last few months, I grew to like the X100T's 23 mm viewpoint, but did not enjoy how bulky the camera is in comparison to the GR III. So I wondered if I could replace the X100T with the Ricoh GR III and simply crop to 23 mm.&lt;/p&gt;
&lt;p&gt;Being a scientist and all that, I set up an experiment: I printed out a &lt;a href="https://www.graphics.cornell.edu/~westin/misc/res-chart.html"&gt;resolution chart&lt;/a&gt;, set my cameras on a tripod, and took pictures with all my cameras such that the chart filled a similar portion of each image. Base ISO, two-second timer, processed in Capture One. White balance and contrast were equalized.&lt;/p&gt;
&lt;figure style="text-align: center;"&gt;
&lt;a href="/static/2021-05/comparison.svg"&gt;&lt;img alt="camera resolution comparison" src="/static/2021-05/comparison.svg" width="80%" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Red bars indicate the point where some lines are no longer distinguishable, i.e. the limit of the system's resolution.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Each row in the above grid is one camera/lens/focal length combination, taken at the brightest aperture in the left column, and at the sharpest aperture in the right column. The text on the left shows photographic parameters, and the text on the right image parameters. The red line is the spot on the chart where some lines start to blend together, the limit of the image's resolution, as judged by my eyeballs. Depending on the exact framing, DPIs vary somewhat between the images, but generally at a factor of around &lt;span class="math"&gt;\(\sqrt{14/16} = 1.22\)&lt;/span&gt; between the 24 MP images and the 16 MP images. The bottom row shows a synthetic image at simulated 24 MP ∕ 380 DPI and 16 MP ∕ 280 DPI.&lt;/p&gt;
&lt;p&gt;From these images, the biggest contribution to resolution seems to be sensor megapixels. All 24 MP images resolve closer line pairs than any 16 MP image. This could also be a hint that lenses seem to generally outresolve their sensors.&lt;/p&gt;
&lt;p&gt;Looking closely at the images, another hint becomes visible: aliasing is visible in all images. This is only possible if lens resolution is higher than sensor resolution.&lt;/p&gt;
&lt;p&gt;Frankly, this is a rather big surprise to me. Reading discussions on the internet had predisposed me to believe that some lenses should be much higher resolution than others, and my big travel-zoom in particular should be terribly soft. From my measurements however, I see no evidence of this claim. All of these lenses outresolve their sensors, and differences between lenses is rather miniscule in general.&lt;/p&gt;
&lt;p&gt;Another common wisdom is that lenses wide open are less sharp than stopped down. In this test sample, this seems to be true for the X100T's, and to a lesser extent for the LX100, and the Fujifilm XF 60. All other lenses do not show significant (!) differences between apertures. And even for the affected lenses, the difference in resolution is not dramatic.&lt;/p&gt;
&lt;p&gt;However, resolution is not sharpness. While line pairs may remain resolvable, their edges do lose definition at wide-open apertures on the Fujifilm XF 18-135 at some focal lengths, and the X100T. These lenses definitely need at least additional sharpening at wider apertures. Contrast is also visibly different, but I chose to edit out contrast differences to keep the graphs legible. Generally, the Ricoh GRs' lenses (particularly the GR I) were by far the most contrasty of these lenses.&lt;/p&gt;
&lt;p&gt;Another interesting comparison can be drawn between the synthetic images in the bottom row and the camera exposures above: None of the 24 MP images reach the resolution of the synthetic image, perhaps due to demosaicing artifacts. However, the 16 MP exposures are very close to the synthetic image. I don't yet know how to interpret this.&lt;/p&gt;
&lt;p&gt;Speaking of demosaicing however, there does not seem to be a difference in resolution between the X-Trans Fujifilm sensors and the Bayer Ricoh/Panasonic sensors. I also checked whether a different raw developer would make a difference, but did not find anything noteworthy.&lt;/p&gt;
&lt;p&gt;Overall, this experiment has taught me that all my lenses are high resolution, and that lens resolution in general is probably not a topic worth fussing over. Subpixel patterns are equally unimportant. Instead, lenses do differentiate themselves in contrast and sharpness, and camera sensors in megapixels. And finally, the tiny GR III in crop mode can indeed outresolve the X100T, and out-contrast and out-sharpness it as well.&lt;/p&gt;</description><author>bastibe.de</author><pubDate>Thu, 27 May 2021 19:45:00 GMT</pubDate><guid isPermaLink="true">https://bastibe.de/2021-05-27-camera-resolution.html</guid></item><item><title>This is just to say</title><link>https://one.mikro2nd.net/pages/this-is-just-to-say/</link><description>I have eaten the cookies that were in your browser and which you were probably saving for facebook thank me! they were toxic: spyware and invasive</description><author>one mikro2nd</author><pubDate>Thu, 27 May 2021 17:05:36 GMT</pubDate><guid isPermaLink="true">https://one.mikro2nd.net/pages/this-is-just-to-say/</guid></item><item><title>WFH Day 438</title><link>https://ingo-richter.io/cv19wfh/2021/05/day438/</link><description>&lt;h2 id="wfh-day-438"&gt;WFH Day 438&lt;/h2&gt;
&lt;p&gt;I get it. We are all waiting for the pandemic to be over. Everybody wants their normal pre-pandemic life back. But will this be possible? What exactly do we mean by saying that? What do we consider normal life? Meeting with friends in person? Meeting anybody in person? Being able to leave your face mask at home? Getting rid of all the restrictions is probably the most important thing for many of us (it&amp;rsquo;s only a guess).&lt;/p&gt;</description><author>Ingo Richter on Today is Tomorrow's Past</author><pubDate>Thu, 27 May 2021 10:00:00 GMT</pubDate><guid isPermaLink="true">https://ingo-richter.io/cv19wfh/2021/05/day438/</guid></item><item><title>[Computing] A thought experiment: High-Availability IRC</title><link>https://www.devever.net/~hl/ha-irc</link><description>&lt;p&gt;I've been idly pondering how to create a system for high-availability IRC. I
don't have any immediate intentions of implementing any of this, but I thought
some people might find it interesting.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Thu, 27 May 2021 07:15:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/ha-irc</guid></item><item><title>Interface-news</title><link>https://b.agilob.net/random/interface/</link><description>It’s been nearly 3 years since my last post, so I don’t expect people to be here any longer.
If anybody is here, I wanted to share with you my recent development.
I wrote a simple CRUD page for storing links about topics that are of interest to me. I read the link (90% I read it, I promise), found it good quality, interesting, refreshing knowledge and I want to share this link with you, so you can gain some knowledge too!</description><author>Random Notes by agilob</author><pubDate>Thu, 27 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://b.agilob.net/random/interface/</guid></item><item><title>Collect Emails from NextJS Form in HubSpot</title><link>https://aknapen.nl/blog/collect-emails-from-nextjs-form-in-hubspot/</link><description>Collect Emails from NextJS Form in HubSpot HubSpot is an awesome platform to centrally track all the contacts your company is doing business with. At ClaimR we have been using HubSpot with great success, as it allows all our team members to have a single source of truth.
During the last few weeks I have been working on our renewed website. NextJS is a no-brainer at this point, as it is an amazing framework which spits out static HTML wherever it can - perfect for fast loading landing pages - while also giving you the flexibility to mix in some dynamic content and other server side logic.</description><author>Adriaan Knapen</author><pubDate>Thu, 27 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://aknapen.nl/blog/collect-emails-from-nextjs-form-in-hubspot/</guid></item><item><title>Enabling real-time collaboration with RSocket</title><link>https://canvatechblog.com/enabling-real-time-collaboration-with-rsocket-92416fe52650</link><description>This post describes how we empowered our millions of users at Canva to collaborate at scale by introducing services that support bidirectional streaming using RSocket.</description><author>SerCe's blog</author><pubDate>Thu, 27 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://canvatechblog.com/enabling-real-time-collaboration-with-rsocket-92416fe52650</guid></item><item><title>How to Use Git Bisect for Debugging</title><link>https://www.dannyguo.com/blog/how-to-use-git-bisect-for-debugging</link><author>Danny Guo</author><pubDate>Thu, 27 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.dannyguo.com/blog/how-to-use-git-bisect-for-debugging</guid></item><item><title>Big Tech does NOT need to listen to you</title><link>https://stop.zona-m.net/2021/05/big-tech-does-not-need-to-listen-to-you/</link><description>&lt;p&gt;Why should they? There are far better ways to know what you do.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 26 May 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/big-tech-does-not-need-to-listen-to-you/</guid></item><item><title>Scalaing Club Leaderboard Infrastructure for Millions of Users</title><link>https://medium.com/strava-engineering/scaling-club-leaderboard-infrastructure-for-millions-of-users-9ee857ce8cfe</link><description>&lt;p&gt;Sometimes, we work on shiny new features that (we hope) athletes will notice and love. Other
times, we need to work on back-end improvements that are unlikely to be noticed at all, but
are usually just as important as the other things we work on. Some recent work I did for our
club leaderboards fell into the latter category — unlikely to be noticed, but really
important for the scalability and reliability of strava.com.&lt;/p&gt;</description><author>Mike Kasberg's Blog</author><pubDate>Wed, 26 May 2021 17:00:00 GMT</pubDate><guid isPermaLink="true">https://medium.com/strava-engineering/scaling-club-leaderboard-infrastructure-for-millions-of-users-9ee857ce8cfe</guid></item><item><title>Read this blog with emacs</title><link>https://evilcookie.de/read-this-blog-with-emacs.html</link><description/><author>blog</author><pubDate>Wed, 26 May 2021 13:53:08 GMT</pubDate><guid isPermaLink="true">https://evilcookie.de/read-this-blog-with-emacs.html</guid></item><item><title>[Computing] Freenode IRC operators perform mass seizure of channels for mentioning Liberachat</title><link>https://www.devever.net/~hl/freenode_abuse2</link><description>&lt;p&gt;Timeline:&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Wed, 26 May 2021 11:51:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/freenode_abuse2</guid></item><item><title>Stripe Goes No-Code — Stripe Payment Links Explained</title><link>https://www.swyx.io/stripe-payment-links</link><description>Stripe has entered the No Code market in a big way! I take a crack at explaining what it's doing and why.</description><author>swyx's site RSS Feed</author><pubDate>Wed, 26 May 2021 05:17:37 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/stripe-payment-links</guid></item><item><title>Taking org-roam everywhere with logseq</title><link>https://coredumped.dev/2021/05/26/taking-org-roam-everywhere-with-logseq/</link><description>Updated: 2022-08-03
I love org-roam. It lets me take notes in a way that matches how I think. It makes it easy to recall what I have learned and find connections between ideas. But there has always been one big problem with org-roam: it ties me to the desktop. When I am on the go and all I have is my phone, I don&amp;rsquo;t have access to my notes.
There are some stop gap solutions to try and fix this.</description><author>Core Dumped</author><pubDate>Wed, 26 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://coredumped.dev/2021/05/26/taking-org-roam-everywhere-with-logseq/</guid></item><item><title>Hrenovuha, the horseradish liquor</title><link>https://benovermyer.com/blog/2021/05/hrenovuha/</link><description>&lt;div class="dress-image"&gt;
    &lt;img alt="A photo of hrenovuha" src="https://benovermyer.com/blog/2021/05/hrenovuha/&amp;#x2F;&amp;#x2F;benovermyer.com&amp;#x2F;processed_images&amp;#x2F;hrenovuha.721205a503505e4d.jpg" /&gt;
    
    &lt;div class="caption"&gt;Hrenovuha, horseradish liquor&lt;/div&gt;
    
&lt;/div&gt;

&lt;p&gt;A few months ago I watched an episode of The Booze Traveller set in Siberia. The show's
about a guy from Boston travelling the world and experiencing local, well, booze. In
Siberia, he encountered something called &lt;em&gt;hrenovuha&lt;/em&gt;, a liquor made from distilling horseradish.&lt;/p&gt;
&lt;p&gt;It reminded me of horseradish vodka. I first encountered horseradish vodka in a Soviet-themed
bar called Hammer and Sickle in Uptown Minneapolis. It was meant as a "punishment" shot as part
of their Russian Roulette drinking game. However, I discovered that I loved it.&lt;/p&gt;
&lt;p&gt;There are some excellent horseradish vodkas available in North America. However, none of them
are really hrenovuha, which is much more horseradish-forward. So I decided to try making
something akin to hrenovuha at home. Since I couldn't actually distill horseradish - that sort
of thing is frowned on by authorities - I did the next closest thing. I took a bunch
of fresh horseradish and a lot of 180 proof Everclear and made an infusion.&lt;/p&gt;
&lt;p&gt;I peeled the horseradish and cut it into several one-inch chunks. Then I added that to
about 500 ml of Everclear in a mason jar. I sealed the jar and let it sit on the counter
in a dark corner for about a week, then removed the horseradish and strained the result
back into the jar and threw it in the fridge.&lt;/p&gt;
&lt;p&gt;The result is really strong and &lt;em&gt;very&lt;/em&gt; horseradish-y. Much more so than horseradish vodka.
I doubt it's the same flavor profile as hrenovuha, but I don't have access to the latter,
so I can only guess.&lt;/p&gt;</description><author>Ben Overmyer's Site</author><pubDate>Wed, 26 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://benovermyer.com/blog/2021/05/hrenovuha/</guid></item><item><title>Perl one-liner articles</title><link>https://learnbyexample.github.io/mini/perl-oneliner-articles/</link><description>&lt;p&gt;One of the feedback I got for my &lt;a href="https://github.com/learnbyexample/learn_perl_oneliners"&gt;Perl one-liners ebook&lt;/a&gt; was to showcase examples where Perl shines compared to other text processing tools.&lt;/p&gt;
&lt;p&gt;Soon after, I got an invite to publish an article for the &lt;a href="https://www.perl.com/"&gt;perldotcom&lt;/a&gt; site. So, I wrote a two-part post highlighting use cases where Perl's rich regular expression engine, built-in functions, extensive ecosystem and portability helps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.perl.com/article/perl-one-liners-part-1/"&gt;Perl / Unix One-liner Cage Match, Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.perl.com/article/perl-one-liners-part-2/"&gt;Perl / Unix One-liner Cage Match, Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Thanks to the editors for suggestions and improvements.&lt;/em&gt;&lt;/p&gt;</description><author>learnbyexample</author><pubDate>Wed, 26 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://learnbyexample.github.io/mini/perl-oneliner-articles/</guid></item><item><title>Open Insurance is coming to Italy</title><link>https://stop.zona-m.net/2021/05/open-insurance-is-coming-to-italy/</link><description>&lt;p&gt;And this is why this is good for EVERYBODY, EVERYWHERE.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 26 May 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/open-insurance-is-coming-to-italy/</guid></item><item><title>How To Add Prettier and ESLint Automation to a React Codebase</title><link>https://www.swyx.io/prettier-eslint-react</link><description>Automated code quality with GitHub Actions, Husky, Lint-staged, Prettier, and ESLint</description><author>swyx's site RSS Feed</author><pubDate>Wed, 26 May 2021 01:32:58 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/prettier-eslint-react</guid></item><item><title>Optimizing Cargo - query 3</title><link>https://river.me/blog/optimizing-cargo-3/</link><description>The final of three Cargo queries I recently optimized on Leaguepedia</description><author>River Writes - A MediaWiki Blog</author><pubDate>Wed, 26 May 2021 01:07:05 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/optimizing-cargo-3/</guid></item><item><title>2021–05–26: PinePhone keyboard</title><link>https://xnux.eu/log/#034</link><author>megi's PinePhone Development Log</author><pubDate>Wed, 26 May 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#034</guid></item><item><title>Awesome Developer Advocates Are Hiding in Plain Sight</title><link>https://whoisnnamdi.com/developer-advocates-plain-sight/</link><description>Why you shouldn’t filter for social media following or prior experience</description><author>Who is Nnamdi?</author><pubDate>Tue, 25 May 2021 17:20:40 GMT</pubDate><guid isPermaLink="true">https://whoisnnamdi.com/developer-advocates-plain-sight/</guid></item><item><title>TIL that sometimes you need to pass a header to nginx’s gprc module</title><link>https://www.zufallsheld.de/2021/05/25/til-that-you-need-to-pass-a-header-to-nginxs-gprc-module/</link><description>&lt;p&gt;I am running &lt;a href="https://www.percona.com/doc/percona-monitoring-and-management/2.x/index.html"&gt;Percona Monitoring and Managent&lt;/a&gt; (&amp;#8220;&lt;span class="caps"&gt;PMM&lt;/span&gt;&amp;#8221;) as a service for my fellow colleagues so they can monitor their&amp;nbsp;databases.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;PMM&lt;/span&gt; runs as a container on a virtual machine, listening on port&amp;nbsp;8080.&lt;/p&gt;
&lt;p&gt;On the same virtual machine runs another nginx webserver as a reverse proxy for the container …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Tue, 25 May 2021 15:39:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/05/25/til-that-you-need-to-pass-a-header-to-nginxs-gprc-module/</guid></item><item><title>So tired of Homebrew messing up with my stuff</title><link>https://nicolaiarocci.com/so-tired-of-homebrew-messing-up-with-my-stuff/</link><description>&lt;p&gt;Pardon me while I&amp;rsquo;m venting out my frustration. I am so tired of Homebrew
messing up with my stuff. It used to be the perfect tool for the right job
until they decided to auto-brew-update-and-cleanup at every new install.
Another day another issue, today with vim not running anymore:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dyld: Library not loaded: /usr/local/opt/lua/lib/liblua.5.3.dylib
Referenced from: /user/local/bin/vi
Reason: image not found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lua has suddenly gone missing. Ah, but of course. I installed something with
Homebrew this morning. The fix is to &lt;code&gt;brew reinstall vim&lt;/code&gt;, which then leads to
the following error:&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 25 May 2021 10:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/so-tired-of-homebrew-messing-up-with-my-stuff/</guid></item><item><title>On Programming and Writing</title><link>https://nicolaiarocci.com/on-programming-and-writing/</link><description>&lt;p&gt;My brilliant friend Salvatore Sanfilippo (otherwise known as antirez of Redis
fame) has an interesting write-up on &lt;a href="http://antirez.com/news/135"&gt;his website&lt;/a&gt;. How similar is
programming to prose writing? After getting his own feet wet with novel
writing, he is convinced that the two activities share many common traits.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One year ago I paused my programming life and started writing a novel, with
the illusion that my new activity was deeply different than the previous one.
A river of words later, written but more often rewritten, I’m pretty sure of
the contrary: programming big systems and writing novels have many common
traits and similar processes.&lt;/p&gt;</description><author>Nicola Iarocci</author><pubDate>Tue, 25 May 2021 09:05:25 GMT</pubDate><guid isPermaLink="true">https://nicolaiarocci.com/on-programming-and-writing/</guid></item><item><title>Salesloft Icon Fundamentals</title><link>https://solomon.io/salesloft-icon-fundamentals/</link><description>This presentation on icon fundamentals was given to Salesloft’s Product Design team on May 24, 2021. Shortly before the new icon set shipped.</description><author>Sam Solomon</author><pubDate>Tue, 25 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://solomon.io/salesloft-icon-fundamentals/</guid></item><item><title>Poor in Tech, and why it is important</title><link>https://stop.zona-m.net/2021/05/poor-in-tech-and-why-it-is-important/</link><description>&lt;p&gt;Here is a post (not mine) that you MUST read.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 25 May 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/poor-in-tech-and-why-it-is-important/</guid></item><item><title>Renting a single family home in Seattle</title><link>https://zdwolfe.medium.com/renting-a-single-family-home-in-seattle-95bf190680b3?source=rss-65d40a7904f------2</link><description>&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9uWAplQoB7CyQpHsM7gGCA.jpeg" /&gt;&lt;figcaption&gt;&lt;a href="https://unsplash.com/photos/ecJvbtex-7c"&gt;Photo by Launde Morel&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;My wife and I have rented one- or two bedroom high-rise apartments in the Capitol Hill, South Lake Union, and Belltown neighborhoods in Seattle for the last eight years.&lt;/p&gt;&lt;p&gt;After eighteen months of COVID lockdown enduring the downsides of urban apartment living (traffic, noise, crime, cost, crowds, little personal space) without being able to enjoy the upsides of proximity to work, dining, bars, friends, and walkability as much, we decided we wanted the change of pace and more space of a single family home in the suburbs.&lt;/p&gt;&lt;p&gt;My experience securing a rental house in the Spring of 2021 was way more difficult than I expected, so below is my attempt to share what I learned with anyone else that plans to rent in Seattle any time soon.&lt;/p&gt;&lt;h3&gt;TL;DR&lt;/h3&gt;&lt;p&gt;If you get bored and don’t read the rest, just take away the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Don’t bother starting to search until you’re about 2 months from moving.&lt;/li&gt;&lt;li&gt;You have a contact window of a few hours to be even be considered for awesome houses.&lt;/li&gt;&lt;li&gt;Use all listing services you can find, get instantly notified of new listings, and act on them right away.&lt;/li&gt;&lt;li&gt;Prefer loose search criteria on listing websites over strict. Owners don’t include everything as searchable attributes on their listings and you’ll miss houses that fit.&lt;/li&gt;&lt;li&gt;Get a completed application in front of the owner ASAP. Seattle’s First In Time law forces owners to offer a house to people in the order they’ve provided a completed application.&lt;/li&gt;&lt;li&gt;Have required application documentation prepared before you get serious. Don’t fumble around on your bank’s website downloading statements from months ago while someone else beats you in line for your dream house.&lt;/li&gt;&lt;li&gt;Tour ASAP, in the morning if possible. Some owners don’t let you apply until you’ve toured, but not all. If so, ask for an application before you leave.&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Finding a Single Family Home Is Much Harder Than Apartment Hunting&lt;/h3&gt;&lt;p&gt;I (foolishly) expected the search for a single family home to require about the same amount of effort as renting apartments from apartment towers downtown from a large real estate company.&lt;/p&gt;&lt;p&gt;In the past, our process to find apartments had been:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Tune search filters on one or two listing services to &lt;strong&gt;exactly&lt;/strong&gt; what we wanted in the neighborhood we wanted.&lt;/li&gt;&lt;li&gt;Select from a menu of apartments with move-in dates in the far future.&lt;/li&gt;&lt;li&gt;Schedule an appointment with a single leasing agent who is available on-site most of the day to see a few different floor plans or units in a single visit. They’re selling to you.&lt;/li&gt;&lt;li&gt;Repeat until signing a lease. In the four apartments we lived in, we never toured more than three times.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Finding a single-family home was much more difficult than finding an apartment in every way due to the sheer number of individuals you need to talk to, time-spent-per-unit-toured, the insane demand and low supply.&lt;/p&gt;&lt;h3&gt;Our House Search Visualized&lt;/h3&gt;&lt;p&gt;The below chart shows the number of listings we viewed, considered, how many we abandoned or were rejected by, and how we eventually secured a place.&lt;/p&gt;&lt;blockquote&gt;Over 432 listings viewed, 57 seriously considered, 8 applications, 3 tours, and 1 accepted application&lt;/blockquote&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VIz6hNY2JYMHPOru5d3frw.png" /&gt;&lt;figcaption&gt;House search listings viewed to accepted visualized. &lt;a href="https://zdwolfe.github.io/2021-house-search/home-search-sankey.html"&gt;Interactive version&lt;/a&gt;. &lt;a href="https://github.com/zdwolfe/2021-house-search"&gt;Source code&lt;/a&gt;.&lt;/figcaption&gt;&lt;/figure&gt;&lt;h3&gt;Sourcing Rental House Leads&lt;/h3&gt;&lt;h4&gt;Use All Available Listing Services&lt;/h4&gt;&lt;p&gt;In contrast to hunting for a downtown high-rise apartment where Zillow is king, I ended up using over a dozen rental listing services and eight property management company sites before eventually finding a house.&lt;/p&gt;&lt;p&gt;Most houses are duplicated on several listing services. However, all the houses we eventually toured were sourced from different sites (Zillow, Trulia, Hotpads, Apartments.com), so it was worth while to us to broaden our search past the obvious big names.&lt;/p&gt;&lt;p&gt;Houses that showed up on several sites often show up on one service many hours before others. As I’ll mention a few times, getting your name in line as quickly as possible is really important, so the advantage of a getting notified of a house a few hours before others is meaningful.&lt;/p&gt;&lt;p&gt;My ranked favorite listing services are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Zillow&lt;/li&gt;&lt;li&gt;Trulia&lt;/li&gt;&lt;li&gt;Hotpads&lt;/li&gt;&lt;li&gt;Apartments.com&lt;/li&gt;&lt;li&gt;Rent.com&lt;/li&gt;&lt;li&gt;PadMapper&lt;/li&gt;&lt;li&gt;kw.com&lt;/li&gt;&lt;li&gt;forrent.com&lt;/li&gt;&lt;li&gt;Apartmentlist&lt;/li&gt;&lt;li&gt;seattlerentals.com&lt;/li&gt;&lt;li&gt;Showmojo&lt;/li&gt;&lt;li&gt;kw.com&lt;/li&gt;&lt;li&gt;Craigslist&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;Get Notified and Act Immediately On Matches&lt;/h4&gt;&lt;p&gt;As I’ll mention a few times elsewhere in this post, getting a tour and application pretty much just comes down to getting on an owner’s radar before others do.&lt;/p&gt;&lt;p&gt;Most listing services have a “Saved Searches” feature which can automatically send you an email when a new listing comes online. Zillow, Hot Pads, and Trulia all had phone apps that would send push notifications.&lt;/p&gt;&lt;p&gt;Configure these to notify you immediately. On Zillow, it looks like this (Instant):&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/932/1*n8jVxS9DYWH5FWaqnG2EOQ.png" /&gt;&lt;/figure&gt;&lt;p&gt;Then act on the notifications as soon as you get them. We dropped what we were doing as soon as we saw a perfect match more than five times to apply to a house and still didn’t even land a tour for it. Also set aside some time every morning and on Fridays, as that appears to be the hottest times for new listings:&lt;/p&gt;&lt;figure&gt;&lt;img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xOySgCiu5PiSmzjuUB5Puw.png" /&gt;&lt;/figure&gt;&lt;h4&gt;Prefer Loose Over Strict Search Filters&lt;/h4&gt;&lt;p&gt;Hyper-tuning search filters on listing services turned out to be a really bad move. I wasted frustrating calendar time thinking there were no fresh houses on the market that met our criteria only to find that I had been unnecessarily filtering many of them out.&lt;/p&gt;&lt;blockquote&gt;Prefer loose over strict search filters to avoid missing incorrectly formatted house listings.&lt;/blockquote&gt;&lt;p&gt;When an owner lists their house for rent, they almost always post a listing on many websites at the same time. The house we eventually rented was listed on five different sites. To make their lives easier, instead of entering criteria as searchable fields in the listing, it seems that owners will copy/paste a text blurb into each listing. Additionally, home types don’t appear to have a strict definition, and owners may use Townhouse and House interchangeably.&lt;/p&gt;&lt;p&gt;For example, we wanted to rent a 3+ bedroom house that allowed small dogs and had parking. That means our search filters for most services would look like:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Pets:&lt;/strong&gt; Small dogs allowed&lt;br /&gt;&lt;strong&gt;Bedrooms:&lt;/strong&gt; 3+&lt;br /&gt;&lt;strong&gt;Parking:&lt;/strong&gt; On-site&lt;br /&gt;&lt;strong&gt;Home Type:&lt;/strong&gt; House&lt;br /&gt;&lt;strong&gt;Move-In Date: 6/5&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;A listing could be:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Pets: ____&lt;br /&gt;Bedrooms:&lt;/strong&gt; 3&lt;br /&gt;&lt;strong&gt;Parking:&lt;/strong&gt; &lt;strong&gt;____&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Home Type: &lt;/strong&gt;Townhouse&lt;strong&gt;&lt;br /&gt;Description:&lt;/strong&gt; For rent is a beautiful 3 bedroom single family house available in June. Pets allowed with additional deposit. Huge two-car garage.&lt;/p&gt;&lt;p&gt;Even though it might fit us perfectly, when searching on Zillow with our filters, the above house doesn’t show up! After I relaxed the filters I had saved, I had much better luck.&lt;/p&gt;&lt;h4&gt;Look At Property Management Company Sites Too&lt;/h4&gt;&lt;p&gt;In addition to big rental listing services, I found some success trawling property management company websites. It seems that some property managers will list properties a few hours (which turned out to be meaningful) before posting to the big names. I found these by Googling the names of companies that managed properties I was interested in.&lt;/p&gt;&lt;h4&gt;Rental Real Estate Agents Refused To Help&lt;/h4&gt;&lt;p&gt;After a few weeks of trying to find a house and only landing a single tour, I was discouraged and wanted some help finding a place. I reached out to rental real estate agencies to get a list of houses. Each of them replied that they weren’t offering the services at the time because houses were flying off the market so fast that they couldn’t even keep their internal lists up to date.&lt;/p&gt;&lt;h4&gt;Be Persistent, Follow Up On Near-Misses; Be Assertive&lt;/h4&gt;&lt;p&gt;On more than one occasion, we didn’t hear back from an owner for some time, only to get a positive response after following up again later.&lt;/p&gt;&lt;p&gt;After time went on, I also learned that we were much more likely to get a tour by calling the owner and scheduling over the phone. After one embarrassing exchange, I learned to save the owner’s name, address, description of the house and whatever I wanted to ask them as a phone contact if they called back later (“.. sorry, which house are you referring to?.. uhh I can’t remember what I wanted to know about it.”🤦).&lt;/p&gt;&lt;h3&gt;Touring&lt;/h3&gt;&lt;h4&gt;Touring To Apply&lt;/h4&gt;&lt;p&gt;As I’ll mention again below, at every step of the process you should be trying to get in line with a completed application as soon as possible. When you tour, ask the owner or property manager to send you an application while you’re physically present to avoid hours of wall time communicating.&lt;/p&gt;&lt;h4&gt;Tour As Early As You Can&lt;/h4&gt;&lt;p&gt;On one occasion, we missed out because a group had been able to tour (and thus get an application) in the morning a few hours before we were able to. Because they were able to tour first and thus apply first, they got the house and we didn’t.&lt;/p&gt;&lt;h4&gt;Questions To Ask On A Tour&lt;/h4&gt;&lt;p&gt;Before you tour, or while on your tour, make sure to ask:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Does the owner use part of the property for storage? More than one house had this situation which we didn’t like.&lt;/li&gt;&lt;li&gt;Did the previous tenants have cats/dogs/other allergens?&lt;/li&gt;&lt;li&gt;How many others are in front of us in the application process? If there are more than five, it’s unlikely you’ll be able to sign a lease.&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Applying&lt;/h3&gt;&lt;p&gt;Applying to rent a house was just as nuanced as sourcing leads.&lt;/p&gt;&lt;h4&gt;Reusable Applications and Seattle’s First In Time Law&lt;/h4&gt;&lt;p&gt;Zillow and similar big listing services provide re-usable applications. Prospective tenants provide typical application information just once, a $30-or-so fee, then the listing company combines that with a credit and background screening. An owner gets an easy thumbs up or thumbs down. Tenants don’t have to fill out the same info over and over.&lt;/p&gt;&lt;p&gt;From the perspective of both an owner and renter, it seems pretty convenient at first glance.&lt;/p&gt;&lt;p&gt;In 2019, Seattle passed the &lt;a href="https://www.seattle.gov/civilrights/civil-rights/fair-housing/first-in-time"&gt;First In Time&lt;/a&gt; law which requires owners to “…offer tenancy to the first qualified applicant who provides a completed application.” Combine this with re-usable applications, however, now as a tenant you have a huge incentive to lurk on Zillow and Apartments.com and slam Apply immediately when houses show up with no additional cost per application. It even felt automated in some cases based on how fast I saw a few listings go from 0 to 100 contacts.&lt;/p&gt;&lt;p&gt;On more than one occasion I would hear my email notification chirp, spend 10–30 minutes thinking about the house, neighborhood, etc, only to find that a dozen people had already applied within an hour of the house going up. Regardless who showed up to a tour first or is ready to sign a lease first, whoever was first to press Apply is given the first shot at the house.&lt;/p&gt;&lt;p&gt;At minimum, I wish re-usable applications required a small fee per property to discourage this type of application squatting behavior.&lt;/p&gt;&lt;h4&gt;Expect Mixed Results, But Still Worth It&lt;/h4&gt;&lt;p&gt;Anecdotally, only about one in eight listings accepted a re-usable application. We had a property manager tell us frankly that we wasted our money.&lt;/p&gt;&lt;p&gt;However, individual owners (especially) interpret this law strictly, probably from a lack of experience, while property management companies avoid the reusable application option entirely as they have their own process.&lt;/p&gt;&lt;p&gt;In our search I had two different owners tell me regardless of what was included in the reusable application, they had to offer the house to who had even submitted a request for application first (which doesn’t match my interpretation of the law, but that’s what they were doing).&lt;/p&gt;&lt;h4&gt;Have Application Documentation Prepared Before You Start&lt;/h4&gt;&lt;p&gt;To avoid fumbling around on your bank’s website while racing others to complete an application for Your Perfect House, have the below documentation ready, printed to PDFs and saved somewhere convenient before you‘re even seriously looking to rent.&lt;/p&gt;&lt;p&gt;I am positive we missed at least one house because I didn’t have all our documentation immediately at hand.&lt;/p&gt;&lt;p&gt;Owners requested this information over email in PDFs, so after the first few I just had a zip file I’d send with everything they could want.&lt;/p&gt;&lt;p&gt;Make sure you black out your social security number and account numbers on the below documentation unless asked otherwise. More than likely the owner will use a third party service like TransUnion to do a background or credit check that can more securely collect that information than floating documents over email.&lt;/p&gt;&lt;p&gt;Across the applications we filled out, we were asked to provide some combination of the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Last two months of pay stubs&lt;/li&gt;&lt;li&gt;Gross monthly income&lt;/li&gt;&lt;li&gt;Net monthly income&lt;/li&gt;&lt;li&gt;Last two years of W2s&lt;/li&gt;&lt;li&gt;Last six months of paid rent statements&lt;/li&gt;&lt;li&gt;Most recent account statements or balances proving 6x rent in cash is available in reserves&lt;/li&gt;&lt;li&gt;Government issued ID&lt;/li&gt;&lt;li&gt;A written positive referral from current landlord&lt;/li&gt;&lt;li&gt;Personal references, phone number and email (not friends)&lt;/li&gt;&lt;li&gt;Current employer’s phone number and email, and supervisor contact info&lt;/li&gt;&lt;li&gt;Five years of rental history, with landlord phone and email contact info, start and end dates (exact days), monthly rent, reason for leaving.&lt;/li&gt;&lt;li&gt;Written authorization to release referral information from current landlord&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;Be Prepared To Pay A 25% Rent Holding Deposit&lt;/h4&gt;&lt;p&gt;In my past experience with apartments, the only fees we needed to pay were the typical first month’s rent + security deposit, and that was only after signing a lease.&lt;/p&gt;&lt;p&gt;Every house we applied to required first month, last month, security deposit upon signing the lease, but also a big fee just to take the house off the market. &lt;a href="https://app.leg.wa.gov/RCW/default.aspx?cite=59.18.253"&gt;Washington law&lt;/a&gt; allows for a non-refundable deposit up to 25% of rent just to take the house off the market, which was about what every house we applied to required. This fee is applied towards rent if you sign, returned if you don’t qualify, but is forfeited if you abandon the house.&lt;/p&gt;&lt;h3&gt;Success!&lt;/h3&gt;&lt;p&gt;After a solid month of serious searching, spending a few hours every day looking at listings, calling owners, we finally secured a place this week and can call it quits!&lt;/p&gt;&lt;img alt="" height="1" src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=95bf190680b3" width="1" /&gt;</description><author>Stories by Zach Wolfe on Medium</author><pubDate>Tue, 25 May 2021 01:34:45 GMT</pubDate><guid isPermaLink="true">https://zdwolfe.medium.com/renting-a-single-family-home-in-seattle-95bf190680b3?source=rss-65d40a7904f------2</guid></item><item><title>Test du mini-PC MeLE Quieter2</title><link>/post/2021/05/25/test-du-mini-pc-mele-quieter2/</link><description>&lt;div class="ox-hugo-toc toc"&gt;
&lt;div class="heading"&gt;Table des matières&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/post/2021/05/25/test-du-mini-pc-mele-quieter2/#introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/post/2021/05/25/test-du-mini-pc-mele-quieter2/#design"&gt;Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/post/2021/05/25/test-du-mini-pc-mele-quieter2/#performances"&gt;Performances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/post/2021/05/25/test-du-mini-pc-mele-quieter2/#conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!--endtoc--&gt;
&lt;hr /&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Au premier coup d&amp;rsquo;oeil, le MeLE Quieter2 est un mini-PC doté de beaucoup de qualités : c&amp;rsquo;est une machine fanless (sans ventilateur), compacte, dotée d&amp;rsquo;une bonne connectique, le tout pour environ 200€.&lt;/p&gt;
&lt;p&gt;Équipé d&amp;rsquo;un Celeron J4125 et de 8 Go de RAM, ce petit ordinateur pourrait être le candidat idéal pour un serveur, un PC d&amp;rsquo;appoint voire une machine principale.&lt;/p&gt;</description><author>Accueil on dbeley</author><pubDate>Tue, 25 May 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">/post/2021/05/25/test-du-mini-pc-mele-quieter2/</guid></item><item><title>10 Technologies You Need to be Aware of (2020)</title><link>https://jonpauluritis.com/articles/10-technologies-you-need-to-be-aware-of/</link><description>&lt;blockquote&gt;
&lt;p&gt;&amp;quot;Predictions are hard. Especially when they're about the future.&amp;quot; -Yogi Berra&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A hobby of mine is to try to understand where things are going. I love knowing ahead of time what's going to happen - aka &amp;quot;the future is already here — it’s just not very evenly distributed.&amp;quot;&lt;/p&gt;
&lt;p&gt;Inherently I think we all know that there is an advantage to being ahead of the curve, but I like to challenge myself to take it up a notch and document my thinking at reasonable intervals (then review when the time comes up). &lt;strong&gt;This particular article is based on a speech I mostly wrote in 2019 and conducted in ~2020.&lt;/strong&gt; I'm turning the speech into an article at the request of my buddy Patrick Thurmond so forgive any lack of cohesion since they were originally just notes.&lt;/p&gt;
&lt;p&gt;(Note: this article is focused very specifically on developer/engineer software tools, frameworks, languages, and systems... meaning it will offer very little for laypeople. I plan to follow up with another article that will touch on larger technology trends that will be influenced by this piece)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#setting-the-stage"&gt;Setting the stage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#what-is-next-according-to-the-experts"&gt;According to the Experts...&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#my-10-year-general-predictions"&gt;General Predictions!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#vaporware"&gt;Vaporware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tech I'm on the fence about&lt;/li&gt;
&lt;li&gt;10 Technologies you need to be aware of&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Setting the stage.&lt;/h2&gt;
&lt;p&gt;Imagine the world back in 2014 ... MVC frameworks (Rails, Laravel, Django) are big, jQuery dominates the web development frontend (framework). &amp;quot;Big Data,&amp;quot; &amp;quot;Cloud Computing,&amp;quot; and the tools powering distributed computing are all the rage. Node.js is picking up steam, Swift is announced to great fanfare, meanwhile, AWS releases their Functions as a Service product (Lambda) and virtually no one notices. Oculus/ Whatsapp are acquired by Facebook and Satya Nadella becomes Microsoft CEO.&lt;/p&gt;
&lt;h3&gt;What would safe predictions look like in 2014?&lt;/h3&gt;
&lt;p&gt;Perhaps Node.js seems like it will be a big deal. Maybe Angular.js will replace jQuery? NoSQL might challenge SQL databases. Mobile is CRAZY hot and compile-to-native mobile frameworks might offer a path for web developers to offer mobile (making mobile even bigger)... and Apache Spark is a must-learn for any serious cloud practitioner.&lt;/p&gt;
&lt;p&gt;How safe would those predictions be? I would argue they're pretty &amp;quot;meh.&lt;/p&gt;
&lt;h2&gt;What is next according to the experts&lt;/h2&gt;
&lt;p&gt;According to &amp;quot;experts&amp;quot;, &amp;quot;venture capitalists&amp;quot;, &amp;quot;thought leaders&amp;quot;, etc the following are the big blocks that make up the future of technology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Artificial Intelligence/ Machine Learning!&lt;/li&gt;
&lt;li&gt;Robotics&lt;/li&gt;
&lt;li&gt;Neurotech (e.g. Nueralink)&lt;/li&gt;
&lt;li&gt;Biotech (e.g. Ginko Bioworks, 23andMe)&lt;/li&gt;
&lt;li&gt;Custom Microprocessors&lt;/li&gt;
&lt;li&gt;Quantum Computing&lt;/li&gt;
&lt;li&gt;New Energy storage&lt;/li&gt;
&lt;li&gt;Space&lt;/li&gt;
&lt;li&gt;IOT &amp;amp; embedded systems&lt;/li&gt;
&lt;li&gt;Health Tech&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(Note: if you notice, all of these technologies are expensive and will take a lot to move the needle on)&lt;/p&gt;
&lt;h2&gt;My 10 Year General Predictions&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I believe in having &amp;quot;skin in the game&amp;quot; when I make predictions, so these predictions also often represent my real financial positions. Meaning this isn't hot air and &amp;quot;thought leadership&amp;quot;... these are real bets that I have made wherever possible&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voice-controlled applications&lt;/strong&gt; will significantly grow in use and users (e.g. Alexa, Google, etc)- Why? 1billion people in Southeast Asia have to type in logograms... which is way WAY slower than talking to a smartphone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unmanned retail and/or unmanned warehouses&lt;/strong&gt; (dark warehouses) will grow significantly in use. Why? There seems to be a tipping point coming with labor dynamics that no one seems to be appreciating. A raise to the minimum wage could/will force the hand of Entrepreneurs to switch from people to technological solutions. Example: Amazon Specialties.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Electric vehicles (NOT Fuel Cell) will overtake Internal Combustion vehicles.&lt;/strong&gt; Why? Fewer moving parts (lower cost of ownership), Quieter, Don't require Gas or Petroleum infrastructure. Plus we're getting very close to a place where economies of scale for EVs equal or overtake ICE vehicles - purchase price, gas, insurance costs for ICE vehicles will start to go up as EV adoption increases. Lastly, there has been a move on the part of institutional investors to pull &amp;quot;Energy Companies&amp;quot; out of their portfolios - this is a non-trivial change in the economic environment of ICE vehicles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Death of Traditional Retail&lt;/strong&gt; (but NOT all retail) will occur. Why? Large retail spaces with premium rent for prime commercial locations made a lot of sense when consumers were able to get the largest/best selection from these large retail locations (AKA drive from far away to a central car-focused retail center). The unit economics are ceasing to make sense though as ecommerce (Amazon) has better selection with lower prices (and no driving!). I would expect some companies to transition to mixed digital/retail models and/or experience-focused retail experiences but not all companies. Death is probably a strong word because I don't think it will be very &amp;quot;visible&amp;quot;, but JC Penny employed more people than the entire coal industry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drone delivery will become a thing.&lt;/strong&gt; This is HIGHLY dependant on regulation... but I feel like it will come through, the Unit economics make a LOT of sense in urban/suburban environments. I imagine it will be kind of like Waymo at first- restricted to a few cool cities (till 2030) but will be generally available by 2040.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ag-Tech will add tools such as precision weeding, ML in agriculture, hydroponics/ Urban/ vertical Farms&lt;/strong&gt; - Few people outside of agriculture are aware of what GPS did for crop yields. The next set of technologies could have a similar impact, and a nice bonus would be the decrease in pesticides that make it into the ecosystem&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nano Satellites for the &amp;quot;other 2 billion people&amp;quot;&lt;/strong&gt; -  Elon Musk built perhaps the largest (&lt;em&gt;barely talked about&lt;/em&gt;) business that may have ever existed with Starlink. My prediction is that a non-trivial amount of people end up using it for their internet before 2030.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ImmunoTherapy&lt;/strong&gt;- though I'm really surprised this hasn't had faster adoption I still feel strongly about treating cancer with immunotherapy. I know a bunch of people that head down to Mexico for Stem Cell and Immunotherapy- so it's started but I'm still genuinely pissed off that it hasn't become a full-fledged offering to Americans right now.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Vaporware&lt;/h2&gt;
&lt;p&gt;The following is a list of things that I do not believe will materialize.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hands down the biggest waste of R&amp;amp;D dollars I can think of are &lt;strong&gt;flying cars&lt;/strong&gt;. Energy isn't cheap enough to warrant adoption, it's an FAA nightmare, but mostly there are a vast number of other technologies easier to implement than flying Cars.&lt;/li&gt;
&lt;li&gt;There's been a lot of hype around this generation's &lt;strong&gt;Virtual Reality&lt;/strong&gt; iteration, but I think it will again be vaporware. Why? It's fighting for dollars in the already heavily invested entertainment segment as opposed to trying to pry dollars away from businesses. The main turning point in my decision making was that John Carramack resigned from his CTO position from Oculus, citing he didn't think VR would end up where he wants it to in his lifetime. That guy is one of the smartest people in computing... and if he thinks it ain't going to happen I trust him.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;Smart&amp;quot; Cities.&lt;/strong&gt; (e.g. Google in Toronto; Bill Gates, New Mexico). So billionaires love the idea of smart cities, but they always fail on the premise that a city is primarily a people thing not an infrastructure setup. Personally, I can't help but think about Disney's Epcot Center which was originally supposed to be the &amp;quot;city of the future&amp;quot; until Walt Disney died.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Hyperloop project&lt;/strong&gt;. Not a lot to say about it... but I will say I don't think its much of an improvement over MagLev. projects or High Speed rail - both of which were conceptualized ~2 generations ago and can't even be built as a prototype in the United States.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Universal Basic Income&lt;/strong&gt; - I believe this warrants a full-fledged article or series itself, but the TLDR; is that none of our current major forms of civilization are equipped to implement such a thing. I have yet to hear a fiscally conservative means of achieving it, so I will retain my skepticism for at least 20 years.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Food Service Robotics&lt;/strong&gt;. Zume Pizza made big waves with it's $300M+ investment (note: since my original speech Zume has actually gone out of business. +1 to me) and there are at least 2+ Robot  coffee shops built in the last ~5 years (I've tried one... and it was a great cup of coffee IMO)... and yet none of these things represent novel technological improvements. Zume Pizza specifically could have been built in the Early/Mid 1800s from what I can tell with the technology that existed at that time. Burger flipping Robots, Coffee, Sandwiches, etc are all things that historic automation could handle and yet we chose not to, in lieu of human labor. I'm pretty confident that it will be a while before someone is overtaking Domino's model.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Where I'm On the Fence...&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;K8s (Kubernetes) and Docker.&lt;/strong&gt; As I understand it the Docker/K8s is the cheapest way to do horizontal scaling and distributed computing... but they come with a major cost of HIGH complexity and they require specialized (expensive) engineering talent to keep the software entropy from eating a project. BUT they are backed by Google and Y combinator. Also worth mentioning Docker might not be financially viable with an open source model. Basically, my heart says companies/engineers will want the control but my head says:  the major cloud providers provide the infrastructure. (&lt;em&gt;Important note: over the last year I have reversed my opinion about Docker/ K8s. I now believe they fall into the vaporware category since IaaS provides superior financials for the majority of companies.&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ruby (Programming Language)&lt;/strong&gt; - Over the last few years Ruby has seen a major decline in bootcamp programs (of course bootcamps have also seen a decline) and overall language prevalence. It has been the last major language to get infrastructure provisions (AWS, Google, Microsoft, etc) but still has a solid opensource offering - Sidekiq, Rails, Sinatra, Jekyll. Seems like a coin toss in my book on whether the language will prevail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockchain Technologies.&lt;/strong&gt; There are a ton of companies playing in this space with a lot of investment. Notable companies include: Brave, Defi, Square Cash, Coinbase... but the Libra Project was basically shut down by congress AND there are HUGE energy consumption considerations- E.G. China stepped in and stopped miners. I feel like the benefits of Blockchain technologies outweigh the negatives... but it's more of a gamble on whether projects can get their energy consumption under control. (2021 Note: I still feel like blockchain technologies are on the fence but I am starting to see governments interested in controlling the technology. I believe this actually bodes well for Blockchain tech but it still has a long way to go I would love to see a useful commercial application start picking up)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Augmented Reality&lt;/strong&gt; - AR has more uses than VR, but more importantly I actually believe there are more &lt;strong&gt;commercial&lt;/strong&gt; opportunities for AR technology than VR. With that said, developers working on AR problems are still focused on the consumer markets and I'm not seeing enough movement to remove my skepticism. Of course the hardware is less expensive than VR, and there are more business-related uses for AR that I'm leaning towards higher adoption.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;10 Technologies you need to be aware of...&lt;/h1&gt;
&lt;h2&gt;1. Infrastructre as a Service&lt;/h2&gt;
&lt;p&gt;5 years ago we would be talking about what &amp;quot;Stack&amp;quot; you work in... I think those types of discussions are moving towards infrastructure and tooling. Of course, this is kind of &amp;quot;Duh&amp;quot; since everyone is on AWS already right? Yes and no... companies still seem to be managing a lot of stuff themselves still... which leads me to believe that companies that are willing to outsource these infrastructure tasks will ultimately gain market share. The same goes for Developers/ Engineers that learn how to piece Infrastructure components together.&lt;/p&gt;
&lt;h3&gt;Why you should care?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;It's a massive productivity boost&lt;/strong&gt; You no longer need to setup your own authentication, configure load-balancers, or split out read/write instances of a database. Someone did it for you and did it better than you. &lt;strong&gt;AND it's cheaper&lt;/strong&gt; - No one seems to like doing the math but DevOps engineers cost &lt;s&gt;a lot&lt;/s&gt; fuck ton of money. IaaS is extremely cheap until you are &lt;em&gt;really&lt;/em&gt; using it. (note: I am even more bull on IaaS than I was when I wrote this speech... and in a related note the costs for IaaS have come down substantially so I feel like it is no longer an advantage to configure your own infrastructure)&lt;/p&gt;
&lt;h2&gt;2. Machine Learning (isn't a fad)&lt;/h2&gt;
&lt;p&gt;Artificial Intelligence and Machine Learning had a moment recently where they became incredibly sexy. Hell, when you can sell a shitty sci-kit learn implementation for a $300 Million valuation... some heads will naturally turn... but underneath the &amp;quot;sexy&amp;quot; exterior there are real solutions that the technology can solve. That doesn't mean it's all kittens and sunshine. In my book there are two completely different AIs at work: the &amp;quot;AI&amp;quot;/&amp;quot;ML&amp;quot; that will do something useful for you and the &amp;quot;AI&amp;quot; the sales guys are selling. The stuff the sales guys are selling is going to go the way of the dodo... but the stuff that helps classify and makes edge cases easier to solve... I believe fully in that technology. Examples of Machine Learning utilities that are incredibly useful right now: recommendation systems, anomaly/ fraud detection, Image/facial recognition systems, Translation systems, etc&lt;/p&gt;
&lt;h3&gt;Why you should care?&lt;/h3&gt;
&lt;p&gt;Classical Machine Learning Algorithms  (e.g. Scikit Learn), Tensorflow, Pytorch, etc are all going to &lt;strong&gt;increase productivity&lt;/strong&gt;. Sure there is a lot of bullshit hype around &amp;quot;AI&amp;quot; but there's a reality that programming rules that can be inferred by an algorithm is just more efficient than having a person try to code the same thing.&lt;/p&gt;
&lt;p&gt;##3. Serverless / FaaS&lt;/p&gt;
&lt;p&gt;FaaS are an aspect of IaaS (see above), but I feel that they are owed their own section because they are such a big deal. Functions as a Service offer a unique value proposition that you can just worry about code (business logic) and nothing else. Why does that matter? &lt;strong&gt;Because Code is a liability, Software is an asset.&lt;/strong&gt; Software you own, where you can externalized the maintenence costs, will ultimately provide companies the most value for their money (assuming all other factors are equal). This is why FaaS are SOOOOOOOOOOO powerful. &lt;strong&gt;You just need less code.&lt;/strong&gt; You don't need a framework... you don't need routing, middleware, additional auth systems, etc. You need fewer dependencies... fewer everything really.&lt;/p&gt;
&lt;p&gt;Looking for more reasons? If you are doing a serverless stack (FaaS) you don't need (or you need fewer) dedicated devops engineers (who are, as previously mentioned, very expensive). Furthermore, it's effectively infinte scale for very little money.&lt;/p&gt;
&lt;h3&gt;Also a BIG deal: Cloudflare Workers&lt;/h3&gt;
&lt;p&gt;Important sidebar - there is one serverless product that seems to stand out against the others based on its performance and cost: &amp;quot;Cloudflare Workers&amp;quot;. Cloudflare workers function similarly to any other Serverless/ FaaS product the only difference is that they use V8 isolates rather than containers (Note: which only support JS/ WASM). It turns out that this makes workers 441% &lt;strong&gt;faster&lt;/strong&gt; than AWS (according to smashing mag) and the cheapest FaaS available (3x Cheaper than AWS). Additionally, because Cloudflare's primary offerings are security and CDN related Workers provide the highest availability- lowest latency around (thanks to their 165 data centers)&lt;/p&gt;
&lt;h2&gt;4. NewSQL&lt;/h2&gt;
&lt;p&gt;Databases have a problem... actually, they have two problems: 1) There is a LOT of innovating left to do in perhaps the most lucrative area in software ...and in full dramatic irony, 2) SQL is perhaps the stickiest technology ever to have been built in software. Over the last ~10 years there has been an incredible increase in the velocity and deployment of data persistence technologies (Spark/HDFS, NoSQL, Column stores, Graph Databases, key-value stores) and each of these technologies has its own very useful niche. Yet, most developers that I know still default to a good old Relational Database Management System when they build a new project. The NewSQL movement seems to be mostly about this. Databases like &lt;a href="https://www.openmymind.net/Migrating-To-CockroachDB/"&gt;CockroachDB&lt;/a&gt; are trying to innovate in the data persistence space while making adoption simpler with good ole SQL as their query language.&lt;/p&gt;
&lt;h3&gt;Why you should care?&lt;/h3&gt;
&lt;p&gt;(&lt;em&gt;As always, if you're doing a simple CRUD app/API or doing basic analytics just use Postgres - it could be the most boring technology in the world. Which more than likely makes it perfect for &lt;strong&gt;your&lt;/strong&gt; application.&lt;/em&gt;) For people/companies that are trying to get the most out of their technology and/or are doing something novel... use the right tool for the job. NewSQL technologies might be that tool. As an example, Cockroach DB provides better availability and automated sharding while still offering the familiarity of SQL for a query language. Column Stores such as Redshift or BigQuery offer a major speed increase for working with big data.&lt;/p&gt;
&lt;h2&gt;5. SSGs &amp;amp; JAM Stack&lt;/h2&gt;
&lt;p&gt;So let me start by saying I think JAM stack is the world's lamest name- I in the camp where I don't feel we need a 3 or 4 letter acronym every time we string together a couple of pieces of software.&lt;/p&gt;
&lt;p&gt;...but now that we've gotten that out of the way. &lt;strong&gt;&lt;em&gt;Everything that was old is new again&lt;/em&gt;.&lt;/strong&gt; The JAM stack and Static Site Generators are effectively a new spin on our old favorite technologies (Javascript, CSS, APIs, and Markup). Put another way, it's a movement to utilize our most basic web technologies as a sort of software minimalism (but with new tooling).&lt;/p&gt;
&lt;p&gt;I am a BIG fan of this kind of minimalism, but it turns out I'm not the only one. Several products such as Netlify, Cloudflare Workers Sites, and Firebase have all taken advantage of new technologies to allow developers to easily work in the JAM stack. At the same time the opensource community has embraced these new paradigms by growing the foundational technologies. SSGs like Jekyll, Gatsby, Metalsmith or Eleventy, Frontend frameworks like Vue, React, or Svelte have all become quite mature and are frequently used as the core technologies for modern applications&lt;/p&gt;
&lt;h3&gt;Why you should care?&lt;/h3&gt;
&lt;p&gt;JAM stack and SSGs offer Performance improvements over traditional backends, Productivity improvements via simpler deployments but best of all it is Cheaper!&lt;/p&gt;
&lt;h2&gt;6. Rust&lt;/h2&gt;
&lt;p&gt;Rust is a low-level, performant language developed and maintained by Mozilla (2021 Note: Rust has been split out into its own foundation and is even being considered as the second language for the Linux Kernal. Quite a lot of moment in the last year). Rust is type-checked, memory safe without garbage collection, and extremely performant. The language places a high value on quality error messages and has a killer ecosystem. It is fast becoming the default choice for low-level development in all sorts of technological areas.&lt;/p&gt;
&lt;h3&gt;Why should you care?&lt;/h3&gt;
&lt;p&gt;Well its been Stack Overflow’s most loved language for four years in a row (via the stack overflow survey), it's elegant, but performant and is useful for other stuff that's coming up in the next couple sections of this article.&lt;/p&gt;
&lt;h2&gt;7. WASM/ Web Assembly&lt;/h2&gt;
&lt;p&gt;WebAssembly is a new type of code that can be run in modern web browsers and it provides low-level access to the browser/ V8. It is designed to be an effective compilation target for low-level source languages like C, C++, Rust, etc.&lt;/p&gt;
&lt;p&gt;(A quick note about performance/ speed: For the bulk of my career I never understood why people cared so much about the speed/ performance of their software. Over the last few years though it has become incredibly clear why it matters. Speed and performance are the ultimate empathy for the users of your software. It takes a great amount of technical skill and forethought to be able to provide performant software, as well you need to care about EVERYONE ... which can be a big challenge at scale. Providing your users a quick, snappy experience is one of many things that will make using your software enjoyable and consequently more useful as a whole. Therefore technologies like WASM are valuable because they make it easier to be empathetic to your users)&lt;/p&gt;
&lt;h3&gt;Why should you care?&lt;/h3&gt;
&lt;p&gt;Um... Crazy performance improvements and it adds C, C++, &amp;amp; Rust to the list of languages that can work for the browser.&lt;/p&gt;
&lt;h2&gt;8. Deno&lt;/h2&gt;
&lt;p&gt;Deno is a Typescript runtime written in Rust. What is Deno really? It's an improvement and a replacement for Nodejs. Deno offers better security, a standard library, a module system that uses URLs and decreases dependency fanning, and is delivered in a single executable file so it's awesome for Serverless!&lt;/p&gt;
&lt;h3&gt;Why should you care?&lt;/h3&gt;
&lt;p&gt;Deno was created by Ryan Dahl and it's going to replace Node and become the default environment for building applications. Period. Full stop.&lt;/p&gt;
&lt;p&gt;I could (and might) write a pretty massive article on just this topic, but I am just going to go all cryptic and say you should check this out for yourself.&lt;/p&gt;
&lt;h2&gt;9. Entropic&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;(2021 Note: I took a little bit of a risk when I threw Entropic on this list and it turned out that I have to eat crow on this one. C J Silverio has been a big deal in open source for a long time and knows how to publish. Unfortunately, it looks like my intuition failed and the project was eaten by entropy)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Entropic is a federated package registry and CLI created by C J Silverio previously of the NPM company. The federated part is the interesting part- It mirrors all packages that users install from the legacy package manager - meaning there can be multiple copies out there but a single source of truth.&lt;/p&gt;
&lt;h3&gt;Why you should care?&lt;/h3&gt;
&lt;p&gt;Entropic offers an improvement over NPM and YARN as removes control from central entities while maintaining secure transport of packages. In my opinion, the really big deal about this is that there is a movement towards taking back the web from the mega-corps.&lt;/p&gt;
&lt;h2&gt;10. Apache Arrow&lt;/h2&gt;
&lt;p&gt;Apache Arrow is a column memory format organized for efficient analytics operations on modern hardware.&lt;/p&gt;
&lt;p&gt;I was previously a little &amp;quot;meh&amp;quot; about the project until learned that Wes McKinney is the original author and leading development efforts. Wes is a prolific software developer both in the commercial and engineering sense (the guy builds great software and great companies)- so that carries some weight with me. Of course, one developer wouldn't validate a technology by themselves, but as I started digging I began to see the problem that they are solving is somewhat akin to the reason why Pandas exists, but the approach is lower level and very much needed for the future of data analytics.&lt;/p&gt;
&lt;h3&gt;Why you should care?&lt;/h3&gt;
&lt;p&gt;If you are doing data analytics and/or need to build a proof of concept for something that you would love to do in memory... Arrow might make a big difference in your life coming up.&lt;/p&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;The above technologies are my best guess for how things will shake out. I think its worth noting that I believe there is an underlying theme to all technological movements (aka the things that make up the future):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The technology is actually superior in a way that makes a difference over the status quo. (conceptually related to &amp;quot;the innovator's dilemma&amp;quot;)&lt;/li&gt;
&lt;li&gt;There is usually a voice or a narrative that helps the rest of us move forward.&lt;/li&gt;
&lt;li&gt;There are people involved that we are willing/wanting to put our faith in.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...with that said, even if a technology has these aspects, it is far from a guarantee that it will proliferate. There are many externalities that factor in and they are impossible to predict. So in the least, be aware. These technologies might the future, or they might show you &amp;quot;what not to do&amp;quot; so you can better predict the future for yourself.&lt;/p&gt;</description><author>JonPaulUritis.com</author><pubDate>Mon, 24 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://jonpauluritis.com/articles/10-technologies-you-need-to-be-aware-of/</guid></item><item><title>Abolishing vendor lock-in</title><link>https://nutcroft.mataroa.blog/blog/abolishing-vendor-lock-in/</link><description>&lt;p&gt;Vendor lock-in one of the saddest feelings when using software.&lt;/p&gt;
&lt;p&gt;It should be a calculated risk but usually it isn’t and thus ends up becoming an unforeseen limitation. It is a feeling of entrapment, a prison of sorts. “If only we could move our database to X tomorrow, &lt;em&gt;just like that&lt;/em&gt;”—with a finger snap.&lt;/p&gt;
&lt;p&gt;But even if it is calculated from the beginning, there is a yet better scenario; to escape it completely.&lt;/p&gt;
&lt;p&gt;One of the core ideas behind &lt;a href="https://mataroa.blog/"&gt;mataroa.blog&lt;/a&gt; (the blogging platform I’m building) is that users should be able to leave as easy as possible and self-host their blog. The goal is to &lt;a href="https://mataroa.blog/modus/operandi/"&gt;empower personal independent blogs&lt;/a&gt; in any case.&lt;/p&gt;
&lt;h2 id="two-steps-towards-vendor-independence"&gt;Two steps towards vendor independence&lt;/h2&gt;
&lt;p&gt;The first step for vendor independence is for a vendor-official exit to merely exist. For online platforms, it might be hard to do that well. Every platform is different and there is no interoperability. You can’t take all your Twitter tweets and transform them into Facebook posts, for example.&lt;/p&gt;
&lt;p&gt;With blogging things are a bit easier. For example, you &lt;em&gt;can&lt;/em&gt; import your WordPress blog posts into Medium.&lt;/p&gt;
&lt;p&gt;With that in mind, we build export functionality in mataroa.blog from the beginning. One can export their blog posts into simple markdown text files, or into &lt;a href="https://www.getzola.org/"&gt;Zola&lt;/a&gt; sources, or &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; sources. Static site generators are one of the best ways to host a blog, which is also the reason of offering these export choices.&lt;/p&gt;
&lt;p&gt;The second step after making it possible is to make it easy.&lt;/p&gt;
&lt;p&gt;That’s why the “redirect to a new blog domain” feature exists. Suppose you decide to start a blog at &lt;code&gt;thoughts.mataroa.blog&lt;/code&gt;. After a few posts, you decide you want to move to your own domain and theme. You want Google and whoever comes across these old tweets that point to your old URL to be able to see what you wrote. &lt;/p&gt;
&lt;h2 id="third-step"&gt;Third step&lt;/h2&gt;
&lt;p&gt;I realised recently what’s a good third step: &lt;strong&gt;minimise the risk of losing vendor independence&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It’s possible for users to leave mataroa using the export feature. And it is easier to do so with the redirect feature. But, what if these things stop working?&lt;/p&gt;
&lt;p&gt;This is where a new feature comes in: &lt;a href="https://hey.mataroa.blog/blog/monthly-auto-exports-via-email/"&gt;monthly auto-exports via email&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Every month an email goes to all users that have it enabled. This email has one attachment: &lt;strong&gt;a zip archive of all your blog posts in markdown format&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Providing the ability to take backups is never enough. Users need to be convinced to store these backups too. If they are convinced to do that, then the risk of data loss is significantly mitigated.&lt;/p&gt;
&lt;p&gt;In this way we don’t need to convince users! We can safely store a user’s blog on their side with the simple action of periodically emailing them the backup.&lt;/p&gt;
&lt;p&gt;Surely, there is a limit on the amount of data that can fit in an email, though, right? There is, and it’s 50MB for Gmail. That’s pretty high for text though and &lt;a href="https://mataroa.blog/"&gt;mataroa.blog&lt;/a&gt; is a text focused platform.&lt;/p&gt;
&lt;p&gt;But whatever the case, a good first step.&lt;/p&gt;</description><author>nutcroft</author><pubDate>Mon, 24 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://nutcroft.mataroa.blog/blog/abolishing-vendor-lock-in/</guid></item><item><title>Hiring, interviews and corporatism in tech</title><link>https://world.hey.com/joaoqalves/hiring-interviews-and-corporatism-in-tech-1a192e54</link><description>&lt;div class="trix-content"&gt;
  &lt;div&gt;Each year there's an ongoing discussion about tech interviewing processes. It's almost like Christmas. Once a year, there we go. That's not new for experienced people in the sector, but it may surprise folks that work in other areas.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Can they do the job?&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The software development industry is relatively new, and it's disconnected from most Computer Science curricula. We learn calculus, physics, algorithms, data structures, and whatnot. Yet, there's not much emphasis on shipping production-ready software or working in a multidisciplinary team to create a mobile app.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some years ago, there weren't many distributed systems courses. Nowadays, most companies are building them. The same is true for mobile development. However, we shouldn't convert universities into market-driven machines. Universities should be knowledge's Cathedrals. They should drive students to understand things from the basics and learn how to learn.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That said, this creates a problem:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote&gt;How can we assess if that person can do the job?&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That's the Saint Graal for software-first organizations. Some common ways companies conduct interviews are:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;Take-home projects where a candidate has a few days to build a project, such as a REST API and a small client.&lt;/li&gt;&lt;li&gt;To answer quizzes on coding, networking, distributed system, operating systems, and other fundamentals.&lt;/li&gt;&lt;li&gt;Pair-programming a kata or fixing a minor bug.&lt;/li&gt;&lt;li&gt;System-design interviews. The candidates design high-level, known applications like Uber, Twitter, etc. Then, the interviewers add more constraints or scalability features.&lt;/li&gt;&lt;li&gt;Code reviews of existing company's code or the candidate's decisions made on a take-home project.&lt;/li&gt;&lt;li&gt;To solve coding puzzles involving algorithms and data structures. Usually, in a one-on-one whiteboard interview or platforms such as &lt;a href="https://codeinterview.io"&gt;codeinterview.io&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Generally, there's a mix of this kind of interview in a process with two to four steps. There are many criticisms to almost all of the above. Nevertheless, it isn't possible to optimize all the variables at the same time:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;"An agile process that doesn't take eight hours from the candidate. They have a life and shouldn't spend all these hours in a take-home exercise."&lt;/li&gt;&lt;li&gt;&amp;nbsp;"A non-whiteboard exercise, because people get nervous and it's not representative of day-to-day work."&lt;/li&gt;&lt;li&gt;"Better not be a coding exercise because there's more to software engineering than code."&lt;/li&gt;&lt;li&gt;"Pair-programming is complicated because it creates a disadvantage for introverts."&lt;/li&gt;&lt;li&gt;"It shouldn't have many interviews. Also, it must avoid biases."&lt;/li&gt;&lt;li&gt;"Let's not create a bar exam and a professional association to recognize people's abilities to perform a job. That would impede people without a degree to get a job in the industry."&lt;/li&gt;&lt;li&gt;"It shouldn't generate a lot of bad hires because using the probation period is dangerous. It generates too much rotation and lowers the morale."&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While I sympathize with some of the above points, we can't have the cake and eat it too. In the best-case scenario, we can optimize for the company's culture or the taste/culture of a percentage of people we believe are our targets in the market. Having a process that caters to the majority of developers is a delusion.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Exams, professional associations, and corporatism&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some folks in the software industry think that having a "bar exam" and a professional association could reduce the effort we put into interviews. Studying "&lt;a href="https://www.crackingthecodinginterview.com/"&gt;Cracking the coding interview&lt;/a&gt;" and preparing for whiteboard or system design interviews is time-consuming. If we're applying to a &lt;a href="https://www.urbandictionary.com/define.php?term=FAANG"&gt;FAANG&lt;/a&gt;-alike company, depending on our background, it can take up to hundreds of hours.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One could say that the upside of it is well worth the time. There are exams to access public sector jobs — nurses, police officers, etc. — in Spain and other European countries. These also require a lot of time to prepare with salaries that are not near what big tech companies pay.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The good part of our industry is that it allows people with a Philosophy degree to access a good-paying job. It matters what you can demonstrate you can do rather than a piece of paper. I think that's beautiful. Promoting a corporatist approach to solve the problem is promoting gate-keeping and ensuring high-paying gigs for those already in the sector.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;A way out&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Is there a way out of corporatism and long, time-consuming processes that don't reflect our daily work? It depends on your pipeline. If you work for Facebook and you receive thousands of candidates every week, maybe they have the leverage to filter candidates. You may require whiteboard interviews, all the interviews on the same day, and whatnot.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For startups or mid-sized companies, maybe there's a way out. I especially like this &lt;a href="https://gist.github.com/javisantana/42b96b41ba5a4de8fdce925b11074c9c"&gt;interview&lt;/a&gt; shared by &lt;a href="https://twitter.com/javisantana"&gt;Javi Santana&lt;/a&gt;, Tinybird's founder. It requires the candidate to explain how they'd approach the problem and get a good understanding of their written communication skills. It also creates a good starter for a deep conversation during an interview. It doesn't require much effort since it's more a system design approach than a coding exercise.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another way out is to create different processes and let applicants choose one. It can be a pair-programming kata, a take-home test, code-review, etc. There are two tricky parts to doing so. The first is how to keep the bar consistent across people that choose different paths. The second one is that instead of an interview process, you'd have several. It raises the cognitive load for your interviewers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Interview processes are full of trade-offs, like everything in our industry. We can optimize for some variables and make conscious choices, but no process will cater to everyone's taste.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;— João&lt;/div&gt;
&lt;/div&gt;</description><author>João Alves</author><pubDate>Sun, 23 May 2021 12:10:31 GMT</pubDate><guid isPermaLink="true">https://world.hey.com/joaoqalves/hiring-interviews-and-corporatism-in-tech-1a192e54</guid></item><item><title>The heartbeat of your startup</title><link>https://ferrucc.io/posts/throughput/</link><description>&lt;p&gt;The only thing that matters is throughput.&lt;/p&gt;
&lt;p&gt;Becoming the most prolific version of yourself is the most important thing you can do to become proud of your work.&lt;/p&gt;
&lt;p&gt;Making a group of people become the most prolific version of themselves sounds like a reasonable way to build a great company. So that&amp;rsquo;s what we&amp;rsquo;re trying to do.&lt;/p&gt;
&lt;p&gt;Throughput is the speed of getting to some outputs. The faster you get, the more things you do, and the more you build momentum.&lt;/p&gt;</description><author>ferrucc.io</author><pubDate>Sun, 23 May 2021 11:39:53 GMT</pubDate><guid isPermaLink="true">https://ferrucc.io/posts/throughput/</guid></item><item><title>I've reached the self-hosting endgame</title><link>https://ounapuu.ee/posts/2021/05/23/self-hosting-endgame/</link><description>If all you have is a powerful workstation...</description><author>./techtipsy</author><pubDate>Sun, 23 May 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://ounapuu.ee/posts/2021/05/23/self-hosting-endgame/</guid></item><item><title>Only record audio from specific applications with NVIDIA ShadowPlay</title><link>https://cmetcalfe.ca/blog/record-audio-from-specific-applications-with-shadowplay.html</link><description>&lt;p&gt;&lt;a href="https://www.nvidia.com/en-us/geforce/geforce-experience/shadowplay/"&gt;ShadowPlay&lt;/a&gt; is a feature of modern NVIDIA graphics cards that allows for recording games and
other applications at native resolution with only a minimal performance hit. It also includes some
genuinely useful features like instant replay.&lt;/p&gt;
&lt;p&gt;Unfortunately, the audio options for recording are pretty limited. By default, all audio is recorded
to a single mixed track. This includes the game audio, but also your mic, notification sounds, any
music you're playing in the background, people talking on Steam, Discord, etc.&lt;/p&gt;
&lt;p&gt;After some experimentation I eventually found a way to pick which applications to record the audio
from, but it's not intuitive at all so I'm documenting it here.&lt;/p&gt;
&lt;h1&gt;General idea&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Create a virtual audio device&lt;/li&gt;
&lt;li&gt;Play the audio from this device to the default audio device so you can hear it&lt;/li&gt;
&lt;li&gt;Force the applications you want to record to only play audio to this device&lt;/li&gt;
&lt;li&gt;Tell ShadowPlay to only record audio from this device&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Step by step guide&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://www.nvidia.com/en-us/geforce/geforce-experience/"&gt;GeForce Experience&lt;/a&gt; and enable the in-game overlay and hotkey in the settings. Press
   the hotkey to bring up the overlay. In the overlay on the right you will see a microphone icon.
   Click it to bring up a dropdown menu then click on "Settings". In the menu that appears,
   configure your microphone input and select the "Separate both tracks" option. This will cause
   your microphone audio to be recorded, but in a separate track from the game audio, making it easy
   to strip out or mix in later. You can also just use the single track mode and mute the mic
   instead if you never want to record yourself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://vb-audio.com/Cable/"&gt;Virtual Audio Cable&lt;/a&gt;. This will install two virtual sound devices on your system:
   "CABLE Input" (an output device) and "CABLE Output" (an input device). When sounds are played out
   the "CABLE Input" device, they can be recorded by listening to the "CABLE Output" device.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the "Sounds" control panel (Open the Control Panel, click "Hardware and Sound", then
   "Sound"), go to the "Recording" tab and find the "CABLE Output" device. Double click it to open
   its properties. Go to the "Listen" tab, check the "Listen to this device" option, and select the
   "Default Playback Device" as the target. This will allow you to hear any audio that is played by
   this device.
   &lt;img alt="Configuring the &amp;quot;CABLE Output&amp;quot; device in the sounds control panel" src="https://cmetcalfe.ca/images/listen_to_this_device.png" /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Launch all applications and games you want to record.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Sound mixer (Start menu, type "Sound mixer options"). You should see your open programs
   mapped to audio inputs and outputs. If you don't see a "NVIDIA Container" program listed then
   activate the GeForce overlay and toggle the "Instant Replay" setting on and off to make it
   appear. Change the "Output" of the "NVIDIA Container" application to the "CABLE Input" device.
   This tells ShadowPlay to only record the audio sent to this output.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the "Output" of each of the applications you want to record to "CABLE Input". I'm
   recording the audio from a game called &lt;a href="https://store.steampowered.com/app/563560/Alien_Swarm_Reactive_Drop/"&gt;&lt;em&gt;Alien Swarm: Reactive Drop&lt;/em&gt;&lt;/a&gt; in the example below.
   &lt;img alt="Recording the audio from Alien Swarm: Reactive Drop" src="https://cmetcalfe.ca/images/mixer_recording_asrd.png" /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start recording (or enable the instant replay recording mode)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The resulting video files will only include audio from the applications you selected in step 6. If
you opted to record your microphone in step 1, it will be in a separate audio track that you can
either remove or manually mix into the main audio later.&lt;/p&gt;
&lt;h1&gt;Notes&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Tested on Windows 10 21H1 running GeForce Experience v3.22.0.32&lt;/li&gt;
&lt;li&gt;Once this is all set up, the only thing you'll have to mess with on an ongoing basis is the mixer
   settings (steps 5 and 6)&lt;/li&gt;
&lt;/ul&gt;</description><author>Carey Metcalfe</author><pubDate>Sun, 23 May 2021 07:00:00 GMT</pubDate><guid isPermaLink="true">https://cmetcalfe.ca/blog/record-audio-from-specific-applications-with-shadowplay.html</guid></item><item><title>OCR bookmarks</title><link>https://xenodium.com/ocr-bookmarks</link><description>&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/schappim/macOCR"&gt;schappim/macOCR: Get any text on your screen into your clipboard.&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</description><author>xenodium.com @alvaro</author><pubDate>Sun, 23 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/ocr-bookmarks</guid></item><item><title>Comparing 3 Popular JavaScript Charting Libraries</title><link>https://www.tderflinger.com/en/comparing-3-popular-javascript-charting-libraries</link><description>Many websites need to visualize data in charts. This article compares three of the most popular open source charting libraries (Chart.js, Plotly.js and ECharts) from the perspective of the software developer.</description><author>Thoughts by Thomas Derflinger</author><pubDate>Sun, 23 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.tderflinger.com/en/comparing-3-popular-javascript-charting-libraries</guid></item><item><title>Open Source North 2021</title><link>https://benovermyer.com/blog/2021/05/open-source-north-2021/</link><description>&lt;p&gt;The first session was about neuroaesthetics and user experience. This was a great talk. It went into areas of neuroscience as they apply to user experience design, such as
gestalt pattern theory and high and low frequency visuals. I'll need to look into these topics in greater detail later so I don't lose this new information, but it was definitely a rich presentation that was interesting and useful. The speaker was excellent too, not just the topic. If you get the chance to hear Alyssa Pilney present on a topic, take it.&lt;/p&gt;
&lt;p&gt;The second I thought was going to be a Rust session, but that was moved to 3:00 pm. As a result I had to pick something else. I started with a talk about open source at schools, but that ended up just being a sales pitch for open source in general. I've seen that too often already. So, I switched to a talk on machine learning in music composition by Leif Bjornson. The machine learning session, unfortunately, had some assumptions about audience knowledge levels that precluded me. So I left the machine learning session on in the background and worked on other things, including this blog post. As it happened, I'm glad I left it on, because the session got really interesting after the technical portion. I started paying attention again shortly, and I learned about some really cool tools like Google Magenta and Musetree. I'll have to explore those in more depth sometime.&lt;/p&gt;
&lt;p&gt;At noon, I dropped into SPS Commerce's chat about running successful tech communities. It seemed more geared for how to build a tech community within a corporation, rather than an unaffiliated tech community. I was hoping for some ideas for running the DevOps learning community in SDG. It was not terribly useful.&lt;/p&gt;
&lt;p&gt;The one o'clock session was about observability. There were some good nuggets of wisdom in this talk; it was presented by a couple guys from Elastic. They suggested standardizing on a specific time zone (GMT), being careful to use NTP correctly, adding schema on writes instead of reads, and enriching logs with context whenever possible. They mentioned the &lt;a href="https://www.elastic.co/guide/en/ecs/current/index.html" rel="external"&gt;Elastic Common Schema&lt;/a&gt; as a good starting point. The last point I took away from it was that it's rare for a single event to be actionable; context determines action. After this, it went into what was essentially a sales pitch for Elasticsearch and Kibana. Until then, it was really informative.&lt;/p&gt;
&lt;p&gt;I skipped the &lt;a href="https://opentelemetry.io/" rel="external"&gt;OpenTelemetry&lt;/a&gt; session I'd planned to attend at 2:00pm. I was starting to get bored at this point. After a review of the other sessions available in that time slot, I decided to spend the time doing something more productive until the final
session.&lt;/p&gt;
&lt;p&gt;The final session I wanted to attend was a Rust adoption journey. However, I discovered that there was an Overwatch 2 developer livestream occurring at the same time, so I decided to catch up on the Rust session in a recording later. I was most excited about the Rust session out of all the sessions I'd planned to attend, but as it was being recorded and the devstream was not, I chose to watch the latter.&lt;/p&gt;
&lt;p&gt;All in all, it was a great small conference. I'm definitely looking forward to another in-person version in 2022. Next year I'll be more careful in my choice of sessions!&lt;/p&gt;</description><author>Ben Overmyer's Site</author><pubDate>Sun, 23 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://benovermyer.com/blog/2021/05/open-source-north-2021/</guid></item><item><title>Writing a Jinja-inspired template library in Python</title><link>http://notes.eatonphil.com/writing-a-template-library-in-python.html</link><description>&lt;p&gt;In this post we'll build a minimal text templating library in Python
inspired by Jinja. It will be able to display variables and iterate
over arrays.&lt;/p&gt;
&lt;p&gt;By the end of this article, with around 300 lines of code, we'll be
able to create this program:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pytemplate&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;eval_template&lt;/span&gt;

&lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'''&lt;/span&gt;
&lt;span class="s1"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;  &amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;  {&lt;/span&gt;&lt;span class="si"&gt;% f&lt;/span&gt;&lt;span class="s1"&gt;or-in(post, posts) %}&lt;/span&gt;
&lt;span class="s1"&gt;  &amp;lt;article&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;    &amp;lt;h1&amp;gt;{{ get(post, 'title') }}&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;    &amp;lt;p&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;      {{ get(post, 'body') }}&lt;/span&gt;
&lt;span class="s1"&gt;    &amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;  &amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;  {&lt;/span&gt;&lt;span class="si"&gt;% e&lt;/span&gt;&lt;span class="s1"&gt;ndfor-in %}&lt;/span&gt;
&lt;span class="s1"&gt;  &amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;span class="s1"&gt;'''&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Hello world!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'This is my first post!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Take two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'This is a second post.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eval_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That runs and produces what we expect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;test.py

&amp;lt;html&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;body&amp;gt;

&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;article&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;h1&amp;gt;Hello&lt;span class="w"&gt; &lt;/span&gt;world!&amp;lt;/h1&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;p&amp;gt;
&lt;span class="w"&gt;      &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;my&lt;span class="w"&gt; &lt;/span&gt;first&lt;span class="w"&gt; &lt;/span&gt;post!
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;/p&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;/article&amp;gt;

&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;article&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;h1&amp;gt;Take&lt;span class="w"&gt; &lt;/span&gt;two&amp;lt;/h1&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;p&amp;gt;
&lt;span class="w"&gt;      &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;second&lt;span class="w"&gt; &lt;/span&gt;post.
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;/p&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;/article&amp;gt;

&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All code is available on
&lt;a href="https://github.com/eatonphil/pytemplate"&gt;Github&lt;/a&gt;. Let's dig in.&lt;/p&gt;
&lt;h3 id="specification"&gt;Specification&lt;/h3&gt;&lt;p&gt;In this templating language, pytemplate, &lt;code&gt;{% $function () %}
... {% end$function %}&lt;/code&gt; blocks are specially evaluated depending
on the particular function being called. For example, the &lt;code&gt;for-in
($iter_name, $array)&lt;/code&gt; function will duplicate its children for
every element in &lt;code&gt;$array&lt;/code&gt;. Within the body of the loop, the
variable &lt;code&gt;$iter_name&lt;/code&gt; will exist and be set to the current
element in the array.&lt;/p&gt;
&lt;p&gt;While we won't implement it here, you can imagine what the &lt;code&gt;if
($test)&lt;/code&gt; block function might do.&lt;/p&gt;
&lt;h3 id="arguments,-expressions,-function-calls:-nodes"&gt;Arguments, expressions, function calls: nodes&lt;/h3&gt;&lt;p&gt;Function arguments are expressions (or &lt;code&gt;nodes&lt;/code&gt; as we'll
call them). They can be strings (surrounded by single quotes),
identifiers found in a provided dictionary (or
&lt;code&gt;environment&lt;/code&gt; as we'll call it), or nested function calls
(also called nodes).&lt;/p&gt;
&lt;h3 id="non-blocks:-tags"&gt;Non-blocks: tags&lt;/h3&gt;&lt;p&gt;The non-block syntax &lt;code&gt;{{ ... }}&lt;/code&gt; are just called tags. The
inside of a tag is a node and is evaluated the same way a function
argument is.&lt;/p&gt;
&lt;h3 id="architecture"&gt;Architecture&lt;/h3&gt;&lt;p&gt;We'll break up the library into a few main parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lexer for the node language&lt;/li&gt;
&lt;li&gt;Parser for the node language&lt;/li&gt;
&lt;li&gt;Lexer for blocks, tags, and text&lt;/li&gt;
&lt;li&gt;Parser for blocks, tags, and text&lt;/li&gt;
&lt;li&gt;Interpreter that takes an AST and an environment dictionary and produces text&lt;/li&gt;
&lt;li&gt;An entrypoint to tie all the above together&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'll tackle these aspects in roughly reverse order.&lt;/p&gt;
&lt;h3 id="entrypoint"&gt;Entrypoint&lt;/h3&gt;&lt;p&gt;When we call the library we want to be able to just accept a template
string and an environment dictionary. The result of the entrypoint
will be the evaluated template.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;eval_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;memfd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memfd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where lex, parse, and interpret have to do with the block- and
tag-level language.&lt;/p&gt;
&lt;h3 id="block,-tag-and-text-lexing"&gt;Block, tag and text lexing&lt;/h3&gt;&lt;p&gt;This process is responsible for turning the template string into an
array of tokens. To make the code simpler, lexing for the function
call and expression language is done separately. At this stage all
we'll look for is tokens consisting of block and tag end and beginning
markers. So
just &lt;code&gt;{%&lt;/code&gt;, &lt;code&gt;%}&lt;/code&gt;, &lt;code&gt;{{&lt;/code&gt;, &lt;code&gt;}}&lt;/code&gt;. If
a token is not one of these, it is regular text.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;BLOCK_OPEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{%'&lt;/span&gt;
&lt;span class="n"&gt;BLOCK_CLOSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'%}'&lt;/span&gt;

&lt;span class="n"&gt;TAG_OPEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{'&lt;/span&gt;
&lt;span class="n"&gt;TAG_CLOSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'}}'&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Handle escaping {&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;next_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                        &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;
                    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

                &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BLOCK_OPEN&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_char&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;TAG_OPEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="c1"&gt;# Handle escaping % and }&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

            &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BLOCK_CLOSE&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;TAG_CLOSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'cursor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it for lexing!&lt;/p&gt;
&lt;h3 id="block,-tag-and-text-parsing"&gt;Block, tag and text parsing&lt;/h3&gt;&lt;p&gt;Next up is a matter of finding the ending/closing patterns in the array of tokens. There are a few main rules we'll look for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every open tag symbol &lt;code&gt;{{&lt;/code&gt; must be followed by a text token then a closing tag symbol &lt;code&gt;}}&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;The text within the open and close tag must parse into a valid expression (we'll define this logic later)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Every block symbol &lt;code&gt;{%&lt;/code&gt; must be followed by a text token then an end of block symbol &lt;code&gt;%}&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;The text token within the open and close block must parse into a valid function call (we'll define this logic later)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Every block must have a matching end block where the text in the end block is &lt;code&gt;end&lt;/code&gt; concatenated to the beginning of the function being called in the start block&lt;ul&gt;
&lt;li&gt;The text between two blocks can contain nested blocks or tags&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let's codify that:&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end_of_block_marker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TAG_OPEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;TAG_CLOSE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Expected closing tag'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;node_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lex_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;node_ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'tag'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;node_ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TAG_CLOSE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Expected opening tag'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;BLOCK_OPEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;BLOCK_CLOSE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Expected end of block open'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;node_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lex_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;node_ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end_of_block_marker&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s1"&gt;'end'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;end_of_block_marker&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;node_ast&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;

            &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;node_ast&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cursor_offset&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Failed to find end of block'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'block'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;node_ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'child'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;cursor_offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;BLOCK_CLOSE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Expected start of block open'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's it for parsing blocks and tags. Now we have to get into the
node language.&lt;/p&gt;
&lt;h3 id="node-lexing"&gt;Node lexing&lt;/h3&gt;&lt;p&gt;In the node language, everything is either a literal or a function
call. Whitespace is ignored. The only special symbols in the node
language are commas and parentheses.&lt;/p&gt;
&lt;p&gt;So to break the text into tokens we just iterate over all characters
until we find whitespace or a symbol. Accumulate the characters that
are not either. Add everything but whitespace to the list of tokens.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lex_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'literal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;')'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'literal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

            &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'syntax'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's it for node lexing.&lt;/p&gt;
&lt;h3 id="node-parsing"&gt;Node parsing&lt;/h3&gt;&lt;p&gt;We'll break this up into two functions. The first is just for parsing
literals and function calls.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'literal'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Expected literal'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="n"&gt;next_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;next_t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;next_t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_node_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
            &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'function'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="s1"&gt;'args'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

        &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Failed to parse node: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The second is for parsing function call arguments.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_node_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;')'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Expected comma to separate args'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getelement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's it for parsing and lexing the entire whole template and
node language!&lt;/p&gt;
&lt;h3 id="interpreting"&gt;Interpreting&lt;/h3&gt;&lt;p&gt;Interpreting is a matter of iterating over the AST recursively,
writing out literal text, evaluating the contents of tags, and doing
special processing for blocks.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;item_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'tag'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tag_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interpret_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'block'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;interpret_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'child'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unknown type: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="intepreting-nodes"&gt;Intepreting nodes&lt;/h4&gt;&lt;p&gt;A node is one of two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A literal which is either a&lt;ul&gt;
&lt;li&gt;String if surrounded by single quotes&lt;/li&gt;
&lt;li&gt;Otherwise an identifier to be looked up in the environment dictionary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Or a function call&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interpret_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'literal'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Is a string&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;'&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;'&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# Default to an env lookup&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

    &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'args'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let's define &lt;code&gt;==&lt;/code&gt; which checks if all args are equal. First
we have to interpret all args and then we return True if they are all
equal.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'=='&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;arg_vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;interpret_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arg_vals&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg_vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg_vals&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let's define a helper for retrieving an entry from a dictionary,
called &lt;code&gt;get&lt;/code&gt;. This will evaluate its first arg and assume
it is a dictionary. Then it will evaluate its second arg and assume it
is a key in the dictionary. Then it will return the result of looking
up the key in the dictionary.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'get'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;arg_vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;interpret_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arg_vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;arg_vals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And if its neither of these supported functions, just raise an error.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unknown function: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="interpreting-blocks"&gt;Interpreting blocks&lt;/h4&gt;&lt;p&gt;Blocks are just a little different than a generic node. In addition to
being evaluated they act on a child AST within the start and end of
the block.&lt;/p&gt;
&lt;p&gt;For example, in an &lt;code&gt;if&lt;/code&gt; block we will evaluate its argument
and recursively call &lt;code&gt;interpret&lt;/code&gt; on the child AST if the argument is
truthy.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interpret_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'args'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'if'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;interpret_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And for &lt;code&gt;for-in&lt;/code&gt; we will use the first argument as the name
of an identifier to be copied into a child environment
dictionary. We'll interpret the second argument and then iterate over
it, calling &lt;code&gt;interpret&lt;/code&gt; recursively for each item in the
array and passing the child environment dictionary so it has access to
the current element.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'for-in'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;loop_variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;loop_iter_variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;interpret_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop_variable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;child_env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;child_env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;loop_iter_variable&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;
            &lt;span class="n"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;child_env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Just like before, if we see a block we don't support yet, throw an
error.&lt;/p&gt;
&lt;p&gt;&lt;span class="code-caption"&gt;pytemplate.py&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unsupported block node function: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that's that. :)&lt;/p&gt;
&lt;h3 id="run-it"&gt;Run it&lt;/h3&gt;&lt;p&gt;Now we can give the example from the beginning a shot.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;test.py

&amp;lt;html&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;body&amp;gt;

&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;article&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;h1&amp;gt;Hello&lt;span class="w"&gt; &lt;/span&gt;world!&amp;lt;/h1&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;p&amp;gt;
&lt;span class="w"&gt;      &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;my&lt;span class="w"&gt; &lt;/span&gt;first&lt;span class="w"&gt; &lt;/span&gt;post!
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;/p&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;/article&amp;gt;

&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;article&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;h1&amp;gt;Take&lt;span class="w"&gt; &lt;/span&gt;two&amp;lt;/h1&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;p&amp;gt;
&lt;span class="w"&gt;      &lt;/span&gt;This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;second&lt;span class="w"&gt; &lt;/span&gt;post.
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;/p&amp;gt;
&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;/article&amp;gt;

&lt;span class="w"&gt;  &lt;/span&gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pretty sweet for only 300 lines of Python!&lt;/p&gt;
&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p dir="ltr" lang="en"&gt;Been wanting to write a Python template library ever since I failed trying to do so years ago in Standard ML. Here's my take on a Jinja-like library!&lt;a href="https://t.co/P1nAV6fSxk"&gt;https://t.co/P1nAV6fSxk&lt;/a&gt; &lt;a href="https://t.co/DbXQt1JYx8"&gt;pic.twitter.com/DbXQt1JYx8&lt;/a&gt;&lt;/p&gt;&amp;mdash; Phil Eaton (@phil_eaton) &lt;a href="https://twitter.com/phil_eaton/status/1396535283190046722?ref_src=twsrc%5Etfw"&gt;May 23, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;/p&gt;</description><author>Notes on software development</author><pubDate>Sun, 23 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">http://notes.eatonphil.com/writing-a-template-library-in-python.html</guid></item><item><title>$120k in Infoproduct Sales - How to Extend the Long Tail</title><link>https://www.swyx.io/long-tail-infoproduct</link><description>This post originated as an AMA on IndieHackers</description><author>swyx's site RSS Feed</author><pubDate>Sun, 23 May 2021 00:56:06 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/long-tail-infoproduct</guid></item><item><title>May 2021 Projects</title><link>https://venam.net/blog/programming/2021/05/23/may-2021-projects.html</link><description>It's been seven months since the last update article in October 2020. We're still in a pandemic and living a more self-reliant, distanced, and introspective life. I've personally taken the stance of slowing down, to use this intermittent time to my advantage for personal growth. Slow living and the enjoyment of little things.</description><author>Venam's Blog — Patrick Louis (Lebanon)</author><pubDate>Sun, 23 May 2021 00:00:00 GMT</pubDate><guid isPermaLink="true">https://venam.net/blog/programming/2021/05/23/may-2021-projects.html</guid></item><item><title>Reduce a problem to its lowest sensible abstraction</title><link>https://adamcraven.com/writing/reduce-a-problem-to-its-lowest-sensible-abstraction/</link><description/><author>Writing on Adam Craven</author><pubDate>Sat, 22 May 2021 23:53:57 GMT</pubDate><guid isPermaLink="true">https://adamcraven.com/writing/reduce-a-problem-to-its-lowest-sensible-abstraction/</guid></item><item><title>Blockchain? OK, but not for property</title><link>https://stop.zona-m.net/2021/05/blockchain-ok-but-not-for-property/</link><description>&lt;p&gt;Even Leonardo da Vinci would agree.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Sat, 22 May 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/blockchain-ok-but-not-for-property/</guid></item><item><title>[Computing] Freenode IRC operators now engaging in routine abuses of power</title><link>https://www.devever.net/~hl/freenode_abuse</link><description>&lt;p&gt;There have been several allegations of this since the handover of the Freenode
infrastructure to its new custodians, but I can now provide a first-hand
account of one incident — because I am the victim of it.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Sat, 22 May 2021 18:51:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/freenode_abuse</guid></item><item><title>Book review - Developing Quality Technical Information</title><link>https://river.me/blog/book-review-quality-technical-information/</link><description>A review of the book &amp;ldquo;Developing Quality Technical Information: A Handbook for Writers and Editors&amp;rdquo;</description><author>River Writes - A MediaWiki Blog</author><pubDate>Sat, 22 May 2021 12:37:18 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/book-review-quality-technical-information/</guid></item><item><title>2021–05–22: PinePhone kernel news</title><link>https://xnux.eu/log/#033</link><author>megi's PinePhone Development Log</author><pubDate>Sat, 22 May 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://xnux.eu/log/#033</guid></item><item><title>Tricks vs implementation in coding interviews</title><link>https://bytepawn.com/tricks-vs-implementation-in-coding-interviews.html</link><description>&lt;p&gt;Thinking up tricky solutions in 3-5 minutes is not a requirement in a work setting. Usually, there are days or weeks for that. But implementing an idea, once the idea is there, should be straightforward for a good programmer. &lt;br /&gt;&lt;br /&gt; &lt;img alt="Daily Coding Problem" src="/images/daily_coding_problem.jpg" style="width: 400px;" /&gt;&lt;/p&gt;</description><author>Bytepawn - Marton Trencseni</author><pubDate>Sat, 22 May 2021 01:00:00 GMT</pubDate><guid isPermaLink="true">https://bytepawn.com/tricks-vs-implementation-in-coding-interviews.html</guid></item><item><title>Ride the curve</title><link>https://utf9k.net/blog/ride-the-curve/</link><description>A post from a darker time on how to better think about taking a break</description><author>utf9k</author><pubDate>Sat, 22 May 2021 00:40:00 GMT</pubDate><guid isPermaLink="true">https://utf9k.net/blog/ride-the-curve/</guid></item><item><title>You want a Stolp in every restaurant</title><link>https://stop.zona-m.net/2021/05/you-want-a-stolp-in-every-restaurant/</link><description>&lt;p&gt;And every home too.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Fri, 21 May 2021 21:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/you-want-a-stolp-in-every-restaurant/</guid></item><item><title>A far too simple (hardcoded) tasks backend in Objective-S</title><link>https://blog.metaobject.com/2021/05/a-far-too-simple-harcoded-tasks-backend.html</link><description>Recently there was a question as to what one should use to create a backend for an iOS/macOS app these days.  I couldn't
resist mentioning Objective-S, and just to check for myself whether that's feasible, I quickly jotted down the following
tiny backend that returns a hardcoded list of tasks via HTTP:


&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
#!env stsh
framework:ObjectiveHTTPD load.

class Task {
   var &amp;lt;bool&amp;gt; done.
   var title.
   -description { "Task: {this:title} done: {this:done}". }
}

taskList ← #( #Task{ #title: 'Clean my room', #done: false }, #Task{ #title: 'Check twitter feed', #done: true } ).

scheme todo {
   var taskList.
   /tasks { 
      |= { 
         this:taskList.
      }
   }
}.

todo := #todo{ #taskList: taskList }.
server := #MPWSchemeHttpServer{ #scheme: todo, #port: 8082 }.
server start.
shell runInteractiveLoop.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

After loading the HTTP framework,  we define a &lt;code&gt;Task&lt;/code&gt; and a list of two example tasks.  Then we define 
a scheme with a single path, just &lt;code&gt;/tasks&lt;/code&gt;, which returns said tasks list.  We then instantiate the 
scheme and serve it via HTTP on port 8082. Since this is a shell script and starting the server does not block,
we finally start up the REPL.&lt;p&gt;

Details such as coding the tasks as JSON, accessing a single task and modifying tasks are left as exercises 
for the reader.&lt;p&gt;</description><author>metablog</author><pubDate>Fri, 21 May 2021 18:40:20 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/05/a-far-too-simple-harcoded-tasks-backend.html</guid></item><item><title>RailsConf Presentation: 'Junior' Developers are a Solution to Many of your Problems</title><link>https://josh.works/railsconf</link><description>&lt;p&gt;Did this talk resonate and you want to implement some of the ideas at your company? I might be able to help. Shoot me an email at &lt;code class="language-plaintext highlighter-rouge"&gt;joshthompson@hey.com&lt;/code&gt; or book some time to talk at https://calendly.com/joshthompson/coffee.&lt;/p&gt;

&lt;p&gt;This talk is available on &lt;code class="language-plaintext highlighter-rouge"&gt;railsconf.org&lt;/code&gt;, here: &lt;a href="https://railsconf.org/watch/bettering-our-industry/junior-devs-are-the-solution-to-many-of-your-problems"&gt;https://railsconf.org/watch/bettering-our-industry/junior-devs-are-the-solution-to-many-of-your-problems&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve only just started systematizing resources around this. I’d love to talk with &lt;em&gt;you&lt;/em&gt; about your thoughts on these resources. Use the &lt;code class="language-plaintext highlighter-rouge"&gt;coffee&lt;/code&gt; link above, or if there’s no availability &lt;em&gt;and&lt;/em&gt; this is an issue you’re willing to throw money at, book time on a much more flexible calendar here: &lt;a href="https://calendly.com/joshthompson/flexible"&gt;https://calendly.com/joshthompson/flexible&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m a month into creating public resources to create a “path” by which an ECSD can grow to a non-ECSD. Follow along here: &lt;a href="https://github.com/sponsors/josh-works"&gt;https://github.com/sponsors/josh-works&lt;/a&gt;&lt;/p&gt;

&lt;h1 id="questions-that-came-up-from-the-talk"&gt;Questions that came up from the talk&lt;/h1&gt;

&lt;p&gt;##&lt;/p&gt;

&lt;h1 id="misc-resources"&gt;Misc Resources&lt;/h1&gt;

&lt;p&gt;There are lots of ways teams can better utilize the resources of their ECSDs.&lt;/p&gt;

&lt;p&gt;Q&amp;amp;A on March 14th, at 4:40p.&lt;/p&gt;

&lt;h2 id="slides"&gt;Slides&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.dropbox.com/s/d98chrnpm8gn4uq/Junior%20Developers%20are%20the%20Solution%20to%20Many%20of%20Your%20Problems.key?dl=0"&gt;Available on Dropbox&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="railsconf-cfp-draft-notes"&gt;RailsConf CFP Draft Notes&lt;/h2&gt;

&lt;p&gt;Here’s the abstract for this talk: &lt;a href="/railsconf-cfp-draft-outline"&gt;RailsConf CFP Draft Notes&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="useful-resources"&gt;Useful Resources&lt;/h2&gt;

&lt;p&gt;I’ll be relying upon scientific and non-scientific literature like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.semanticscholar.org/paper/Psychological-Conditions-of-Personal-Engagement-and-Kahn/cbb3887590de9e5dc702b5d2655fbe804669fea0"&gt;William Khan: Psychological Conditions of Personal Engagement and Disengagement at Work&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://commoncog.com/blog/tacit-knowledge-is-a-real-thing/"&gt;Why Tacit Knowledge is More Important Than Deliberate Practice&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.journals.uchicago.edu/doi/abs/10.1086/421787"&gt;Structural Holes and Good Ideas, American Journal of Sociology, 2004&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.researchgate.net/publication/334448588_Hiring_is_Broken_What_Do_Developers_Say_About_Technical_Interviews"&gt;Hiring is Broken: What Do Developers Say About Technical Interviews&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://web.mit.edu/5.95/readings/bloom-two-sigma.pdf"&gt;The 2 Sigma Problem: The Search for Methods of Group Instruction as Effective as One-to-One Tutoring (Benjamin Bloom, 1984)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://pubmed.ncbi.nlm.nih.gov/19739881/"&gt;Conditions for Intuitive Expertise: A Failure to Disagree, Kahneman &amp;amp; Klein, Princeton, 2009&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.amazon.com/Secret-Our-Success-Evolution-Domesticating/dp/0691166854"&gt;The Secret of Our Success: How Culture Is Driving Human Evolution, Domesticating Our Species, and Making Us Smarter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://how.complexsystems.fail/"&gt;How Complex Systems Fail&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="other-talks-ive-given"&gt;Other Talks I’ve given&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://josh.works/boulder_ruby_group"&gt;Boulder Ruby: Move Slow and Improve Things: Performance Improvement in a Rails App&lt;/a&gt;
👉 &lt;a href="https://www.greaterthancode.com/safety-science-and-failure-as-an-opportunity"&gt;Greater Than Code ep 220: Safety Science and Failure As An Opportunity For Growth&lt;/a&gt;&lt;/p&gt;

&lt;h4 id="footnotes"&gt;Footnotes&lt;/h4&gt;</description><author>Josh Thompson</author><pubDate>Fri, 21 May 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://josh.works/railsconf</guid></item><item><title>Devlog #5: voxel world goals</title><link>https://domwillia.ms/devlog5</link><description>An introduction to and high-level specification of the 3D voxel game world.</description><author>Dom Williams</author><pubDate>Fri, 21 May 2021 15:00:00 GMT</pubDate><guid isPermaLink="true">https://domwillia.ms/devlog5</guid></item><item><title>Why are there no return statements in Objective-S?</title><link>https://blog.metaobject.com/2021/05/why-are-there-no-return-statements-in.html</link><description>My &lt;a href="https://blog.metaobject.com/2021/05/a-far-too-simple-harcoded-tasks-backend.html"&gt;previous example&lt;/a&gt; raised a &lt;a href="https://twitter.com/longmucholove/status/1395365345561661442?s=21"&gt;question&lt;/a&gt;:  why no return statements?  I am assuming this was about this part of the example:
&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;

   -description { "Task: {this:title} done: {this:done}". }

&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;
The answer is that I would like to do without return statements if and as much as I can.  We will see
how much that is.  In general, I am in favor of &lt;a href="https://en.wikipedia.org/wiki/Expression-oriented_programming_language"&gt;expression-orientation&lt;/a&gt; in programming languages.  A simple example is if-statements
vs. conditional expressions.  In most languages today, like C, Objective-C and Swift, &lt;code&gt;if&lt;/code&gt; is a statement.
That means I write something as follows:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
if ( condition ) {
   do something if true
} else {
   do something different if false
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

This seems obvious and is general, but very often you don't want to &lt;em&gt;do&lt;/em&gt; arbitrary stuff, you just want to have 
some variable have some value in one case and a different value in another case.

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
int foo;
if ( condition ) {
   foo = 1;
} else {
   foo = 42;
}
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

In that case, it is annoying that the &lt;code&gt;if&lt;/code&gt; is defined to be a statement and not an expression, because 
you can't just write the following:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
int foo;
foo = if ( condition ) { 1; } else { 42; }
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

In addition, as hinted to in the previous examples, you can't use a statement to initialize a variable, that
definitely has to be an expression.  Which is why C and many derived languages have the "ternary" operator
(?:), which is really just an if/else in expression form.

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
int foo=condition ? 1 : 42;
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

That solves the problem, but now you have two conditionals.  Why not have just one?  LISP, most of the
FP languages as well as Smalltalk and Objective-S have an &lt;code&gt;if&lt;/code&gt; that returns a value.

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
a := condition ifTrue:{ 1. } ifFalse:{ 42. }.
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

So that's why expression-orientation is useful in general.  What about methods?  The same general idea
applies.  Whereas in Java, for example, a read accessor is called &lt;code&gt;getX()&lt;/code&gt;,  indicating
an action that is performed ("get the value of x") in Objective-C Smalltalk and Objective-S, it is
just called &lt;code&gt;x&lt;/code&gt;, ("the value of x").&lt;p&gt;

The same idea applies to dropping return statements where possible.  It's not "get me the description
of this object", it is "the description of this object is...".  And inside the method, it's not "this 
statement now returns the following string as the description", but, again, "the description is...".&lt;p&gt;

Describing things that are, rather than actions to perform, is at the heart of Objective-S, as 
discussed in &lt;a href="https://2020.programming-conference.org/details/salon-2020-papers/5/Can-Programmers-Escape-the-Gentle-Tyranny-of-call-return-"&gt;Can Programmers Escape the Gentle Tyranny of Call/Return&lt;/a&gt;.&lt;p&gt;

As Guy Steele &lt;a href="https://dreamsongs.com/ObjectsHaveNotFailedNarr.html"&gt;put it&lt;/a&gt;:

&lt;blockquote cite="https://dreamsongs.com/ObjectsHaveNotFailedNarr.html"&gt;
Another weakness of procedural and functional programming is that their viewpoint assumes a process by which "inputs" are transformed into "outputs"; there is equal concern for correctness and for termination (and proofs thereof). But as we have connected millions of computers to form the Internet and the World Wide Web, as we have caused large independent sets of state to interact–I am speaking of databases, automated sensors, mobile devices, and (most of all) people–in this highly interactive, distributed setting, the procedural and functional models have failed, another reason why objects have become the dominant model. Ongoing behavior, not completion, is now of primary interest. Indeed, object-oriented programming had its origins in efforts to simulate the ongoing behavior of interacting real-world entities–thus the programming language SIMULA was born.
&lt;/blockquote&gt;

So wherever possible, Objective-S tries to push towards expressing things as statically as possible, pushing
away from action-orientation.  For example, hooking up a timed source to a pin:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
#Blinker{ #seconds: 1, #active: true} → ref:gpio:17. &lt;/pre&gt;
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

instead of executing a loop:

&lt;hr /&gt;
&lt;code&gt;
&lt;pre&gt;
while True: 
    GPIO.output(17, True) 
    sleep(1) 
    GPIO.output(17, False) 
    sleep(1) &lt;/pre&gt;
&lt;/pre&gt;
&lt;/code&gt;
&lt;hr /&gt;

The same goes for many other relationships:  instead of writing
procedural code that initiates and/or maintains the relatinship, with the
actual relationship remainig implicit, describe the actual relationship,
make &lt;em&gt;that&lt;/em&gt; explicit, and instead keep the procedural code that maintains
it as a hidden implementation detail.&lt;p&gt;

If the &lt;code&gt;return&lt;/code&gt; statement comes back, and it very well might, I am hoping
it will be in a slightly more general form.  I recall Smalltalk's "^" being described
as "send back".  I've already taken that and generalised it to mean "send result", using it
in filter definitions, where "^" means "send a result to the next filter in the pipeline".
It is needed there because filters are not limited to sending a single result, they can
send zero or many.&lt;p&gt;

With those more general semantics, "^" might also be used to send back results to the
sender of an asynchronous message, which is obviously quite different from a "return".&lt;p&gt;

And of course it would be useful for early returns, which are currently not possible.&lt;p&gt;

&lt;h3&gt;What about void methods?&lt;/h3&gt;

Objective-S does have void methods, after all its procedural part is essentially identical
to Objective-C, which also has them.  However, I agree with the FP folk that functions
(procedures, methods) should be as (side-)effect free as possible, and void methods by 
definition are effectful (or no-ops).&lt;p&gt;

So where do the effects go?  Two places:

&lt;ol&gt;
&lt;li&gt;The left hand side of the "←". &lt;p&gt; In most current programming languages, assignment
is severely crippled, and therefore not really useful for generalised effects.  With
Polymorphic Identifiers and Storage Combinators, there is enough expressive power and
ability to abstract that we should need far fewer void methods.&lt;/li&gt;
&lt;li&gt;Connecting via "→"&lt;p&gt;Much of the need for effectful methods in OO is for 
constructing and connecting objects.  In Objective-S, you don't need to call
methods that result in a connection being established as a side effect of 
munging on some state, you define connections between objects directly using "→".&lt;p&gt;
Well, and you define objects using object literals such as &amp;nbsp;&lt;code&gt;#Blinker{ #seconds: 1, #active: true}&lt;/code&gt;&amp;nbsp; instead of 
setting instance variables procedurally.
&lt;/li&gt;
&lt;/ol&gt;

That's the plan, anyway.  Although a lot of that plan is coming true at the moment.  Exciting times!  (And one of the reasons I haven't been blogging all that much).</description><author>metablog</author><pubDate>Fri, 21 May 2021 09:45:44 GMT</pubDate><guid isPermaLink="true">https://blog.metaobject.com/2021/05/why-are-there-no-return-statements-in.html</guid></item><item><title>[Computing] Live feed of the HN Top 30 as an IRC Channel</title><link>https://www.devever.net/~hl/hntop</link><description>&lt;p&gt;I've created an IRC bot which uses the &lt;a href="https://github.com/HackerNews/API"&gt;Hacker News
API&lt;/a&gt; to provide a live feed of any item
which hits the HN front page. Since the HN API provides live updates using
Server Sent Events, this is truly live and not polled.&lt;/p&gt;</description><author>devever.net/~hl</author><pubDate>Thu, 20 May 2021 21:13:00 GMT</pubDate><guid isPermaLink="true">https://www.devever.net/~hl/hntop</guid></item><item><title>PiClock: Waking Up to the Unix Philosophy</title><link>https://davi.sh/blog/2021/05/pi-clock/</link><description>&lt;p&gt;I built a clock! &lt;a href="https://github.com/davish/pi-clock"&gt;It runs Linux&lt;/a&gt;.
Sounds like quite the over-engineering project, but I learned a ton
along the way and was able to flex some muscles that had been lying
dormant for a while. I write a decent amount of code, but I hadn’t
worked on an electronics project in years and had never really embarked
on a 3D modeling or design project outside the classroom.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="/blog/images/piclock/clock-activated.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;I discovered &lt;a href="https://hackaday.com/2021/04/25/keep-in-touch-with-grandma-with-this-lo-tech-interface/"&gt;the
Yayagram&lt;/a&gt;
a few weeks ago, and my project was inspired by many of the same
principles. Although I’m building this clock for myself rather than an
older family member, I love the idea that objects can be smart and
internet-connected in useful ways without interactive touchscreens.
Making smart devices with lower-fidelity input and output can help slow
down the feedback loops that we often encounter with the modern
internet. I’m hoping my clock can be a step for me away from
doomscrolling and towards a more measured relationship with content
consumption on the internet.&lt;/p&gt;
&lt;h1 id="the-problem"&gt;The Problem&lt;/h1&gt;
&lt;p&gt;I’m a relatively deep sleeper. I sometimes sleep through alarms, and
even when I wake up most of the time, I fall asleep again just as
quickly after I hit “off” or “snooze.” I’ve resolved to just
setting four or five alarms staggered five minutes apart around the time
I want to wake up. It’s not a use-case that’s supported too well by
Apple’s Clock app:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Lots of alarms to scroll through and old TODOs that I&amp;#x27;ve left for
myself in the alarm names." src="/blog/images/piclock/clock.png" /&gt;&lt;/p&gt;
&lt;p&gt;On top of all this, needing to grab my phone to snooze my alarm makes it
easy for me to keep scrolling rather than get up and start my day.
Off-the-shelf digital alarm clocks are a lot less distracting than the
general-purpose internet communicators we carry around all day. Still,
they generally can’t set more than one or two alarms and are difficult
to program when schedules change day-to-day.&lt;/p&gt;
&lt;h1 id="the-spec"&gt;The Spec&lt;/h1&gt;
&lt;p&gt;I wanted a clock that would:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let me set multiple alarms per wake-up.&lt;/li&gt;
&lt;li&gt;Not be distracting in the morning when I want to start my routine.&lt;/li&gt;
&lt;li&gt;Provide a bit of news to begin my day without sucking me into an
algorithm-powered bottomless doom vortex.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Point #3 means that this clock needed to be internet-connected. But
point #2 means that it couldn’t be distracting. This rules out writing
a custom app for my phone or something like an Echo Show.&lt;/p&gt;
&lt;h1 id="the-hardware"&gt;The Hardware&lt;/h1&gt;
&lt;h2 id="electronics"&gt;Electronics&lt;/h2&gt;
&lt;p&gt;My strange alarm staggering requirements meant that I also felt like
I’d want to program in custom behavior. With the interaction of all
these requirements, I decided to have the brains of the clock be a
Raspberry Pi Zero W running a standard Raspbian distribution. For the
splash of news in requirement #3, I wanted to have the choice between
audio podcasts and small snippets from Twitter and newsletters, along
with the weather and recent COVID statistics for my area, so I opted to
add in a thermal receipt printer for a more low-screen digital output.&lt;/p&gt;
&lt;p&gt;I sourced all the parts from &lt;a href="https://www.adafruit.com"&gt;Adafruit&lt;/a&gt;, which
has a fantastic selection and quick delivery times from their warehouse
in NYC to where I’ve been in Philadelphia. I’ve listed a more-or-less
complete &lt;a href="https://github.com/davish/pi-clock/blob/main/bom.org"&gt;bill of
materials&lt;/a&gt; in the
Github repository.&lt;/p&gt;
&lt;p&gt;&lt;img alt="All the electronics &amp;#x22;exploded&amp;#x22; out of the
enclosure." src="/blog/images/piclock/components.jpg" /&gt;&lt;/p&gt;
&lt;h2 id="enclosure"&gt;Enclosure&lt;/h2&gt;
&lt;p&gt;The enclosure was a challenge — my first non-school project in
SolidWorks since taking a mechanical design elective two years ago and
my first 3D printing project since I was on my high school’s robotics
team. If I’d had access to a laser cutter, then that would’ve been my
go-to since the enclosure is just a simple box. Engineering facilities
at Penn were closed down to most undergrads this year without pressing
work to do, so my trusty Printrbot Simple Metal would have to cut it.&lt;/p&gt;
&lt;p&gt;I had more issues with bed adhesion and warping than I’d ever
encountered before. Once I ironed out the adhesion issues, I couldn’t
pull the finished prints off the bed without applying some serious lever
force. I ended up investing in a &lt;a href="https://www.buildtak.com/product/buildtak-flexplate-system/"&gt;BuildTak
FlexPlate&lt;/a&gt;
to avoid damaging the printer more going forward.&lt;/p&gt;
&lt;p&gt;I knew that the software was the most exciting part of this for me and
what I was most comfortable with. So I didn’t let myself get started on
the coding until after I’d finish the enclosure; otherwise, I’d be
left with a mess of wires on my bedside table until the end of time.
Ultimately, though, I’m happy with the finished result. It’s about as
small as possible while still fitting all the components and looks clean
by my bed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Components packed into the enclosure before the top goes
on." src="/blog/images/piclock/enclosure.jpg" /&gt;&lt;/p&gt;
&lt;h1 id="the-software"&gt;The Software&lt;/h1&gt;
&lt;p&gt;Adafruit’s whole library stack for their peripherals, I’m pretty sure,
is only available for Python on the Pi and Arduino for more basic
microcontrollers. This basically made my programming language decision
for me, and I’m more than comfortable enough in Python to be happy with
that solution.&lt;/p&gt;
&lt;p&gt;Before this, my other electronics/embedded computing experience has come
from from Arduino, with its &lt;code&gt;setup()&lt;/code&gt;{.verbatim} and &lt;code&gt;loop()&lt;/code&gt;{.verbatim}
functions that control the whole project. I was dreading writing all my
code in a single Python &lt;code&gt;while True&lt;/code&gt;{.verbatim} loop that would get
initiated on system startup. When I worked on a &lt;a href="https://hackaday.io/project/3627-trinket-watch"&gt;similar digital watch
project&lt;/a&gt; five years ago,
the code ended up being extremely brittle, with different modes and
state riddled all over the place. Fundamentally, I realized I was asking
myself why I would try and write a robust mini-OS for a clock when the
computer it’s running on is already running Linux?&lt;/p&gt;
&lt;p&gt;The architecture I landed on is spread among different files and uses
&lt;code&gt;cron&lt;/code&gt;{.verbatim} to orchestrate all the behavior. Python scripts
control various aspects of the clock’s functions and are called on
one-minute intervals. Luckily, my display doesn’t have space for
seconds since this is the smallest interval available for cron.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/davish/pi-clock/blob/main/tick.py"&gt;&lt;code&gt;tick.py&lt;/code&gt;{.verbatim}&lt;/a&gt;:
updates the seven-segment displays with the current time.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/davish/pi-clock/blob/main/alarm.py"&gt;&lt;code&gt;alarm.py&lt;/code&gt;{.verbatim}&lt;/a&gt;:
checks alarm settings and rings an alarm if appropriate.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/davish/pi-clock/blob/main/podcast.py"&gt;&lt;code&gt;podcast.py&lt;/code&gt;{.verbatim}&lt;/a&gt;:
depending on the command line argument, downloads a new podcast
episode from an RSS feed or plays the most recent one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s been my first real foray into building utilities that faithfully
follow the Unix philosophy of doing one small thing well in a composable
way. Settings like alarm schedules are saved in JSON files, and I’m
planning on writing wrapper commands around
&lt;a href="https://stedolan.github.io/jq/"&gt;&lt;code&gt;jq&lt;/code&gt;{.verbatim}&lt;/a&gt; to make it easy to
modify settings without dropping into an editor over SSH. It’s
certainly been discussed how composable Unix utilities can lend
themselves well to a productive developer workflow. It was really cool
seeing this approach work well for an embedded system, too, albeit
without actual realtime requirements.&lt;/p&gt;
&lt;h1 id="next-steps"&gt;Next Steps&lt;/h1&gt;
&lt;p&gt;There are certainly some more directions I want to take the clock in. On
the software side, I still haven’t played around with the receipt
printer much. It’ll be interesting to experiment with data sources and
layouts that can work well for morning updates. I also think it could be
cool to print out transit directions on-demand. I’m moving to New York
City soon, and I always find myself making sure I don’t close out of
Google Maps when I’m on the subway, since service can be spotty and I
often forget what stop I need to get off on or what train to transfer to
next. I’m also thinking about integrating to-dos for the day to track
what I plan on getting done in the morning.&lt;/p&gt;
&lt;p&gt;On the hardware end, there’s certainly room for expansion. The clock
currently has a single pushbutton that I use to interact with alarms. It
might be cool to add a
&lt;a href="https://www.adafruit.com/product/3317"&gt;time-of-flight&lt;/a&gt; sensor for
gesture control. Or maybe a &lt;a href="https://www.adafruit.com/product/4750"&gt;fingerprint
sensor&lt;/a&gt; to allow for different
controls based on what finger I press to the sensor.&lt;/p&gt;
&lt;p&gt;Code exists in any number of copies. It’s been awesome tinkering and
tweaking a physical &lt;em&gt;thing&lt;/em&gt; that’s more-or-less one of a kind. And
unlike an off-the-shelf product, it’ll be cool to see it evolve over
time as I tweak whaat I’ve got add more features to it.&lt;/p&gt;</description><author>Davis Haupt's Blog</author><pubDate>Thu, 20 May 2021 20:00:00 GMT</pubDate><guid isPermaLink="true">https://davi.sh/blog/2021/05/pi-clock/</guid></item><item><title>The worst argument against UBI</title><link>https://stop.zona-m.net/2021/05/the-worst-argument-against-ubi/</link><description>&lt;p&gt;&amp;ldquo;Let&amp;rsquo;s not fight for social progress because it could be taken away at a later point.&amp;rdquo;&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 20 May 2021 16:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/the-worst-argument-against-ubi/</guid></item><item><title>A Quick CSS Tip Regarding Legacy Image Tables</title><link>https://www.masswerk.at/nowgobang/2021/legacy-img-tables</link><description>Dealing with legacy image tables and modern web standards.</description><author>mass:werk – Now Go Bang!</author><pubDate>Thu, 20 May 2021 12:43:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/legacy-img-tables</guid></item><item><title>I’m building a cryptocurrency trading bot that you can follow live on Telegram</title><link>https://ailef.tech/2021/05/20/im-building-a-cryptocurrency-trading-bot-that-you-can-follow-live-on-telegram/</link><description>TLDR: I&amp;#8217;ve been working on a cryptocurrency trading bot that you can follow live on Telegram. It&amp;#8217;s currently doing simulated trades on all Binance perpetual futures pairs. If the results are promising, I will run it with a real budget. If you look at the history of the channel you can see how the bot [&amp;#8230;]</description><author>AILEF</author><pubDate>Thu, 20 May 2021 10:19:16 GMT</pubDate><guid isPermaLink="true">https://ailef.tech/2021/05/20/im-building-a-cryptocurrency-trading-bot-that-you-can-follow-live-on-telegram/</guid></item><item><title>An Update to the Virtual 6502 Suite</title><link>https://www.masswerk.at/nowgobang/2021/virtual6502-update</link><description>Improving one of the oldest 6502 tool sets on the web.</description><author>mass:werk – Now Go Bang!</author><pubDate>Thu, 20 May 2021 09:50:00 GMT</pubDate><guid isPermaLink="true">https://www.masswerk.at/nowgobang/2021/virtual6502-update</guid></item><item><title>Sublime Text 4 is released!</title><link>https://river.me/blog/sublimetext-4/</link><description>Sublime Text 4 has been released!</description><author>River Writes - A MediaWiki Blog</author><pubDate>Thu, 20 May 2021 06:46:13 GMT</pubDate><guid isPermaLink="true">https://river.me/blog/sublimetext-4/</guid></item><item><title>How To Optimize for Change</title><link>https://www.swyx.io/optimize-for-change</link><description>Lessons from React, GraphQL, and Rich Hickey on how to design software that doesn't implode the first time requirements change.</description><author>swyx's site RSS Feed</author><pubDate>Thu, 20 May 2021 03:16:31 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/optimize-for-change</guid></item><item><title>If dreams make and keep us smarter...</title><link>https://stop.zona-m.net/2021/05/if-dreams-make-and-keep-us-smarter.../</link><description>&lt;p&gt;Using stuff that makes us dream less is bad. No?&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Thu, 20 May 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/if-dreams-make-and-keep-us-smarter.../</guid></item><item><title>New Vehicle. New Adventure. Details on Patreon now!</title><link>https://theroadchoseme.com/new-vehicle-new-adventure-details-on-patreon-now</link><description>In just two weeks I&amp;#8217;m flying to a different continent, building a new dedicated vehicle and setting out to explore far and wide over roughly 12-18 months. Going in I plan to get more&amp;#46;&amp;#46;&amp;#46;</description><author>The Road Chose Me</author><pubDate>Wed, 19 May 2021 05:10:49 GMT</pubDate><guid isPermaLink="true">https://theroadchoseme.com/new-vehicle-new-adventure-details-on-patreon-now</guid></item><item><title>gpg: decryption failed: No secret key (macOS)</title><link>https://xenodium.com/gpg-decryption-failed-no-secret-key-macos</link><description>&lt;pre&gt;&lt;code&gt;gpg: decryption failed: No secret key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OMG! Where's my secret key gone!?&lt;/p&gt;
&lt;p&gt;But but but, &lt;em&gt;gpg –list-secret-keys&lt;/em&gt; says they're there. Puzzled…&lt;/p&gt;
&lt;p&gt;Ray Oei's Stack Overflow &lt;a href="https://stackoverflow.com/a/66234166"&gt;answer&lt;/a&gt; solved the mystery for me: pinentry never got invoked, so likely something's up with the agent… Killing (and thus restaring) the gpg-agent did the trick:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;gpgconf --kill gpg-agent
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thank you internet stranger. Balance restored.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Wed, 19 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/gpg-decryption-failed-no-secret-key-macos</guid></item><item><title>Add expiration dates to TODO comments</title><link>https://holzer.online/articles/add-expiration-dates-to-todo-comments/index.html</link><description>&lt;p&gt;
When I spot an idea for a non-trivial refactoring, I often add a TODO comment. The ideas which are not acted upon immediately are often, but not always, valuable.  A problem is, that they tend to pile up without being resolved. In a mid-sized real-world code base that I maintain the ratio was roughly one such comment per 100 SLOC.
&lt;/p&gt;

&lt;p&gt;
Therefore I decided to add a reasonable explicit expiration date to any such TODO (some 4 to 8 weeks in the future). When I encounter a comment that has expired, deleting it is a perfectly viable cleanup. The code structure obviously was not so bad after all, or else I would have put energy into the refactoring already.
&lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Wed, 19 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/add-expiration-dates-to-todo-comments/index.html</guid></item><item><title>Digital pacification WILL end, that is sure</title><link>https://stop.zona-m.net/2021/05/digital-pacification-will-end-that-is-sure/</link><description>&lt;p&gt;The problem is, how pacifically.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Wed, 19 May 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/digital-pacification-will-end-that-is-sure/</guid></item><item><title>Learn With Me: Julia - Tools and Learning Resources (#2)</title><link>https://geekmonkey.org/lwm-julia-2-tools-and-learning-resources/</link><description>A short collection of tools and resources that help experienced developers learn Julia.</description><author>geekmonkey</author><pubDate>Tue, 18 May 2021 18:00:00 GMT</pubDate><guid isPermaLink="true">https://geekmonkey.org/lwm-julia-2-tools-and-learning-resources/</guid></item><item><title>TIL that Hetzner offers a free DNS service</title><link>https://www.zufallsheld.de/2021/05/18/til-that-hetzner-offers-a-free-dns-service/</link><description>&lt;p&gt;I host my private servers at &lt;a href="https://www.hetzner.com/de/"&gt;Hetzner&lt;/a&gt;, a really nice, reliable and cheap german&amp;nbsp;webhoster.&lt;/p&gt;
&lt;p&gt;Previously I managed my &lt;span class="caps"&gt;DNS&lt;/span&gt; zones with Route 53 from Amazon. There I paid 50 cents per zone per month. I have multiple zones so this adds up at the end of the&amp;nbsp;year.&lt;/p&gt;
&lt;p&gt;Then …&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Tue, 18 May 2021 12:39:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/05/18/til-that-hetzner-offers-a-free-dns-service/</guid></item><item><title>Why Creator Clones Fail</title><link>https://www.swyx.io/clones-fail</link><description>The Innovator's Dilemma according to MKBHD, applied to YouTube's attempt to clone TikTok</description><author>swyx's site RSS Feed</author><pubDate>Tue, 18 May 2021 05:39:42 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/clones-fail</guid></item><item><title>New Responsive Layout and Blog Plans</title><link>https://blog.yiningkarlli.com/2021/05/responsive-layout.html</link><description>&lt;p&gt;I recently noticed that my blog and personal website’s layout looked really bad on mobile devices and in smaller browser windows.
When I originally created the current layout for this blog and for my personal website back in 2013, I didn’t really design the layout with mobile in mind whatsoever.
Back in 2013, responsive web design had only just started to take off, and being focused entirely on renderer development and computer graphics, I wasn’t paying much attention to the web design world that much!
I then proceeded to not notice at all how bad the layout on mobile and in small windows was because… well, I don’t really visit my own website and blog very much, because why would I?
I know everything that’s on them already!&lt;/p&gt;

&lt;p&gt;Well, I finally visited my site on my iPhone, and immediately noticed how terrible the layout looked.
On an iPhone, the layout was just the full desktop browser layout shrunk down to an unreadable size!
So, last week, I spent two evenings extending the current layout to incorporate responsive web design principles.
Responsive web design principles call for a site’s layout to adjust itself according to the device and window size such that the site renders in a way that is maximally readable in a variety of different viewing contexts.
Generally this means that content and images and stuff should resize so that its always at a readable size, and elements on the page should be on a fluid grid that can reflow instead of being located at fixed locations.&lt;/p&gt;

&lt;p&gt;Here is how the layout used by my blog and personal site used to look on an iPhone 11 display, compared with how the layout looks now with modern responsive web design principles implemented:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/mobile_before_after.png"&gt;
&lt;img alt="Figure 1: Old layout (left) vs. new responsive layout (right) in Safari on an iPhone 11." src="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/preview/mobile_before_after.webp" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So why did I bother with implementing these improvements to my blog and personal site now, some eight years after I first deployed the current layout and current version of the blog?
To answer this (self-asked) question, I want to first write a bit about how the purpose of this blog has evolved over the years.
I originally started this blog back when I first started college, and it originally didn’t have any clear purpose.
If anything, starting a blog really was just an excuse to rewrite and expand a custom content management system that I had written in PHP 5 back in high school.
Sometime in late 2010, as I got more interested in computer graphics, this blog became something of a personal journal to document my progress in exploring computer graphics.
Around this time I also decided that I wanted to focus all of my attention on computer graphics, so I dropped most of the web-related projects I had at the time and moved this blog from my own custom CMS to Blogger.
In grad school, I started to experiment with writing longer-form posts; for the first time for this blog, these posts were written primarily with a reader other than my future self in mind.
In other words, this is the point where I actually started to write posts intended for an external audience.
At this point I also moved the blog from Blogger to running on Jekyll hosted through Github Pages, and that’s when the first iterations of the current layout were put into place.&lt;/p&gt;

&lt;p&gt;Fast forward to today; I’ve now been working at Disney Animation for six years, and (to my constant surprise) this blog has picked up a small but steady readership in the computer graphics field!
The purpose I see for this blog now is to provide high quality, in-depth writeups of whatever projects I find interesting, with the hope that 1. my friends and colleagues and other folks in the field will find the posts similarly interesting and 2. that the posts I write can be informative and inspiring for aspirational students that might stumble upon this blog.
When I was a student, I drew a lot of inspiration from reading a lot of really cool computer graphics and programming blogs, and I want to be able to give back the same to future students!
Similarly, my personal site, which uses an extended version of the blog’s layout, now serves primarily as a place to collect and showcase projects that I’ve worked on with an eye towards hopefully inspiring other people, as opposed to serving as a tool to get recruited.&lt;/p&gt;

&lt;p&gt;The rate that I post at now is much slower than when I was in school, but the reason for this slowdown is because I put far more thought and effort into each post now, and while the rate at which new posts appear has slowed down, I like to think that I’ve vastly improved both the quality and quantity of content within each post. 
I recently ran &lt;code class="language-plaintext highlighter-rouge"&gt;wc -w&lt;/code&gt; on the blog’s archives, which yielded some interesting numbers.
From 2014 to now, I’ve only written 38 posts, but these 38 posts total a bit over 96,000 words (which averages to roughly 2,500 words per post).
Contrast with 2010 through the end of 2013, when I wrote 78 posts that together total only about 28,000 words (which averages to roughly 360 words per post)!
Those early posts came frequently, but a lot of those early posts are basically garbage; I only leave them there so that new students can see that my stuff wasn’t very good when I started either.&lt;/p&gt;

&lt;p&gt;When I put the current layout into place eight years ago, I wanted the layout to have as little clutter as possible and focus on presenting a clear, optimized reading experience.
I wanted computer graphics enthusiasts that come to read this blog to be able to focus on the content and imagery with as little distraction from the site’s layout as possible, and that meant keeping the layout as simple and minimal as possible while still looking good.
Since the main topic this blog focuses on is computer graphics, and obviously computer graphics is all about pictures and the code that generates those pictures (hence the name of the blog being “Code &amp;amp; Visuals”), I wanted the layout to allow for large, full-width images.
The focus on large full-width images is why the blog is single-column with no sidebars of any sort; in many ways, the layout is actually more about the images than the text, hence why text never wraps around an image either.
Over the years I have also added additional capabilities to the layout in support of computer graphics content, such as MathJax integration so that I can embed beautiful LaTeX math equations, and an embedded sliding image comparison tool so that I can show before/after images with a wiping interface.&lt;/p&gt;

&lt;p&gt;So with all of the above in mind, the reason for finally making the layout responsive is simple: I want the blog to be as clear and as readable as I can reasonably make it, and that means clear and readable on &lt;em&gt;any&lt;/em&gt; device, not just in a desktop browser with a large window!
I think a lot of modern “minimal” designs tend to use &lt;em&gt;too&lt;/em&gt; much whitespace and sacrifice information and text density; a key driving principle behind my layout is to maintain a clean and simple look while still maintaining a reasonable level of information and text density.
However, the old non-responsive layout’s density in smaller viewports was just ridiculous; nothing could be read without zooming in a lot, which on phones then meant a lot of swiping both up/down and left/right just to read a single sentence.
For the new responsive improvements, I wanted to make everything readable in small viewports without any zooming or swiping left/right.
I think the new responsive version of the layout largely accomplishes this goal; here’s an animation of how the layout resizes as the content window shrinks, as applied to the landing page of my personal site:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/scaling.gif"&gt;
&lt;img alt="Figure 2: Animation of how the new layout changes as the window changes size." src="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/preview/scaling.gif" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adapting my layout to be responsive was surprisingly easy and straightforward!
My blog and personal site use the same layout design, but the actual implementations are a bit different.
The blog’s layout is a highly modified version of an old layout called &lt;a href="https://github.com/kezzbracey/N-Coded"&gt;N-Coded&lt;/a&gt;, which in turn is an homage to what &lt;a href="https://ghost.org"&gt;Ghost&lt;/a&gt;’s default &lt;a href="https://github.com/TryGhost/Casper"&gt;Casper&lt;/a&gt; layout looked like back in 2014 (Casper looks completely different today).
Since the blog’s layout inherited some bits of responsive functionality from the layout that I forked from, getting most things working just required updating, fixing, and activating some already existing but inactive parts of the CSS.
My personal site, on the other hand, reimplements the same layout using completely hand-written CSS instead of using the same CSS as the blog; the reason for this difference is because my personal site extends the design language of the layout for a number of more customized pages such as project pages, publication pages, and more.
Getting my personal site’s layout updated with responsive functionality required writing more new CSS from scratch.&lt;/p&gt;

&lt;p&gt;I used to be fairly well versed in web stuff back in high school, but obviously the web world has moved on considerably since then.
I’ve forgotten most of what I knew back then anyway since it’s been well over a decade, so I kind of had to relearn a lot of things.
However, I guess a lot of things in programming are similar to riding a bicycle- once you learn, you never fully forget!
Relearning what I had forgotten was pretty easy, and I quickly figured out that the only really new thing I needed to understand for implementing responsive stuff was the CSS &lt;code class="language-plaintext highlighter-rouge"&gt;@media&lt;/code&gt; rule, which was introduced in 2009 but only gained full support across all major browsers in 2012.
For the totally unfamiliar with web stuff: the &lt;code class="language-plaintext highlighter-rouge"&gt;@media&lt;/code&gt; rule allows for checking things like the width and height and resolution of the current viewport and allows for specifying CSS rule overrides per media query.
Obviously this capability is super useful for responsive layouts; implementing responsive layouts really boils down to just making sure that positions are specified as percentages or relative positions instead of fixed positions and then using &lt;code class="language-plaintext highlighter-rouge"&gt;@media&lt;/code&gt; rules to make larger adjustments to the layout as the viewport size reaches different thresholds.
For example, I use &lt;code class="language-plaintext highlighter-rouge"&gt;@media&lt;/code&gt; rules to determine when to reorganize from a two-column layout into stacked single-column layout, and I also use &lt;code class="language-plaintext highlighter-rouge"&gt;@media&lt;/code&gt; rules to determine when to adjust font sizes and margins and stuff.
The other important part to implementing a responsive layout is to instruct the browser to set the width of the page to follow the screen-width of the viewing device on mobile.
The easiest way to implement this requirement by far is to just insert the following into every page’s HTML headers:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&amp;lt;meta name="viewport" content="width=device-width"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the most part, the new responsive layout actually doesn’t really noticeably change how my blog and personal site look on full desktop browsers and in large windows much, aside from some minor cleanups to spacing and stuff.
However, there is one big noticeable change: I got rid of the shrinking pinned functionality for the navbar.
Previously, as a user scrolled down, the header for my blog and personal site would shrink and gradually transform into a more compact version that would then stay pinned to the top of the browser window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/old_header.gif"&gt;
&lt;img alt="Figure 3: Animation of how the old shrinking, pinned navbar worked." src="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/preview/old_header.gif" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The shrinking pinned navbar functionality was implemented by using a small piece of JavaScript to read how far down the user had scrolled and dynamically adjusting the CSS for the navbar accordingly.
This feature was actually one of my favorite things that I implemented for my blog and site layout!
However, I decided to get rid of it because on mobile, space in the layout is already at a premium, and taking up space that otherwise could be used for content with a pinned navbar just to have my name always at the top of the browser window felt wasteful.
I thought about changing the navbar so that as the user scrolled down, the nav links would turn into a hidden menu accessible through a &lt;a href="https://en.wikipedia.org/wiki/Hamburger_button"&gt;hamburger button&lt;/a&gt;, but I personally don’t actually really like the additional level of indirection and complexity that hamburger buttons add.
So, the navbar is now just fixed and scrolls just like a normal element of each page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/new_header.gif"&gt;
&lt;img alt="Figure 4: Animation of how the new fixed navbar works." src="https://blog.yiningkarlli.com/content/images/2021/May/responsive-layout/preview/new_header.gif" /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think a fixed navbar is fine for now; I figure that if someone is already reading a post on my blog or something on my personal site, they’ll already know where they are and don’t need a big pinned banner with my name on it to remind them of where they are.
However, if I start to find that scrolling up to reach nav links is getting annoying, I guess I’ll put some more thought into if I can come up with a design that I like for a smaller pinned navbar that doesn’t take up too much space in smaller viewports.&lt;/p&gt;

&lt;p&gt;While I was in the code, I also made a few other small improvements to both the blog and my personal site.
On the blog, I made a small improvement for embedded code snippets: embedded code snippets now include line numbers on the side!
The line numbers are implemented using a small bit of JavaScript and exist entirely through CSS, so they don’t interfere with selecting and copying text out of the embedded code snippets.
On my personal site, removing the shrinking/pinning aspect of the navbar actually allowed me to completely remove almost all JavaScript includes on the site, aside from some analytics code.
On the blog, JavaScript is still present for some small things like the code line numbers, some caption features, MathJax, and analytics, but otherwise is at a bare minimum.&lt;/p&gt;

&lt;p&gt;Over time I’d like to pare back what includes my layout uses even further to help improve load times even more.
One of the big motivators for moving my blog from Blogger to Jekyll was simply for page loading speed; under the hood Blogger is a big fancy dynamic CMS, whereas Jekyll just serves up static pages that are pre-generated once from Markdown files.
A few years ago, I similarly moved my personal site from using a simple dynamic templating engine I had written in PHP to instead be entirely 100% static; I now just write each page on my personal site directly as simple HTML and serve everything statically as well.
As a result, my personal site loads extremely fast!
My current layout definitely still has room for optimization though; currently, I use fonts from TypeKit because I like nice typography and having nice fonts like Futura and Proxima Nova is a big part of the overall “look” of the layout.
Fonts can add a lot of weight if not optimized carefully though, so maybe down the line I’ll need to streamline how fonts work in my layout.
Also, since the blog has a ton of images, I think updating the blog to use native browser lazy loading of images through the &lt;code class="language-plaintext highlighter-rouge"&gt;loading="lazy"&lt;/code&gt; attribute on &lt;code class="language-plaintext highlighter-rouge"&gt;img&lt;/code&gt; tags should help a lot with load speeds, but not all major browsers support this attribute yet.
Some day I’d like to get my site down to something as minimal and lightweight as &lt;a href="https://macwright.com/2016/05/03/the-featherweight-website.html"&gt;Tom MacWright’s blog&lt;/a&gt;, but still, for now I think things are in decent shape.&lt;/p&gt;

&lt;p&gt;If for some reason you’re curious to see how all of the improvements mentioned in this post are implemented, the source code for both my blog and my personal site are available on my Github.
Please feel free to either steal any bits of the layout that you may find useful, or if you want, feel free to even fork the entire layout to use as a basis for your own site.
Although, if you do fork the entire layout, I would suggest and really prefer that you put some effort into personalizing the layout and really making it your own instead of just using it exactly as how I have it!&lt;/p&gt;

&lt;p&gt;Hopefully this is the last time for a very long while that I’ll write a blog post about the blog itself; I’m an excruciating slow writer these days, but I currently have the largest simultaneous number of posts near completion that I’ve had in a long time, and I’ll be posting them soon.
As early as later this week I’ll be posting the first part of a two-part series about porting Takua Renderer to 64-bit ARM; get ready for a deep dive into some fun concurrency and atomics-related problems at the x86-64 and arm64 assembly level in this post.
The second part of this series should come soon too, and over the summer I’m also hoping to finish posts about hex-tiling in Takua and on implementing/using different light visibility modes.
Stay-at-home during the pandemic has also given me time to slowly chip away on the long-delayed second and third parts of what was supposed to be a series on mipmapped tiled texture caching, so with some luck maybe those posts will finally appear this year too.
Beyond that, I’ve started some very initial steps on new next-generation from-the-ground-up reimplementations of Takua in CUDA/Optix and in Metal, and I’ve started to dip my toes into Rust as well, so who knows, maybe I’ll have stuff to write about that too in the future!&lt;/p&gt;</description><author>Code &amp;amp; Visuals</author><pubDate>Tue, 18 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://blog.yiningkarlli.com/2021/05/responsive-layout.html</guid></item><item><title>My apps were listed at the top of Developers Tools (iOS and Mac App Stores), I made around $60</title><link>https://www.outcoldman.com/blog/2021/05/18/apple-dev-tools/</link><description>&lt;p&gt;I love building developer tools and utilities. And, I believe the reason is simple, because I am a developer, and
those tools I make, I create first of all for myself. I worked for Microsoft before on Visual Studio.&lt;/p&gt;
&lt;p&gt;I worked in Splunk on some work related to the developers (App Platform). I can say that I had some experience
building developer tools by learning from large corporations.&lt;/p&gt;
&lt;p&gt;For the last 3-4 years, I work for myself. Build a successful B2B company, and now I have the flexibility sometimes
to explore other options.&lt;/p&gt;</description><author>outcoldman</author><pubDate>Tue, 18 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.outcoldman.com/blog/2021/05/18/apple-dev-tools/</guid></item><item><title>Is it worth my time?</title><link>https://holzer.online/articles/is-it-worth-my-time/index.html</link><description>&lt;p&gt;
        About twenty-five centuries ago Hippocrates of Kos famously stated that
        &lt;a href="https://en.wikipedia.org/wiki/Ars_longa,_vita_brevis"&gt;life is short,
            while art is long&lt;/a&gt;. Most knowledge workers can very much relate to that.
        While the body of knowledge in a field is ever growing,
        the time available to an individual for deliberate learning activities is limited severely.
    &lt;/p&gt;

    &lt;p&gt;
        I use the following criteria to assess if a topic is worth spending my time on it.
    &lt;/p&gt;

    &lt;h2&gt;Longevity and Transferability&lt;/h2&gt;
    &lt;p&gt;
        I prefer evergreen knowledge and focus on topics that are not bound to a specific technology.
        As a general principle, anything with a version number attached to it should be treated with caution.
        But: Using a specific technology (even if it's non-mainstream or dated) to illustrate a concept is fine.
    &lt;/p&gt;

    &lt;h2&gt;Applicability and personal usefulness&lt;/h2&gt;
    &lt;p&gt;
        Many concepts are long-lived and transfer well, but when the
        chance of applying them is rather slim, then other things should take priority.
        Not applying something is detrimental to retention and time is too valuable
        to spend it on learning something that falls into oblivion.
    &lt;/p&gt;
    &lt;p&gt;
        As a caveat: To some degree, it cannot be known which topics will lend themselves
        to be applied to problems presented to you due to external needs and interests.
        Chance favours the prepared mind.
    &lt;/p&gt;

    &lt;h2&gt;Immediate necessity&lt;/h2&gt;
    &lt;p&gt;
        Not everything which needs to be learned will be a beautiful timeless idea.
        Sometimes I need to grasp a poorly documented functionality of some obscure
        technology to get a task done. It comes down to a judgement call, which is a matter of
        experience and confidence, how deep to go. It can be beneficial to build a real understanding
        instead of just copy-pasting from the web (which might get the job done but comes at some
        loss of control).
    &lt;/p&gt;
    &lt;h2&gt;Personal interest and commitment&lt;/h2&gt;
    &lt;p&gt;
        There are also a few topics and technologies, to which I'm committed deeply.
        I consider it worthwhile to keep up with them, even if the criteria laid out
        before are to some extent not met. I also regularly reassess my commitment to the topics.
    &lt;/p&gt;</description><author>Fabian's public notepad</author><pubDate>Tue, 18 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://holzer.online/articles/is-it-worth-my-time/index.html</guid></item><item><title>Shortage of metals worsens climate change</title><link>https://stop.zona-m.net/2021/05/shortage-of-metals-worsens-climate-change/</link><description>&lt;p&gt;But only if we use them in the wrong amounts.&lt;/p&gt;</description><author>Welcome to Marco Fioretti's website! on Stop at Zona-M</author><pubDate>Tue, 18 May 2021 02:00:00 GMT</pubDate><guid isPermaLink="true">https://stop.zona-m.net/2021/05/shortage-of-metals-worsens-climate-change/</guid></item><item><title>Hiring vs nurturing managers</title><link>https://world.hey.com/joaoqalves/hiring-vs-nurturing-managers-e44689cb</link><description>&lt;div class="trix-content"&gt;
  &lt;div&gt;Over the last few years, companies put a lot of effort and money into developing their engineering management teams: one-on-ones, yearly goal setting, feedback, coaching, and whatnot. I believe there are two primary reasons for such investments:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;ol&gt;&lt;li&gt;As software eats the world, there are more development teams. As organizations get bigger, they start to create more management structures to make sure they can scale. That increases the demand for such profiles.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Hiring and keeping good engineering talent is a Sisyphean task. The more capable management teams are, the odds of succeeding in doing those tasks increase.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In my journey of becoming an engineering manager, some start-up CTOs and good friends asked me:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote&gt;I'm thinking about giving my teams a structure. What's your experience? What are your recommendations?&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I thought about them for a while. Many of these folks didn't want to set great engineers up for failure by persuading them to get into management roles. At the same time, these leaders were afraid of hiring managers from the outside because they wanted to create opportunities inside their companies. Or, they thought it would be tough for an outsider to get the necessary respect from the engineers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;A natural lack of support&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Generally speaking, I'm very pro-nurturing talent and hiring junior people. Maybe it's because I was one of them not so long ago. However, training managers is more complicated because humans are, at least, one order of magnitude more complex than software. The impacts of doing it wrong may also impact that new manager's reports. That's a big responsibility.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aside from all the challenges of becoming an effective manager, it comes with a significant mental burden if they're doing it with their former colleagues. Are they prepared for having tough conversations? I'm talking about performance reviews, salary, or let a former peer go. If these folks don't have proper support, there's a high chance their experience becomes a fiasco. One could think this aspect should not be an issue because they'd have support from their CTOs. The reality, though, is trickier. Sometimes, these CTOs are also in a learning process themselves. Managing individuals is one thing. Managing managers is another thing. So, most of the time, new engineering managers are left to their fate.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;The golden ratio and the right incentives&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If I were a CTO somewhere, I'd apply a "golden ratio" to craft my management team: 2/3 hired, 1/3 nurtured. The rationale behind this is to get people that could get my back and, at the same time, help the rest of the crew navigate their new roles. There are a lot of things to learn: how to give feedback effectively, how to challenge the team, help to create an engineering ladder, and to level individual contributors (ICs), etc.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I believe this ratio also creates internal opportunities without forcing many great individual contributors to become a manager. It sends a message to the organization. We're hiring the best people we can find to fill these roles. We want them to help &lt;em&gt;you&lt;/em&gt;. Likewise, I recommend this to be part of a strategy where individual contributors have a proper career path and a seat at the table.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another consideration is to carefully design is a rollback plan if the transition to a management position isn't successful. That's where nurturing managers shine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The last incentive to get right is also the more contentious. Nurturing managers means that you need to think about their compensations. There are some tricky parts to doing it right, though. Managers are &lt;em&gt;accountable&lt;/em&gt; for their teams' outcomes and frequently need to go above and beyond in hiring, budgeting, fighting for salary raises, etc. Their impact is, automatically, bound to their team's. Hence, one could argue that these responsibilities should also come with a larger paycheck. Nevertheless, nowadays, there's a tendency to avoid salary differences between ICs and managers working at the same &lt;em&gt;level&lt;/em&gt;. It's a great way to keep your engineers doing what they do best and, at the same time, to make sure that wanna-be managers take that step as a career change.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Wouldn't you like an opportunity in the same company instead?&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm not the best one to talk about it. I left a company to get my first management role elsewhere. That's usually against the book, but I also see some advantages of doing so. Starting fresh and not having to manage your previous peers are my top ones. However, trying a new role in a new company can be twice as difficult. And, probably, there's no &lt;em&gt;rollback&lt;/em&gt; available.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I understand the sentiment of having opportunities at a company one feels comfortable. As time passes, we also need to acknowledge there's only one CTO, a handful of directors, a few dozens of managers. So, looking for an opportunity elsewhere that allows you to grow is something natural in one's career.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm not one of those folks who think everyone needs to pass the same bar as I did — &lt;em&gt;au contraire&lt;/em&gt;. I'm not even advocating to change roles and jobs at the same time. But, if I need to set up a management team from the ground up, I have it clear: 2/3 hiring, 1/3 nurturing. That's it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;— João&lt;/div&gt;
&lt;/div&gt;</description><author>João Alves</author><pubDate>Mon, 17 May 2021 22:52:49 GMT</pubDate><guid isPermaLink="true">https://world.hey.com/joaoqalves/hiring-vs-nurturing-managers-e44689cb</guid></item><item><title>Revolutionizing Audio with Descript and Temporal</title><link>https://www.swyx.io/temporal-descript-case-study</link><description>A case study I published for my work at Temporal</description><author>swyx's site RSS Feed</author><pubDate>Mon, 17 May 2021 21:36:38 GMT</pubDate><guid isPermaLink="true">https://www.swyx.io/temporal-descript-case-study</guid></item><item><title>Exploring ZooKeeper-less Kafka</title><link>https://www.morling.dev/blog/exploring-zookeeper-less-kafka/</link><description>&lt;div class="toc" id="toc"&gt;
&lt;div id="toctitle"&gt;Table of Contents&lt;/div&gt;
&lt;ul class="sectlevel1"&gt;
&lt;li&gt;&lt;a href="#_trying_zk_less_kafka_yourself"&gt;Trying ZK-less Kafka Yourself&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_taking_brokers_down"&gt;Taking Brokers Down&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#_wrap_up"&gt;Wrap-Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Sometimes, less is more.
One case where that’s certainly true is dependencies.
And so it shouldn’t come at a surprise that the &lt;a href="https://kafka.apache.org/"&gt;Apache Kafka&lt;/a&gt; community is eagerly awaiting the removal of the dependency to the &lt;a href="https://zookeeper.apache.org/"&gt;ZooKeeper&lt;/a&gt; service,
which currently is used for storing Kafka metadata (e.g. about topics and partitions) as well as for the purposes of leader election in the cluster.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;The Kafka improvement proposal &lt;a href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-500%3A+Replace+ZooKeeper+with+a+Self-Managed+Metadata+Quorum"&gt;KIP-500&lt;/a&gt;
("Replace ZooKeeper with a Self-Managed Metadata Quorum")
promises to make life better for users in many regards:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Better getting started and operational experience by requiring to run only one system, Kafka, instead of two&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Removing potential for discrepancies of metadata state between ZooKeeper and the Kafka controller&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Simplifying configuration, for instance when it comes to security&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Better scalability, e.g. in terms of number of partitions; faster execution of operations like topic creation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description><author>Gunnar Morling</author><pubDate>Mon, 17 May 2021 20:45:00 GMT</pubDate><guid isPermaLink="true">https://www.morling.dev/blog/exploring-zookeeper-less-kafka/</guid></item><item><title>initLogs 3: Learnings From My Experience on How to Learn Programming</title><link>https://www.unsungnovelty.org/posts/05/2021/initlogs-3-learnings-from-my-experience-on-how-to-learn-programming/</link><description>&lt;p&gt;When you start your programming journey, you ask for recommendations from friends, colleagues or anybody who knows how to program. Either that or you just search online on how to learn programming. The recommendations would be something which you are blindly jumping into with no idea what to expect, in the hope that it will just “click”. This does work, sometimes. Especially if you have a mentor to help you out. Like a colleague in your company or a friend.&lt;/p&gt;
&lt;p&gt;You might have heard stories of people who have failed several times in programming before “it clicked”. But what they meaning is the time they took to figure out what they want to learn or specialise within software development. Like front-end, back-end, databases or full stack. Data visualisation, data analysis or web application security. This is possible only by moving forward with your journey to learn programming. And once you do, learning the actual thing like learning JavaScript for front-end development for instance needs less time comparatively. Because now you know what to do, which materials to use so that you can learn faster and move fast towards your goal.&lt;/p&gt;
&lt;p&gt;There are things that I found repeatable during my journey on how to learn programming. I hope this saves your time and help you learn programming faster.&lt;/p&gt;
&lt;h2 id="dont-ask-advice-from-too-many-people"&gt;Don&amp;rsquo;t ask advice from too many people&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re just starting your journey on learning to program, don&amp;rsquo;t ask advises from too many people. It will not help you. Only confuse you more and could slow down your progress. People will recommend so many different ways and methods to learn or materials to use. So ask a very few people and try those methods and materials and see if it is working for you. Remember, the point is to learn and understand what is working &lt;em&gt;for you&lt;/em&gt;, not blindly follow someone&amp;rsquo;s advice. It doesn&amp;rsquo;t matter how successful the person recommending something to you is. If it is not working for you just try something else. It will save you a world of time and help you not dread learning.&lt;/p&gt;
&lt;h2 id="champions-bias"&gt;Champion&amp;rsquo;s bias&lt;/h2&gt;
&lt;p&gt;Another important thing I wanna talk about is the champion&amp;rsquo;s bias or success bias. Champion&amp;rsquo;s bias or success bias is the inability of a person to understand and think from the point of view of a beginner or novice. The champion&amp;rsquo;s bias happens mainly because a person is more experienced now and forgot how it is to be a beginner. This means their recommendations and advises could be &lt;em&gt;from their level of experience&lt;/em&gt; meaning it could waste several hours of us beginners. This effects you mainly when they recommend study materials or course materials. They will be extremely hard for you to grasp and costs you a lot of time. So always keep this in mind when you are talking to an experienced professional. Teaching is a lot harder than learning things for yourself. You can always be a very good programmer and still suck at teaching.&lt;/p&gt;
&lt;h2 id="use-a-material--method-that-works-for-you"&gt;Use a material / method that works for you&lt;/h2&gt;
&lt;p&gt;If you have reached until this post, you probably have a course, a material or a method that you are using to learn how to program. Like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Video courses: Like a Udemy course or Youtube videos .&lt;/li&gt;
&lt;li&gt;Books: Something like Eloquent JavaScript or Learn Python the hard way.&lt;/li&gt;
&lt;li&gt;Exercise based learning: Like freecodecamp.org or exercism.io&lt;/li&gt;
&lt;li&gt;Learning through building projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it&amp;rsquo;s working, great! But what if it isn&amp;rsquo;t? What if you really dread opening that book or that video course? People are recommending things that worked &lt;em&gt;for them&lt;/em&gt;. It doesn&amp;rsquo;t mean they will work for you too. So if the material or the the method is not working for you, it means it is an issue with the material or the method. &lt;em&gt;&lt;strong&gt;NOT you&lt;/strong&gt;&lt;/em&gt;. Find a method or material that works for you. I personally use a mix of videos and learning through building as my preferred way. So try out things and if it is working, great! But if not, do not stick with it and make your life miserable. It doesn&amp;rsquo;t matter who recommended it to you, if it is not working for you, it just means it is not working for you. So move on. &lt;em&gt;Fail fast to learn fast&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="stay-away-from-eloquent-javascript-if-you-are-a-beginner-to-programming-or-javascript"&gt;Stay away from Eloquent JavaScript if you are a beginner to programming or JavaScript!&lt;/h2&gt;
&lt;p&gt;Most of us beginners who are learning JavaScript would have heard the book &lt;a href="https://eloquentjavascript.net/"&gt;Eloquent JavaScript&lt;/a&gt;. Recommended by people as a beginner friendly book. I have wasted countless amount of time and energy trying to make sense of this book. This was recommended to me by two experienced engineers. This was also recommended by the bootcamp which I was planning to get into before the Covid-19 happened. So it has to be good right? No! It has been the main reason for me getting stuck. Still, I see this recommended everywhere by experienced people, especially since this is a free book. This is not an unpopular opinion. There are so many people who are like me and have got stuck with this book. So if someone recommend you this book and you can&amp;rsquo;t follow, don&amp;rsquo;t stress yourself and waste too much time on it. Try a different resource.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Alt Text" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c8s6mxqcz7xu8ke2szmy.png" /&gt;
&lt;em&gt;This is just one tweet. Reading &lt;a href="https://www.goodreads.com/book/show/8910666-eloquent-javascript#other_reviews"&gt;reviews of Eloquent JavaScript in Goodreads&lt;/a&gt; will give you a clear indication on how a beginner vs experienced programmer perceives the book.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="tutorial-hell"&gt;Tutorial hell&lt;/h2&gt;
&lt;p&gt;It is common for beginners to get stuck with tutorials or exercises in loop. This is when you keep doing those exercises or going through course materials without doing anything with the information that you have learned. It can also be very hard to get out of this loop. Mainly because you will want to finish the entire course before doing anything with it. This is why course materials these days include projects after teaching you the next topic.&lt;/p&gt;
&lt;p&gt;Apart from building things, teaching by writing a blog post or by creating a YouTube video about what you learnt is also a great way to revise. These methods will challenge you to understand the topic you&amp;rsquo;ve just learned thoroughly. You will be surprised by the amount of information you are unable to explain properly when you start creating a video or starts writing a blog post. Doing this will make you learn topics thoroughly.&lt;/p&gt;
&lt;h2 id="difference-between-programming-and-coding"&gt;Difference between programming and coding&lt;/h2&gt;
&lt;p&gt;Programming is about thinking and understanding how to do and coding is about what to do. These are two different things. To be a bit more clear, programming is about the thought process on how to solve a problem and coding is about the syntax to write once you know how to solve the problem. We use the word coder and programmer interchangeably. But as a beginner, you should strive to be a programmer and not a coder. You should be prioritising how to be a programmer over becoming a coder. Otherwise you will know what syntax to write but won&amp;rsquo;t know how to write it. &lt;a href="https://www.youtube.com/watch?v=azcrPFhaY9k"&gt;Andy Harris&amp;rsquo;s talk on How to begin thinking like a programmer&lt;/a&gt; explains this very well. I really recommend you watch it. It is one the best video I have come across and I re-watch it from time to time.&lt;/p&gt;
&lt;h2 id="community-is-important"&gt;Community is important!&lt;/h2&gt;
&lt;p&gt;What I don&amp;rsquo;t see people talk enough about this. It is important to be a part of a community while learning to program. freeCodeCamp has their forum. &lt;a href="https://www.theodinproject.com/"&gt;The Odin Project&lt;/a&gt; has a Discord server which is faster than a forum for immediate queries. It is very useful to be a part of a community and to get involved with the community. You will learn about different point of views and see the problems others are facing. It is also a great place for you to find help and even make friends. So I recommend you find a community while you are learning to program. It could be the community channel of your courseware, a Telegram channel, Forum or even a subreddit. But do find it cos it will help a lot in your journey instead of doing a solo ride.&lt;/p&gt;
&lt;h2 id="productivity-tips-as-you-learn"&gt;Productivity tips as you learn&lt;/h2&gt;
&lt;p&gt;Here are some of the productivity tips and advises that I found useful. Maybe it will help you too.&lt;/p&gt;
&lt;h3 id="it-is-ok-to-search-online-for-tips"&gt;It is OK to search online for tips&lt;/h3&gt;
&lt;p&gt;I always had the feeling that I have to be able to do the exercises entirely based the material I was following. Searching online was thought as something bad. So when you are stuck, it&amp;rsquo;s OK to search online. About how to loop through an array, a better explanation to higher order functions. But Just search online for these building blocks. It is OK.&lt;/p&gt;
&lt;h3 id="switch-between-two-tasks-to-be-more-productive"&gt;Switch between two tasks to be more productive&lt;/h3&gt;
&lt;p&gt;When you are on a problem or exercise and has been stuck for a while, you eventually might dread the problem. You will also end up wasting the entire day (or days) on it without being productive at all. The solution I found is to have a secondary task which you want to do when you don&amp;rsquo;t feel like doing your primary task and toggle between the two. &lt;em&gt;I am NOT recommending multi tasking so don&amp;rsquo;t take two big projects and switch between them or don&amp;rsquo;t learn two programming languages at the same time!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Find a secondary task which is inferior in importance and priority but still important enough. Like writing a blog post. The idea is to switch back to your primary task after a while. This way you have less wasted time and will be more productive.&lt;/p&gt;
&lt;h3 id="accepting-failures-is-part-of-learning"&gt;Accepting failures is part of learning&lt;/h3&gt;
&lt;p&gt;Following a material alone won&amp;rsquo;t help you learn things faster. You also have to learn when to accept failure while learning new things. So that you won&amp;rsquo;t waste a whole day (or days) on one problem. &lt;em&gt;Make sure you give your 100% before accepting defeat&lt;/em&gt;. Try go for a walk and if it&amp;rsquo;s late night, sleep it off. Or like I said before, switch your task or search online for tips etc. And if nothing works, accept defeat and ask for help with the community or your friend / mentor.&lt;/p&gt;
&lt;h2 id="resources-i-like-for-learning-programming"&gt;Resources I like for learning programming&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.theodinproject.com/"&gt;The Odin Project&lt;/a&gt;: The complete and best resource I have found for learning programming or to learn web development / full stack development. They offer a full stack developer path for JavaScript or Ruby on Rails. Their best part is, instead of creating materials for their curriculum like everybody else, they curate the best resources available online and let&amp;rsquo;s you track the progress through their website. Their Discord server will unlock a great community which is helpful and resourceful.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.freecodecamp.org/"&gt;freeCodeCamp&lt;/a&gt;: If you love exercise based learning, you will love freeCodeCamp. They are home to a lot of exercises and I use fCC to practice algorithms and data structures among other things. They also have a forum which you should be part of if you are them. They offer certifications and courses for a wide range of specialities and is free.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.udemy.com/course/the-complete-javascript-course/"&gt;The complete JavaScript course by Jonas Schmedtmann&lt;/a&gt;: This is the only paid course I have tried and it turned out to be amazing. This course dives deep into how JavaScript language works behind the scenes. It is focused on front end based exercises. The only caveat is that this course includes JavaScript only content. So if you don&amp;rsquo;t know enough HTML or CSS, you might feel out of place. There is an HTML &amp;amp; CSS course by Jonas if you wanna take that route and pay for another course. Or just learn enough HTML and CSS through W3Schools. ;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.w3schools.com/"&gt;W3Schools&lt;/a&gt;: W3Schools is best for getting started with web development quickly. While I didn&amp;rsquo;t use W3Schools as a standalone resource to learn programming or coding, I used it to get a quick start with HTML, CSS and JavaScript for web development. I found it very useful and beginner friendly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://exercism.io/"&gt;Exercism&lt;/a&gt;: This is an exercise based learning resource which cater to a wide range of languages. If you are into exercises, you will find it at home. I switch between freeCodeCamp and Exercism for their exercises.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.websiteplanet.com/blog/html-guide-beginners/"&gt;Basic HTML for Beginners&lt;/a&gt;: A step-by-step instruction for beginners who want to understand HTML. This is a good first step guide that can walk you through the basics without overwhelming you.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="secret-sauce-to-learn-programming"&gt;Secret sauce to learn programming?!&lt;/h2&gt;
&lt;p&gt;&lt;img alt="Alt Text" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jx4uwk2bzdhcp0b1y2bn.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Practice, practice and…… PRACTICE!&lt;/p&gt;
&lt;p&gt;The secret sauce is practicing over and over again until you become great at programming!&lt;/p&gt;
&lt;h2 id="credits"&gt;Credits&lt;/h2&gt;
&lt;p&gt;Thank you Nora for sharing the HTML for Beginners resource with me. I hope this addition helps more people.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;If you like the content, think I am wrong about something or have any questions, drop a comment or DM me on twitter &lt;a href="https://twitter.com/unsungNovelty"&gt;@unsungnovelty&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description><author>unsungNovelty</author><pubDate>Mon, 17 May 2021 15:34:30 GMT</pubDate><guid isPermaLink="true">https://www.unsungnovelty.org/posts/05/2021/initlogs-3-learnings-from-my-experience-on-how-to-learn-programming/</guid></item><item><title>TIL that creating Markdown from HTML is hard</title><link>https://www.zufallsheld.de/2021/05/17/til-that-creating-markdown-from-html-is-hard/</link><description>&lt;p&gt;Today I wanted to take the text of a Confluence page and convert it to&amp;nbsp;Markdown.&lt;/p&gt;
&lt;p&gt;Getting the text was rather easy (after some duckduckgoing): Confluence provides a &lt;span class="caps"&gt;REST&lt;/span&gt;-&lt;span class="caps"&gt;API&lt;/span&gt; that returns among other things the content of pages in json. Here&amp;#8217;s the curl-command to do&amp;nbsp;this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl "https …&lt;/code&gt;&lt;/p&gt;</description><author>zufallsheld</author><pubDate>Mon, 17 May 2021 13:39:00 GMT</pubDate><guid isPermaLink="true">https://www.zufallsheld.de/2021/05/17/til-that-creating-markdown-from-html-is-hard/</guid></item><item><title>Emacs plus –with-native-comp</title><link>https://xenodium.com/emacs-plus-with-native-comp</link><description>&lt;p&gt;&lt;img alt="" src="https://xenodium.github.io/images/emacs-plus-with-native-comp/brew-native-comp.png" /&gt;&lt;/p&gt;
&lt;p&gt;I'm a big fan of &lt;a href="https://d12frosted.io/"&gt;Boris Buliga&lt;/a&gt;'s &lt;a href="https://github.com/d12frosted/homebrew-emacs-plus"&gt;Emacs Plus&lt;/a&gt; &lt;a href="https://brew.sh/"&gt;homebrew&lt;/a&gt; recipe for customizing and installing Emacs builds on macOS.&lt;/p&gt;
&lt;p&gt;For a little while, I took a detour and built Emacs myself, so I could enable &lt;a href="https://twitter.com/Koral_001"&gt;Andrea Corallo&lt;/a&gt;'s fantastic &lt;a href="http://akrl.sdf.org/gccemacs.html"&gt;native compilation&lt;/a&gt;. I documented the steps &lt;a href="https://xenodium.com/trying-out-gccemacs-on-macos/"&gt;here&lt;/a&gt;. Though it was fairly straightforward, I did miss Emacs Plus's convenience.&lt;/p&gt;
&lt;p&gt;I had been meaning to check back on Emacs Plus for native compilation support. Turns out, it was merged back in &lt;a href="https://github.com/d12frosted/homebrew-emacs-plus/pull/188"&gt;Dec 2020&lt;/a&gt;, and it works great!&lt;/p&gt;
&lt;p&gt;Enabling native compilation is simple (just use &lt;em&gt;–with-native-comp&lt;/em&gt;). As a bonus, you get all the Emacs Plus goodies. I'm loving &lt;em&gt;–with-elrumo2-icon&lt;/em&gt;, enabling a spiffy icon to go with macOS Big Sur. &lt;em&gt;–with-no-frame-refocus&lt;/em&gt; is also handy to &lt;a href="https://xenodium.com/no-emacs-frame-refocus-on-macos/"&gt;avoid refocusing other frames&lt;/a&gt; when another one is closed.&lt;/p&gt;
&lt;p&gt;In any case, here's the minimum needed to install Emacs Plus with native compilation support enabled:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-{.bash"&gt;brew tap d12frosted/emacs-plus
brew install emacs-plus@28 --with-native-comp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sit tight. Homebrew will build and install some chunky dependencies (including gcc and libgccjit).&lt;/p&gt;
&lt;p&gt;Note: Your init.el needs tweaking to take advantage of native compilation. See my &lt;a href="https://xenodium.com/trying-out-gccemacs-on-macos/"&gt;previous post&lt;/a&gt; for how I set mine, or go straight to &lt;a href="https://github.com/xenodium/dotsies/blob/main/emacs/features/fe-package-extensions.el#L19"&gt;my config&lt;/a&gt;.&lt;/p&gt;</description><author>xenodium.com @alvaro</author><pubDate>Mon, 17 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://xenodium.com/emacs-plus-with-native-comp</guid></item><item><title>ShellHistory - Full Text Search, Backup, Synchronize and Organize your shell history</title><link>https://www.outcoldman.com/blog/2021/05/17/shellhistory/</link><description>&lt;p&gt;I am excited to show my new application, ShellHistory. I can say that I started working on it four years ago when I
published my little shell script &lt;a href="https://www.outcoldman.com/en/archive/2017/07/19/dbhist/"&gt;dbhist&lt;/a&gt;. That script was
convenient. It kept shell history from many macs for the last years (close to 100,000 records). But it was a time
for an upgrade.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://loshadki.app/blog/2021-05-17-shellhistory/"&gt;Read more at loshadki.app&amp;hellip;&lt;/a&gt;&lt;/p&gt;</description><author>outcoldman</author><pubDate>Mon, 17 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://www.outcoldman.com/blog/2021/05/17/shellhistory/</guid></item><item><title>Seastar's semaphore utility</title><link>https://makedist.com/posts/2021/05/17/seastars-semaphore-utility/</link><description>Everyone&amp;rsquo;s favorite concurency control device.</description><author>Noah Watkins</author><pubDate>Mon, 17 May 2021 03:00:00 GMT</pubDate><guid isPermaLink="true">https://makedist.com/posts/2021/05/17/seastars-semaphore-utility/</guid></item><item><title>A boilerplate for SSR'd Vite, React 17, and TypeScript 4.3</title><link>https://jonlu.ca/posts/vite</link><description>A minimal server-rendered starter setup for Vite, React 17, and TypeScript 4.3.</description><author>JonLuca's Blog</author><pubDate>Sun, 16 May 2021 19:49:29 GMT</pubDate><guid isPermaLink="true">https://jonlu.ca/posts/vite</guid></item><item><title>Mixing Metal with UIScrollView</title><link>https://whackylabs.com/metal/2021/05/16/metal-scroll-view/</link><description>&lt;p&gt;About mixing &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollView&lt;/code&gt; with &lt;code class="language-plaintext highlighter-rouge"&gt;MTKView&lt;/code&gt;. This is the trick I learned from &lt;a href="https://developer.apple.com/videos/play/wwdc2012/223/"&gt;WWDC 2012 Enhancing User Experience with Scroll Views&lt;/a&gt; session that talks about mixing &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollView&lt;/code&gt; with OpenGL. The idea is to simply use the &lt;code class="language-plaintext highlighter-rouge"&gt;MTKView&lt;/code&gt; to render whatever metal content we would like and then use the &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollView&lt;/code&gt; to provide with the scrolling effect. The benefit of using &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollView&lt;/code&gt; is that we get exactly the same dragging and bounciness behavior that iOS users expect.&lt;/p&gt;

&lt;p&gt;So, for example let’s say we have a &lt;code class="language-plaintext highlighter-rouge"&gt;MTKView&lt;/code&gt; that renders 3 cubes on screen and at any given time only of the cube is visible on screen. To prepare for scrolling we need to expose a &lt;code class="language-plaintext highlighter-rouge"&gt;CGPoint scrollOffset&lt;/code&gt; property that can scroll the content.&lt;/p&gt;

&lt;div class="language-objc highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;CubeView&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MTKView&lt;/span&gt;
&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonatomic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CGPoint&lt;/span&gt; &lt;span class="n"&gt;scrollOffset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On top of it we also need to handle touch events for other things such as changing the background color at tap.&lt;/p&gt;

&lt;div class="language-objc highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt; &lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;touchesEnded&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;NSSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UITouch&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;touches&lt;/span&gt; &lt;span class="nf"&gt;withEvent&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;UIEvent&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;event&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt; &lt;span class="nf"&gt;_randomizeColor&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now since in our app, we only ever scroll horizontally, we can simply forward the &lt;code class="language-plaintext highlighter-rouge"&gt;x&lt;/code&gt; value to the &lt;code class="language-plaintext highlighter-rouge"&gt;Renderer&lt;/code&gt;&lt;/p&gt;

&lt;div class="language-objc highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;setScrollOffset&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;CGPoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;scrollOffset&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;_renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scrollOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then in the &lt;code class="language-plaintext highlighter-rouge"&gt;Renderer&lt;/code&gt; we can use this &lt;code class="language-plaintext highlighter-rouge"&gt;contentOffset&lt;/code&gt; value to calculate the &lt;em&gt;view matrix&lt;/em&gt;&lt;/p&gt;

&lt;div class="language-objc highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;matrix_float4x4&lt;/span&gt; &lt;span class="n"&gt;modelMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;viewOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_contentOffset&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;viewX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kMinX&lt;/span&gt; &lt;span class="k"&gt;+&lt;/span&gt; &lt;span class="n"&gt;viewOffset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kMaxX&lt;/span&gt; &lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="n"&gt;kMinX&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;matrix_float4x4&lt;/span&gt; &lt;span class="n"&gt;viewMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matrix4x4_translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;uniforms&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;modelViewMatrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matrix_multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewMatrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modelMatrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need to multiply with &lt;code class="language-plaintext highlighter-rouge"&gt;-1&lt;/code&gt; because we need to move the content in the opposite direction of the scroll. Remember the &lt;code class="language-plaintext highlighter-rouge"&gt;contentOffset&lt;/code&gt; gives a increasing &lt;code class="language-plaintext highlighter-rouge"&gt;+ve&lt;/code&gt; value from &lt;code class="language-plaintext highlighter-rouge"&gt;0&lt;/code&gt; as we go towards right, while the content is actually moving left.&lt;/p&gt;

&lt;p&gt;Next we need to actually add the &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollView&lt;/code&gt;. The simplest way could be to just add a &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollView&lt;/code&gt; on top of &lt;code class="language-plaintext highlighter-rouge"&gt;CubeView&lt;/code&gt; and use the &lt;code class="language-plaintext highlighter-rouge"&gt;UIScrollViewDelegate&lt;/code&gt; to forward the &lt;code class="language-plaintext highlighter-rouge"&gt;contentOffset&lt;/code&gt;.&lt;/p&gt;

&lt;div class="language-objc highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;viewDidLoad&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;super&lt;/span&gt; &lt;span class="nf"&gt;viewDidLoad&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  
  &lt;span class="n"&gt;_cubeView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CubeView&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;CGRect&lt;/span&gt; &lt;span class="n"&gt;cubeVwFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_cubeView&lt;/span&gt; &lt;span class="nf"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;CGSize&lt;/span&gt; &lt;span class="n"&gt;contentSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CGSizeMake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CGRectGetWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubeVwFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CGRectGetHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubeVwFrame&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;UIScrollView&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scrollView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;UIScrollView&lt;/span&gt; &lt;span class="nf"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;initWithFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cubeVwFrame&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contentSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;showsHorizontalScrollIndicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;scrollView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&