<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>tautology.town</title>
    <description>See you &apos;round; tautology.town 🐸
</description>
    <link>http://tautology.town/</link>
    <atom:link href="http://tautology.town/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 10 Sep 2026 03:15:11 +0000</pubDate>
    <lastBuildDate>Thu, 10 Sep 2026 03:15:11 +0000</lastBuildDate>
    <generator>Jekyll v4.3.4</generator>
    
      <item>
        <title>A few reasons to like Ruby</title>
        <description>&lt;style&gt;

@keyframes slide-left {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0%);
  }
}

.playground {
  display: flex;
  flex-direction: column;
  margin: 1.5rem 0;
  gap: 0.5rem;
}

.playground-row {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  overflow: hidden;
}

/* Ruby snippet */
.playground .editor {
  flex: 3;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border-radius: 4px;
  overflow: hidden;
  border: 1px solid var(--code-border);
}

.playground .editor .CodeMirror {
  flex: 1;
  height: auto;
  box-shadow: none;
  font-size: 0.8em;
  /*padding: 0.5rem;*/
  font-family: var(--code-font);
}

/* console */
.playground .console {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 0.5rem;
  overflow: auto;
  border: none;
  border-radius: 4px;
  border: 1px solid var(--code-border);
  white-space: pre-wrap;
}

@media (max-width: 600px) {
  .playground-row { flex-direction: column; }
  .toc { columns: 1; }
}

.run-button {
  font-family: inherit;
  border: 1px solid var(--code-border);
  box-shadow: inset -1px -1px 2px var(--run-code-shadow);
  background: var(--code-border);
  color: var(--run-code-text);
  border-radius: 4px;
  font-weight: bold;
  /*color: inherit;*/
  /*opacity: 0.8;*/
  font-size: 1rem;
  padding: 0.5rem 1rem;
  cursor: pointer; 
  flex: 1;
}

.run-button:active {
  box-shadow: inset 1px 1px 2px var(--run-code-shadow);
}

.toc {
  columns: 2;
}

.toc ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

&lt;/style&gt;

&lt;p&gt;&lt;a href=&quot;https://www.ruby-lang.org/en/&quot;&gt;Ruby&lt;/a&gt; is my favorite programming language.&lt;/p&gt;

&lt;p&gt;I have worked in Python, Javascript (Typescript also), and Go. I have written C, C++, Java, and Racket. Consistently, Ruby is the language I enjoy the most.&lt;/p&gt;

&lt;p&gt;I like Ruby because it feels “good in the hand”. It is hard to explain, as are all matters of taste, or beauty. I believe Ruby ought to be appreciated like anything else well crafted, the way people obsess over a really nice pen or mechanical keyboards.&lt;/p&gt;

&lt;p&gt;It’s rare to find others who feel the same. Ruby has &lt;a href=&quot;https://trends.google.com/explore?q=%2Fm%2F06ff5&amp;amp;date=all&amp;amp;geo=US&quot;&gt;fallen&lt;/a&gt; from the zeitgeist, and developers aren’t as interested in learning it anymore. At &lt;a href=&quot;https://www.recurse.com&quot;&gt;Recurse Center&lt;/a&gt; last year, I was the only one writing any Ruby.&lt;/p&gt;

&lt;p&gt;For the unacquainted, here are a few reasons to like Ruby:&lt;/p&gt;

&lt;div class=&quot;toc&quot;&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#0-ruby-wants-you-to-be-happy&quot;&gt;0. Ruby wants you to be happy&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#1-expressions-everywhere&quot;&gt;1. Expressions everywhere&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#2-looping-with-numbers&quot;&gt;2. Looping with numbers&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#3-better-lambdas-with-blocks&quot;&gt;3. Better lambdas with blocks&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#4-enumerating-is-easy&quot;&gt;4. Enumerating is easy&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#5-method-pipelines&quot;&gt;5. Method pipelines&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#6-combinatorics&quot;&gt;6. Combinatorics&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#7-single-line-if&quot;&gt;7. Single line if&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#8-meaningful-punctuation&quot;&gt;8. Meaningful punctuation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#9-modules-are-namespaces&quot;&gt;9. Modules are namespaces&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#10-until&quot;&gt;10. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;until&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#11-do-what-works-for-you&quot;&gt;11. Do what works for you&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;

&lt;/div&gt;

&lt;h2 id=&quot;run-the-code&quot;&gt;Run the code&lt;/h2&gt;

&lt;p&gt;All the code snippets in this post run in your browser thanks to &lt;a href=&quot;https://opalrb.com&quot;&gt;Opal&lt;/a&gt;. You can edit them too. Note that there are &lt;a href=&quot;https://github.com/opal/opal/blob/abf15821c36dbc0fbc04ad53226deeb156c669d8/docs/unsupported_features.md?plain=1#L9&quot;&gt;differences&lt;/a&gt; between Opal and Ruby, but they shouldn’t matter here.&lt;/p&gt;

&lt;p&gt;It’s okay if you don’t know any Ruby (and even better if you’re new). You should be able to follow along if you know any modern mainstream language. More important than the language rules, I hope you get a sense of what Ruby is &lt;em&gt;like&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;0-ruby-wants-you-to-be-happy&quot;&gt;&lt;a href=&quot;#0-ruby-wants-you-to-be-happy&quot;&gt;0. Ruby wants you to be happy&lt;/a&gt;&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;For me, the purpose of life is, at least partly, to have joy. Programmers often feel joy when they can concentrate on the creative side of programming, so Ruby is designed to make programmers happy.&lt;/p&gt;

  &lt;p&gt;– Matz, the creator of Ruby, in &lt;a href=&quot;https://web.archive.org/web/20080220073029/http://www.informit.com/articles/article.aspx?p=18225&quot;&gt;2000&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Languages are, in part, philosophical endeavours and are imbued with their creators’ values. Matz has a particular philosophy — Ruby is designed to make you happy.&lt;/p&gt;

&lt;p&gt;This is in contrast to languages that have pragmatic goals or technical constraints, like memory safety, concurrency, or mathematical purity.&lt;/p&gt;

&lt;p&gt;It’s a worthy and inspiring goal, and the single best reason to try out Ruby.&lt;/p&gt;

&lt;h2 id=&quot;1-expressions-everywhere&quot;&gt;&lt;a href=&quot;#1-expressions-everywhere&quot;&gt;1. Expressions everywhere&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Ruby is inspired by Lisp (everyone admires Lisp, right?). Everything evaluates to a value, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt;-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;else&lt;/code&gt;. Functions return their last expression, like in math (and Lisp). No unnecessary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return&lt;/code&gt;s!&lt;/p&gt;

&lt;p&gt;For example, you can do this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;- hashes start comments&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;st&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;nd&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;13&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;rd&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;th&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# functions return last expression&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# puts is Ruby for print&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# all the code snippets are editable! &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# uncomment me and run again&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# puts ordinal(5)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Assigning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suffix&lt;/code&gt; once makes the program’s intent clearer than setting it multiple times.&lt;/p&gt;

&lt;h2 id=&quot;2-looping-with-numbers&quot;&gt;&lt;a href=&quot;#2-looping-with-numbers&quot;&gt;2. Looping with numbers&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Numbers are objects and expose some wonderful methods for looping.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-better-lambdas-with-blocks&quot;&gt;&lt;a href=&quot;#3-better-lambdas-with-blocks&quot;&gt;3. Better lambdas with blocks&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Also &lt;a href=&quot;https://www.artima.com/articles/blocks-and-closures-in-ruby&quot;&gt;inspired by Lisp&lt;/a&gt;, Ruby has a dedicated and elegant syntax for lambdas. This is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_name do |variables| ... end&lt;/code&gt; and the abbreviated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_name { |variables| ... }&lt;/code&gt; above. It’s a “block” of code (a closure) that is passed like a lambda into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method_name&lt;/code&gt; to be invoked within by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yield&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;|variables|&lt;/code&gt; can be omitted if the block takes no arguments.&lt;/p&gt;

&lt;p&gt;In the loop examples above, we’ve been passing blocks into loop methods that are invoked once per iteration. The returning last expression choice works well since blocks are usually short.&lt;/p&gt;

&lt;p&gt;You can use them for:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;context and resource management (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt; in Python)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rack/rack&quot;&gt;middlewares&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;iterators/generators&lt;/li&gt;
  &lt;li&gt;domain specific languages (DSL)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a helper to &lt;a href=&quot;https://github.com/ruby/benchmark&quot;&gt;benchmark&lt;/a&gt; code:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stopwatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;amp;blk means accept a block&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# yield runs the block!&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;finish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopwatch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; 
  &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;array of &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; random numbers&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;took &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; seconds to make&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;first five: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or a compact DSL for &lt;a href=&quot;https://github.com/minitest/minitest&quot;&gt;testing&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Test: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@checks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@failed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# @variables are instance vars&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# run test cases&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@failed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[✓] All &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@checks&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; passed!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[x] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@failed&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; / &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@checks&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; failed!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@failed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=&amp;gt; Check #&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@checks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; failed: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should be &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;vi&quot;&gt;@checks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Can your language do this?&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;addition&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;subtraction&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# fails!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Blocks are very natural to incorporate and use.&lt;/p&gt;

&lt;h2 id=&quot;4-enumerating-is-easy&quot;&gt;&lt;a href=&quot;#4-enumerating-is-easy&quot;&gt;4. Enumerating is easy&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.ruby-lang.org/en/master/Enumerable.html&quot;&gt;Enumerables&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;s, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Range&lt;/code&gt;s, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hash&lt;/code&gt;maps, even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Prime&lt;/code&gt;s) have excellent methods, many of which take blocks:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tally&lt;/code&gt;: tallies the number of each item, my personal favorite. What a great method name!&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce&lt;/code&gt;: classic map/reduce&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reject&lt;/code&gt;: querying&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include?&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any?&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all?&lt;/code&gt;: checking for stuff&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sort&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sort_by&lt;/code&gt;: passing a block can change what it sorts with&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take(n)&lt;/code&gt;: takes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; elements&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each_cons(n)&lt;/code&gt;: sliding window of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; elements&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;each_slice(n)&lt;/code&gt;: tumbling window of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;n&lt;/code&gt; elements&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chunk&lt;/code&gt;: takes a block and groups each chunk by the return value&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lazy&lt;/code&gt; switches an iterator to lazy evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These methods are designed to be chained and return another Enumerable.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;years&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2020&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2025&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;year: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;ss&quot;&gt;leap: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;years&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:leap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;leap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;leap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;366&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;365&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; days&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5-method-pipelines&quot;&gt;&lt;a href=&quot;#5-method-pipelines&quot;&gt;5. Method pipelines&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;A nice part of Javascript is method chaining. This is a good style for data being piped through a series of transforms. Ruby does this even better because:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;everything is an object&lt;/li&gt;
  &lt;li&gt;everything is an expression and returns an object&lt;/li&gt;
  &lt;li&gt;objects come with many useful methods&lt;/li&gt;
  &lt;li&gt;blocks simplify lambdas&lt;/li&gt;
  &lt;li&gt;optional punctuation — methods without arguments can omit parens. No need for a &lt;a href=&quot;https://github.com/tc39/proposal-pipeline-operator&quot;&gt;pipe operator&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;commas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reverse&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reverse&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1234567890&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Powerful methods compose into powerful pipelines.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gatsby&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;In my younger and more vulnerable years my father 
gave me some advice that I&apos;ve been turning over in my mind 
ever since. &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Whenever you feel like criticizing anyone,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; 
he told me, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;just remember that all the people in this world
haven&apos;t had the advantages that you’ve had.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;bigrams&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gatsby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&apos;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flat_map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# every 2 consecutive char in the words&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_cons&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# hashmap sorted as a list of pairs sorted by last item (the value)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;top_bigram&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bigrams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;most common bigram: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_bigram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;seen &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_bigram&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; times&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;6-combinatorics&quot;&gt;&lt;a href=&quot;#6-combinatorics&quot;&gt;6. Combinatorics&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;There are even methods to shuffle, permute, combine, and sample.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;jack&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;queen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;king&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;suits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;spade&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;heart&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;club&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;diamond&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
  &lt;span class=&quot;s2&quot;&gt;&quot;spade&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;heart&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;club&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;diamond&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;red&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a deck has &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; cards&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;drawn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;drawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;a &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; of &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suit&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;s&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;you drew &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; and &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;possible&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;H&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;T&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;repeated_permutation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ways&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:tally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tally&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;outcome&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sample&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;you flipped &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outcome&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;that is 1 of &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ways&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outcome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; ways &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;to get that number of heads and tails&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;7-single-line-if&quot;&gt;&lt;a href=&quot;#7-single-line-if&quot;&gt;7. Single line if&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;A trailing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; conditionally executes a line. Guards read nicely and emphasize early returns.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;max&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Most things in Ruby are expressions, but this is a statement. Ruby is flexible, not dogmatic!&lt;/p&gt;

&lt;h2 id=&quot;8-meaningful-punctuation&quot;&gt;&lt;a href=&quot;#8-meaningful-punctuation&quot;&gt;8. Meaningful punctuation&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Question marks &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; are a nice touch for methods that return booleans, and read nicely in English.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;planets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;Mercury&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Venus&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Earth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Mars&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;s2&quot;&gt;&quot;Jupiter&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Saturn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Uranus&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Neptune&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Pluto&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;any?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;planets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;any?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;z&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Exclamation points &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; are used for methods that modify an object in-place or throw errors.&lt;/p&gt;

&lt;p&gt;Meanwhile parentheses are optional which is great for keeping straightforward method calls visually clean.&lt;/p&gt;

&lt;h2 id=&quot;9-modules-are-namespaces&quot;&gt;&lt;a href=&quot;#9-modules-are-namespaces&quot;&gt;9. Modules are namespaces&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Namespaces are always a good idea. Names resolve relative to modules or you can fully qualify names.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Baseball&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bat&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;EQUIPMENT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Bat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Cave&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bat&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;ANIMALS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Bat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Baseball&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EQUIPMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Baseball&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Bat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Baseball&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EQUIPMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Cave&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Bat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Cave&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ANIMALS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Cave&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Bat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Cave&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ANIMALS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Baseball&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Bat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;10-until&quot;&gt;&lt;a href=&quot;#10-until&quot;&gt;10. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;until&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;In addition to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while&lt;/code&gt; loops, Ruby has its opposite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;until&lt;/code&gt; that is much nicer to read for traversals.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;until&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;right&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;left&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;S&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SLL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SLR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SRL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SRR&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;val&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You could do this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while stack.length &amp;gt; 0&lt;/code&gt; but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;until stack.empty?&lt;/code&gt; reads so much better.&lt;/p&gt;

&lt;h2 id=&quot;11-do-what-works-for-you&quot;&gt;&lt;a href=&quot;#11-do-what-works-for-you&quot;&gt;11. Do what works for you&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Ruby deliberately lets you do things many different ways so you can do what maps better to your mental model. This goes as far as making it easy to introspect and modify the language. It wants to fit your thinking, rather than wanting you to think in terms of it.&lt;/p&gt;

&lt;p&gt;Give &lt;a href=&quot;https://try.ruby-lang.org&quot;&gt;Ruby a try today&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;links&quot;&gt;Links&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;Rails’ take on programmer happiness is &lt;a href=&quot;https://rubyonrails.org/doctrine#optimize-for-programmer-happiness&quot;&gt;worth a read&lt;/a&gt;, as is the rest of its principles: &lt;a href=&quot;https://rubyonrails.org/doctrine&quot;&gt;The Rails Doctrine&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Matz discusses Ruby’s philosophy in a series of interviews from 2003: &lt;a href=&quot;https://www.artima.com/articles/the-philosophy-of-ruby&quot;&gt;The Philosophy of Ruby: A Conversation with Yukihiro Matsumoto, Part I&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;More reasons to like Ruby: &lt;a href=&quot;https://eliseshaffer.com/2023/12/18/i-love-ruby/&quot;&gt;I Love Ruby (Elise Shaffer)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Nice tour of Ruby: &lt;a href=&quot;https://learnxinyminutes.com/ruby/&quot;&gt;Learn Ruby in Y minutes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ruby-lang.org/en/about/&quot;&gt;About Ruby&lt;/a&gt; in the official Ruby docs&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- TODO: vendor these! --&gt;
&lt;script src=&quot;https://cdn.opalrb.com/opal/current/opal.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;https://cdn.opalrb.com/opal/1.8.3/native.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;https://cdn.opalrb.com/opal/current/opal-parser.js&quot;&gt;&lt;/script&gt;

&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css&quot; /&gt;

&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/solarized.min.css&quot; /&gt;

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/ruby/ruby.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;

const template = (index, snippet) =&gt; `
  require &apos;native&apos;

  def puts(*args)
    lines = args.empty? ? [&quot;&quot;] : args.flatten.map { |a| a.to_s }
    text = lines.map { |l| l.end_with?(&quot;\\n&quot;) ? l : l + &quot;\\n&quot; }.join
    $$.putsOverride(${index}, text)
  end

  def p(*args)
    puts(args.map { |arg| arg.is_a?(Array) ? arg.to_s : arg })
  end

${snippet}
`


let editors = []
let consoles = []

function putsOverride(i, text) {
  consoles[i].appendChild(document.createTextNode(text))
}

function run(i) {
  consoles[i].style.display = &apos;block&apos;
  consoles[i].style.animation = &apos;slide-left 0.1s ease-in&apos;
  consoles[i].textContent = &apos;&apos;
  try {
    Opal.eval(template(i, editors[i].getValue()))
  } catch (e) {
    consoles[i].appendChild(document.createTextNode((e &amp;&amp; e.message || e) + &quot;\n&quot;))
  }
}

window.onload = () =&gt; {
  const blocks = Array.from(document.querySelectorAll(&apos;div.highlight&apos;))
  
  const setTheme = () =&gt; {
    let isDark = window.matchMedia(&apos;(prefers-color-scheme: dark)&apos;).matches
    let theme = (isDark) ? &apos;solarized dark&apos; : &apos;solarized light&apos;
    editors.map(ed =&gt; ed.setOption(&apos;theme&apos;, theme))
  }

  blocks.forEach((block, i) =&gt; {
    const code = block.textContent.replace(/\n+$/, &apos;&apos;)

    const playground = document.createElement(&apos;div&apos;)
    playground.className = &apos;playground&apos;
    block.parentNode.replaceChild(playground, block)

    const row = document.createElement(&apos;div&apos;)
    row.className = &apos;playground-row&apos;
    playground.appendChild(row)

    const editorHost = document.createElement(&apos;div&apos;)
    editorHost.className = &apos;editor&apos;
    row.appendChild(editorHost)

    const consoleElement = document.createElement(&apos;pre&apos;)
    consoleElement.className = &apos;console&apos;
    row.appendChild(consoleElement)
    consoles[i] = consoleElement

    editors[i] = CodeMirror(editorHost, {
      value: code,
      mode: &apos;ruby&apos;,
      theme: &apos;solarized dark&apos;,
      lineNumbers: true,
      tabSize: 2,
      indentUnit: 2,
      viewportMargin: Infinity
    })
    
    setTheme()

    const button = document.createElement(&apos;button&apos;)
    button.className = &apos;run-button&apos;
    button.textContent = &apos;Run&apos;
    button.onclick = () =&gt; run(i)
    playground.appendChild(button)

    // hide before run first timer
    consoleElement.style.display = &apos;none&apos;
  })

  window.matchMedia(&apos;(prefers-color-scheme: dark)&apos;)
    .addEventListener(&apos;change&apos;, () =&gt; editors.map(ed =&gt; {
      setTheme()
    })
  )

}

&lt;/script&gt;

</description>
        <pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/06/18/reasons-to-like-ruby.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/06/18/reasons-to-like-ruby.html</guid>
        
        
      </item>
    
      <item>
        <title>What do Visa and Mastercard do? An intro to card networks</title>
        <description>&lt;script src=&quot;https://unpkg.com/mermaid@11.12.0/dist/mermaid.min.js&quot;&gt;&lt;/script&gt;

&lt;!-- TODO: these images are hardcoded for dark mode... is there a better way? --&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;/assets/2026/card-guy-dark.svg&quot; media=&quot;(prefers-color-scheme: dark)&quot; /&gt;
  &lt;img src=&quot;/assets/2026/card-guy.svg&quot; style=&quot;width: 8vw; min-width: 120px; float: right&quot; /&gt;
&lt;/picture&gt;

&lt;p&gt;Most people can recognize the Visa and Mastercard brands. Chances are, you use one of their cards to transact every day. You may have some notion that most places (in the US) take both, but some places only take Visa (e.g. Costco), and vice versa.&lt;/p&gt;

&lt;p&gt;So what do they do? &lt;a href=&quot;https://www.youtube.com/watch?v=lnz2gRPDzrA&quot;&gt;Here’s Visa’s attempt&lt;/a&gt; to answer that question.&lt;/p&gt;

&lt;p&gt;A few things they don’t do&lt;sup id=&quot;fnref:dont-do&quot;&gt;&lt;a href=&quot;#fn:dont-do&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;They aren’t the company that issues the card. Those are called &lt;strong&gt;card issuers&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;They aren’t a bank, though the cards you have are probably issued by one (Chase, Capital One, BofA, etc).&lt;/li&gt;
  &lt;li&gt;They don’t distribute point of sale methods or online checkouts, which are done by &lt;strong&gt;payment processors&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;They aren’t responsible for onboarding or underwriting stores and merchants, known as &lt;strong&gt;merchant acquiring&lt;/strong&gt;. This is done by banks offering merchant accounts, but increasingly offered by modern payment processors (Stripe, Square, Adyen)&lt;sup id=&quot;fnref:payfac&quot;&gt;&lt;a href=&quot;#fn:payfac&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
  &lt;li&gt;They don’t manufacture or print cards.&lt;/li&gt;
  &lt;li&gt;Nor do they manufacture the point of sale hardware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, Visa and Mastercard are &lt;strong&gt;card networks&lt;/strong&gt;&lt;sup id=&quot;fnref:card-network-name&quot;&gt;&lt;a href=&quot;#fn:card-network-name&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, facilitating card transactions by connecting the cardholders and issuers to the merchants and acquirers.&lt;/p&gt;

&lt;p&gt;This forms a &lt;a href=&quot;https://en.wikipedia.org/wiki/Two-sided_market&quot;&gt;two-sided market&lt;/a&gt; of all the participants in a transaction&lt;sup id=&quot;fnref:or-four&quot;&gt;&lt;a href=&quot;#fn:or-four&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;figure&gt;
&lt;pre class=&quot;mermaid wide&quot; id=&quot;key-players&quot;&gt;
---
config:
  theme: &apos;neutral&apos;
  fontFamily: system-ui, -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Oxygen, Ubuntu, Cantarell, &apos;Open Sans&apos;, &apos;Helvetica Neue&apos;, sans-serif;
  nodeSpacing: 20
  rankSpacing: 25
  flowchart: 
    subGraphTitleMargin: {bottom: 20}
    useMaxWidth: true
---
%% For some reason arrows are still showing up in the Obsidian preview, but not in Mermaid online editor
flowchart LR
    subgraph Issuing[**Issuing**]
        direction TB
        CI[Card issuer] --- CH[Cardholder]
    end
    subgraph Acquiring[**Acquiring**]
        direction TB
        MA[Merchant acquirer] --- M[Merchant]
    end
    Issuing ---- CN[**Card network**]
    CN ---- Acquiring
&lt;/pre&gt;
&lt;!-- For some horrible reason, Mermaid.js is fixing the max-width even if useMaxWidth is true --&gt;
&lt;script&gt;
window.addEventListener(&apos;load&apos;, () =&gt; {
  document.querySelectorAll(&quot;.mermaid svg&quot;).forEach(el =&gt; el.style = &quot;&quot;)
})
&lt;/script&gt;
&lt;figcaption&gt;The key players in a card transaction.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The card network’s job is to enable card transactions, and also to grow participation in their networks.&lt;/p&gt;

&lt;p&gt;This boils down to four key responsibilities:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#1-run-the-telecommunications-network&quot;&gt;Run the telecommunications network&lt;/a&gt; to route transaction messages.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#2-coordinate-the-banking-network&quot;&gt;Coordinate the banking network&lt;/a&gt; to move money and settle transactions.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#3-set-incentives&quot;&gt;Set incentives&lt;/a&gt; to encourage the use of the network.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#4-set-rules-and-handle-disputes&quot;&gt;Set and enforce rules&lt;/a&gt; of the network, including a mechanism for disputes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the rest of the discussion, we’ll focus on Visa, as it’s what I’m most familiar with from my years in the payment industry. Mastercard is more or less the same, with different names for things.&lt;/p&gt;

&lt;h1 id=&quot;1-run-the-telecommunications-network&quot;&gt;1. Run the telecommunications network&lt;/h1&gt;

&lt;p&gt;When we talk about networks, we think of the Internet, computers connected together by fiber and &lt;a href=&quot;https://www.submarinecablemap.com/&quot;&gt;deep sea cables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Card networks, being telecommunication networks, are no different. They maintain data centers and lease fiber optics cables to connect issuers and acquirers electronically. At their most basic technical level, Visa’s responsibility is forwarding transaction messages between its participating issuers and acquirers. Mastercard calls this activity “&lt;a href=&quot;https://www.mastercard.com/eea/switching-services/our-technology/transaction.html&quot;&gt;switching&lt;/a&gt;”, seeing itself as a network switch.&lt;/p&gt;

&lt;!-- TODO: support sidenotes/asides https://kau.sh/blog/jekyll-footnote-tufte-sidenote/ --&gt;

&lt;p&gt;Visa takes its data centers very seriously. They are highly secure, redundant, and fitted to survive all kinds of disasters. From &lt;a href=&quot;https://www.networkcomputing.com/data-center-networking/inside-visa-s-data-center&quot;&gt;Inside Visa’s Data Center (Network Computing, 2013)&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The company’s flagship data center, dubbed Operations Center East, or OCE, is a 140,000-square-foot facility that Visa will only say is located “somewhere along the Eastern seaboard.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Not surprisingly, the facility, which is also designed to withstand earthquakes and gale-force winds up to 170 miles per hour, is locked down like a digital Fort Knox. The roads entering the complex have hydraulic bollards that can shoot up fast enough to stop a vehicle traveling up to 50 miles per hour dead in its tracks. (The road is too curvy to drive safely at higher speeds.) Visitors must pass through a security gate, be cleared by roving security teams, and then be subjected to a biometric scan before being admitted.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And a 2012 headline from &lt;a href=&quot;https://web.archive.org/web/20120330102616/https://www.usatoday.com/tech/news/story/2012-03-25/visa-data-center/53774904/1/&quot;&gt;USA Today&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Top secret Visa data center banks on security, even has moat&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That top secret location? In &lt;a href=&quot;https://maps.app.goo.gl/tPEDWcTiY621sZz37&quot;&gt;Ashburn, Virginia&lt;/a&gt;, conveniently located by Topgolf and Trader Joe’s.&lt;/p&gt;

&lt;div&gt;
&lt;!-- From https://stackoverflow.com/a/19360305/7971276 --&gt;
&lt;figure class=&quot;image&quot;&gt;
&lt;!-- Need to | escape the description so I can include quotation marks --&gt;
&lt;img src=&quot;/assets/2026/visa-oce-satellite.png&quot; alt=&quot;I think the &amp;quot;moat&amp;quot; is the pool of water on the top center-left.&quot; /&gt;
  &lt;figcaption&gt;I think the &quot;moat&quot; is the pool of water on the top center-left.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;

&lt;h2 id=&quot;authorization-and-clearing&quot;&gt;Authorization and clearing&lt;/h2&gt;

&lt;p&gt;When a card is used, the card network routes a transaction request, known as an &lt;strong&gt;authorization&lt;/strong&gt;, from the merchant to the issuer. The issuer then approves or declines the request in a response. Card numbers, also known as &lt;strong&gt;Primary Account Numbers (PANs)&lt;/strong&gt;, are used like IP addresses&lt;sup id=&quot;fnref:pan-exhaustion&quot;&gt;&lt;a href=&quot;#fn:pan-exhaustion&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. The first 6 to 8 digits of the PAN identifies the card issuer and is called the &lt;strong&gt;Bank Identification Number (BIN)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An approved authorization places a temporary hold on the account for the amount of the transaction. Later, the merchant submits the final transaction amount (for example, adding tips written on receipts or voiding the transaction) to initiate the transfer of money, known as &lt;strong&gt;clearing&lt;/strong&gt;&lt;sup id=&quot;fnref:capture&quot;&gt;&lt;a href=&quot;#fn:capture&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Consider: before this was done by computers, this was done by people via phone calls&lt;sup id=&quot;fnref:verbal-authorization&quot;&gt;&lt;a href=&quot;#fn:verbal-authorization&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; and mail.&lt;/p&gt;

&lt;h1 id=&quot;2-coordinate-the-banking-network&quot;&gt;2. Coordinate the banking network&lt;/h1&gt;

&lt;p&gt;In addition to a telecom network, Visa has a &lt;em&gt;financial&lt;/em&gt; network of banks.&lt;/p&gt;

&lt;p&gt;After a transaction is finalized, money on both ends must move to fulfill the transaction, known as &lt;strong&gt;settlement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Visa’s second job is to &lt;em&gt;route money&lt;/em&gt; for settlement by having financial relationships with each party. It can collect money from one and transfer to another.&lt;/p&gt;

&lt;p&gt;To be efficient, Visa does &lt;strong&gt;net settlement&lt;/strong&gt;: every day, each network participant’s debits and credits are totalled, and at the end of the day the net money is moved to or from each participant once.&lt;/p&gt;

&lt;p&gt;For domestic transactions, moving money is &lt;a href=&quot;https://gendal.me/2013/11/24/a-simple-explanation-of-how-money-moves-around-the-banking-system/&quot;&gt;relatively straightforward, thanks to central banks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Importantly, Visa is also able to settle internationally, even &lt;a href=&quot;https://usa.visa.com/travel-with-visa/dynamic-currency-conversion.html&quot;&gt;handling currency conversion&lt;/a&gt;. Visa acts as an &lt;a href=&quot;https://www.bis.org/cpmi/publ/d213.pdf&quot;&gt;adapter between banking systems&lt;/a&gt; with its global banking relationships. This greatly simplifies international money movement for participants in its network — without Visa, each participant would need to manage their own international banking relationships.&lt;/p&gt;

&lt;figure&gt;
&lt;pre class=&quot;mermaid wide&quot;&gt;
---
config:
  theme: &apos;neutral&apos;
  fontFamily: system-ui, -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, Oxygen, Ubuntu, Cantarell, &apos;Open Sans&apos;, &apos;Helvetica Neue&apos;, sans-serif;
  nodeSpacing: 20
  rankSpacing: 60
  flowchart: 
    subGraphTitleMargin: {bottom: 20}
    diagramPadding: 10
    useMaxWidth: true
---
flowchart LR
    CI[Issuer in Country A] --&amp;gt; VisaA[&quot;Visa&apos;s bank in A&quot;]
    VisaB[Visa&apos;s bank in B] --&amp;gt; MA[Acquirer in Country B] 
    subgraph **Visa**
        VisaA -.-VisaB
    end
&lt;/pre&gt;
&lt;figcaption&gt;Visa as an adapter&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Shuffling this amount of money around and timing everything right is no easy feat. Visa faces non-payment risk in addition to maintaining a significant balance to cover payouts while waiting to receive settlement payments. From &lt;a href=&quot;https://investor.visa.com/SEC-Filings/default.aspx#annual-filings&quot;&gt;Visa’s 2024 annual SEC report&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Most U.S. dollar settlements are settled within the same day and do not result in a receivable or payable balance, while settlements in currencies other than the U.S. dollar generally remain outstanding for one to two business days, which is consistent with industry practice for such transactions. … As of September 30, 2024, we held $11.2 billion of our total available liquidity to fund daily settlement in the event one or more of our financial institution clients are unable to settle, with the remaining liquidity available to support our working capital and other liquidity needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Company’s settlement exposure is limited to the amount of unsettled Visa payment transactions at any point in time, which vary significantly day to day. For fiscal 2024, the Company’s maximum daily settlement exposure was $137.4 billion and the average daily settlement exposure was $84.3 billion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;3-set-incentives&quot;&gt;3. Set incentives&lt;/h1&gt;

&lt;p&gt;The key to this whole arrangement are the fees required to participate in the network, largely set by the network.&lt;/p&gt;

&lt;p&gt;Let’s walk through an average credit card transaction in the US:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A cardholder pays for a product at a merchant for $100.00.&lt;/li&gt;
  &lt;li&gt;The merchant pays 2.5% ($2.50) of the transaction to their payment processor or merchant acquirer. The 2.5% is the &lt;strong&gt;merchant discount rate&lt;/strong&gt; or MDR.&lt;/li&gt;
  &lt;li&gt;The payment processor keeps 0.35% ($0.35), then pays 2% ($2.00) to the cardholder’s issuing bank and 0.15% ($0.15) to Visa. The 2% is the &lt;em&gt;interchange fee&lt;/em&gt;, commonly known as &lt;strong&gt;interchange&lt;/strong&gt;. The 0.15% is the &lt;strong&gt;network assessment fee&lt;/strong&gt;. &lt;sup id=&quot;fnref:interchange-name&quot;&gt;&lt;a href=&quot;#fn:interchange-name&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;The issuing bank keeps 2% ($2.00)!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Surprisingly, the issuing bank keeps most and the network takes the least, by an order of magnitude! This is because for the tranasction, the issuer is traditionally considered to take on most of the risk (although merchants are likely to disagree).&lt;/p&gt;

&lt;p&gt;In addition to &lt;a href=&quot;https://www.consumerfinance.gov/rules-policy/regulations/1005/6/&quot;&gt;regulatory requirements&lt;/a&gt;, Visa and Mastercard offer zero-liability protection. This means that the issuing bank, not the cardholder, is liable for any charges made on a card if it is lost or stolen.&lt;sup id=&quot;fnref:friendly-fraud&quot;&gt;&lt;a href=&quot;#fn:friendly-fraud&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; The issuing bank also takes on &lt;a href=&quot;https://en.wikipedia.org/wiki/Credit_risk&quot;&gt;credit risk&lt;/a&gt;, and must always pay for an approved transaction even if a cardholder cannot pay off their balance.&lt;/p&gt;

&lt;p&gt;Out of these, the network sets the interchange and network assessment fee. Interchange fees vary dramatically based on the kind of card, category of spend, and even the metadata attached to a transaction. The network’s goal is to set fees that incentivize desired behaviors on their network, including using more secure payment methods (lowering interchange fees for merchants), or for companies to do more business spending (higher interchange fees on commercial credit cards).&lt;/p&gt;

&lt;p&gt;Because of how much is given to the issuers, there are a lot of incentives for issuers to acquire customers and fund lavish rewards programs to encourage spending. This split also explains the recent rise of &lt;strong&gt;issuing processors&lt;/strong&gt;, which make it easier for neobanks and fintechs to issue cards to access a more lucrative end of the market.&lt;/p&gt;

&lt;p&gt;Why are merchants willing to pay this fee?&lt;sup id=&quot;fnref:surcharge&quot;&gt;&lt;a href=&quot;#fn:surcharge&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;10&lt;/a&gt;&lt;/sup&gt; The idea is that accepting card payments nets more customers and higher spending, due to convenience, consumer protections, and credit card rewards. More cards means more merchants, more merchants mean more cards, and more of everything is good for the network. In theory, the network benefits through fees, the merchants benefit through more purchases, and the consumer benefits through convenience.&lt;/p&gt;

&lt;figure class=&quot;image wide&quot;&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;/assets/2026/card-spend-dark.svg&quot; media=&quot;(prefers-color-scheme: dark)&quot; /&gt;
  &lt;img src=&quot;/assets/2026/card-spend.svg&quot; alt=&quot;The virtuous cycle of card spend: Issuers get more interchange -&amp;gt; Issuers issue more cards and offer more rewards -&amp;gt; People spend more with card -&amp;gt; More merchants accept cards -&amp;gt; Repeat&quot; /&gt;
&lt;/picture&gt;
  &lt;figcaption&gt;The virtuous cycle of card spend.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In the EU, &lt;a href=&quot;https://www.psr.org.uk/our-work/card-payments/the-ifr/&quot;&gt;interchange fees are restricted to 0.3%&lt;/a&gt;, which explains the lack of rewards cards and wider acceptance of alternative payment methods like bank payments.&lt;/p&gt;

&lt;h1 id=&quot;4-set-rules-and-handle-disputes&quot;&gt;4. Set rules and handle disputes&lt;/h1&gt;

&lt;p&gt;Aside from incentivizing good behavior, card networks also need to regulate bad behavior on their networks. These rules are detailed in “Visa Core Rules and Visa Product and Service Rules”, a 923 page volume that is &lt;a href=&quot;https://usa.visa.com/dam/VCOM/download/about-visa/visa-rules-public.pdf&quot;&gt;publically available&lt;/a&gt;. This is a tome detailing things like appropriate use of the Visa brand mark, detailed interchange data requirements, transaction processing timelines requirements, requirements and procedures for specific Visa services, and features issuers/acquirers should support.&lt;/p&gt;

&lt;p&gt;For issuers and acquirers, who interact with the network, violating these rules could affect interchange rates, incur non-compliance fines (anywhere from $25k to $1M per month), or risk suspension from the network.&lt;/p&gt;

&lt;p&gt;For cardholders and merchants, who interact with each other, networks provide a mechanism for resolving disputes between them. This could mean a fraudulent transaction, the product was not as promised, or a number of other possible reasons detailed in the rules. This is the process that happens behind the scenes when you call your bank to report fraud or request a chargeback.&lt;/p&gt;

&lt;p&gt;When a payment method doesn’t have a dedicated dispute mechanism, the legal system is used to settle disputes&lt;sup id=&quot;fnref:legal-system-chargeback&quot;&gt;&lt;a href=&quot;#fn:legal-system-chargeback&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;. This a problem with cash or bank payments that cards don’t have.&lt;/p&gt;

&lt;h2 id=&quot;arbitration&quot;&gt;Arbitration&lt;/h2&gt;

&lt;p&gt;Visa is not actually involved directly in resolving most disputes. The rules essentially impose forced arbitration&lt;sup id=&quot;fnref:forced-arbitration&quot;&gt;&lt;a href=&quot;#fn:forced-arbitration&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;. The issuer of the cardholder and acquirer of the merchant send evidence back and forth until one party yields and accepts liability for the transaction.&lt;/p&gt;

&lt;p&gt;If both parties refuse to yield, then Visa reviews the dispute, charging a whopping $600 ($1000 for appeals) makes a decision based on the evidence provided (signature, security footage, receipts, etc) and the guidelines listed in the Visa Product and Service Rules.&lt;/p&gt;

&lt;p&gt;The losing party pays the original transaction amount plus the review fee, so both parties have a lot of incentive to resolve it between themselves. Issuers often refund the cardholder themselves and write off the loss. Merchants proactively refund dissatisfied customers since they are charged a $15-30 processing fee by the acquirer upon receiving any dispute, even if they win.&lt;/p&gt;

&lt;p&gt;Arbitration is not a fair system, but it is an efficient one.&lt;sup id=&quot;fnref:fraud&quot;&gt;&lt;a href=&quot;#fn:fraud&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I hope you have gained some appreciation for the important role of card networks and some reasons for why cards are as popular as they are today. Use this knowledge to topple the V/MC duopoly, design your own payment method, or think about while you spend your money.&lt;/p&gt;

&lt;p&gt;Edit: this reached the &lt;a href=&quot;https://news.ycombinator.com/item?id=49614280&quot;&gt;frontpage of Hacker News&lt;/a&gt; — hello, and welcome to town!&lt;/p&gt;

&lt;hr /&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:dont-do&quot;&gt;
      &lt;p&gt;Nowadays they may have product offerings for some of these, or own subsidiaries that do some of these, but these are not core to the business of being a card network. &lt;a href=&quot;#fnref:dont-do&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:payfac&quot;&gt;
      &lt;p&gt;Technically, these are “payment facilitators”. A bank underwites the payment company, and the payment company underwrites their merchants. The different roles in payments have historically been meaningful, but companies are increasingly blurring the lines, so these distinctions and terms are less interesting today. &lt;a href=&quot;#fnref:payfac&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:card-network-name&quot;&gt;
      &lt;p&gt;To be precise, we use “card networks” to colloqially refer to the companies operating their own card payment networks / schemes. For example, &lt;em&gt;VisaNet&lt;/em&gt; is technically the network, Visa is the company/brand. Mastercard’s network is called &lt;em&gt;Banknet&lt;/em&gt;. Both companies also own and operate specialized subsidiary networks for things like debit cards and ATMs, like Visa’s Interlink and Plus, or Mastercard’s Cirrus and Maestro, although technically VisaNet and Banknet can process debit (this is a story for another time). There are further terms to distinguish the telecommunications network with the bank network, and even subsets of each. Visa even thinks of itself as a &lt;a href=&quot;https://annualreport.visa.com/business-overview/default.aspx#:~:text=Our%20network%20of%20networks%20strategy,transactions%2C%20no%20matter%20the%20network.&quot;&gt;“network of networks”&lt;/a&gt;. Turtles all the way down. &lt;a href=&quot;#fnref:card-network-name&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:or-four&quot;&gt;
      &lt;p&gt;Or &lt;a href=&quot;https://www.marqeta.com/uk/demystifying-cards-guide/card-payments-ecosystem&quot;&gt;four or more&lt;/a&gt;, depending on how you count. &lt;a href=&quot;#fnref:or-four&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:pan-exhaustion&quot;&gt;
      &lt;p&gt;Similar to IPv4, 16 digit PANs are rapidly exhausting due to the use of anonymized “token” PANs used by things like Apple/Google Pay and saved payment details. &lt;a href=&quot;#fnref:pan-exhaustion&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:capture&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Clearing_(finance)&quot;&gt;Clearing&lt;/a&gt; includes finalizing/committing the payment and reconciliation against the authorization. “Capturing a payment” is what this is called from the merchant’s perspective. &lt;a href=&quot;#fnref:capture&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:verbal-authorization&quot;&gt;
      &lt;p&gt;Known as a “&lt;a href=&quot;https://web.mit.edu/ecommerce/www/verbal-auth.html&quot;&gt;voice authorization&lt;/a&gt;”. You might be able to get one today, although I don’t know if banks are staffing operators to field calls. &lt;a href=&quot;#fnref:verbal-authorization&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:interchange-name&quot;&gt;
      &lt;p&gt;Although &lt;em&gt;interchange&lt;/em&gt; is short for &lt;em&gt;interchange fee&lt;/em&gt;, technically &lt;em&gt;interchange&lt;/em&gt; refers to the payment messages being routed by the card networks, and the fee is provided for that data. But, you almost never hear &lt;em&gt;interchange&lt;/em&gt; used to mean payment messages except in technical specs. &lt;a href=&quot;#fnref:interchange-name&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:friendly-fraud&quot;&gt;
      &lt;p&gt;Although this creates trust in card payments, it has opened the doors to &lt;em&gt;friendly fraud&lt;/em&gt;, where legitimate purchases are reported as fraudulent. Not to mention a &lt;a href=&quot;https://en.wikipedia.org/wiki/Moral_hazard&quot;&gt;moral hazard&lt;/a&gt;. &lt;a href=&quot;#fnref:friendly-fraud&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:surcharge&quot;&gt;
      &lt;p&gt;Some merchants add a surcharge on card transactions to pass through the fee to customers. This used to be against Visa’s rules (and &lt;a href=&quot;https://oag.ca.gov/consumers/general/credit-card-surcharges&quot;&gt;California state law&lt;/a&gt;) but lawsuits have challenged that. The surcharge amount is also &lt;a href=&quot;https://usa.visa.com/dam/VCOM/download/merchants/surcharging-faq-by-merchants.pdf&quot;&gt;only supposed&lt;/a&gt; to be the amount the merchant pays on a card transaction, but I suspect that many small merchants charge more. &lt;a href=&quot;#fnref:surcharge&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:legal-system-chargeback&quot;&gt;
      &lt;p&gt;There are some &lt;a href=&quot;https://arstechnica.com/tech-policy/2021/02/citibank-just-got-a-500-million-lesson-in-the-importance-of-ui-design/&quot;&gt;amusing cases&lt;/a&gt; of this at work in large transactions, where the legal fees are much more in proportion with the transaction costs. &lt;a href=&quot;#fnref:legal-system-chargeback&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:forced-arbitration&quot;&gt;
      &lt;p&gt;Not that you asked, but the expansion of forced arbitration and confidential settlements are bad for society. Back in the day, you could sue your bank (for example) for doing bad stuff, but now contracts for everything include a clause for arbitration, waiving your rights as a consumer. The company pays for the arbitration, not you, so you can guess the outcome of that. Famously, Disney tried to use a forced arbitration clause in the Disney+ streaming agreement to prevent a wrongful death suit from a allergic reaction in a Disney restaurant. In the Visa case, the dispute process means the consumer tends to benefit, at the cost of the issuer and the merchant. &lt;a href=&quot;#fnref:forced-arbitration&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:fraud&quot;&gt;
      &lt;p&gt;The system is designed to &lt;a href=&quot;https://www.bitsaboutmoney.com/archive/optimal-amount-of-fraud/&quot;&gt;encourage transactions rather than stopping fraud&lt;/a&gt;. It opens the door to fraud, especially friendly fraud, as explained in another footnote. Friendly fraud is &lt;a href=&quot;https://www.cnbc.com/2024/07/25/retailers-are-losing-100-billion-a-year-from-friendly-fraud-report-finds-.html&quot;&gt;a growing problem&lt;/a&gt;. &lt;a href=&quot;#fnref:fraud&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/06/01/card-networks.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/06/01/card-networks.html</guid>
        
        
      </item>
    
      <item>
        <title>Money: a constructivist approach</title>
        <description>&lt;p&gt;When I was younger, I thought that money was cash.&lt;/p&gt;

&lt;p&gt;In this view, if you went to a bank and deposited a dollar, at the end of the day, the dollar would be &lt;a href=&quot;/assets/2026/scrooge.gif&quot;&gt;placed neatly&lt;/a&gt; next to a stack of gold bricks, in a vault deep beneath the bank. If you sent a wire, the dollar (or another like it) would be aboard an armored car the next day to the receiving bank. An armored plane if you paid for same day.&lt;/p&gt;

&lt;p&gt;Of course, this mostly wrong. But, I suspect, that &lt;em&gt;why&lt;/em&gt; this is wrong is not as obvious to many people. I might even wager that banks, payments, and money generally, are misunderstood by most people.&lt;/p&gt;

&lt;p&gt;Like:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;What is money, if money is not cash?&lt;/li&gt;
  &lt;li&gt;Or: How does a dollar get around, if not by armored car?&lt;/li&gt;
  &lt;li&gt;And: What do banks do, if not hoard piles of gold in a vault?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To begin to answer these questions, and more, I invite you, dear reader, to close your eyes, and imagine a time before money, and allow us to invent money from scratch…&lt;/p&gt;

&lt;style&gt;
.dramatic p {
  margin-bottom: 1.6rem;
}
.pause {
  height: 1.6rem;
}
&lt;/style&gt;

&lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;div class=&quot;dramatic&quot;&gt;
  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;You are &lt;a href=&quot;https://monoskop.org/images/9/96/Le_Guin_Ursula_K_1986_1989_The_Carrier_Bag_Theory_of_Fiction.pdf&quot;&gt;Boog&lt;/a&gt;.&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;You have recently invented Agriculture.&lt;/p&gt;

  &lt;p&gt;On paper (not yet invented), settling down and growing food was a great idea. But, you’re much busier than you expected, and your diet of oats &lt;a href=&quot;https://en.wikipedia.org/wiki/Neolithic_Revolution#Diet_and_health&quot;&gt;has not been feeling great&lt;/a&gt;.&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;So, you ask Grob, your hunter-gather friend, for a slice of wooly mammoth.&lt;/p&gt;

  &lt;p&gt;“For Rock-god’s sake!” cries Grob. “This is the sixth time this rock-week. What’s in it for me?”&lt;/p&gt;

  &lt;p&gt;And so, you agree to exchange three bowls of oatmeal for a delicious mammoth-steak.&lt;/p&gt;

  &lt;p&gt;Thus, Commerce is born (and Altruism begins its slow decline).&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;Things are going well. Your oat fields grow large and bountiful. Others settle down near you for oats. You exchange oats for Goods and Services. Eventually, enough people settle down, creating Civilization.&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;Problems quickly emerge.&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;You are sick and tired of oats. You happily trade vast sums of oats for modern indulgences like Clothing, Bronze, and Stone Tablets.&lt;/p&gt;

  &lt;p&gt;But oats have become so commonplace that the oat-rate for these luxuries has reached the hundreds, even thousands, of bushels (this is called Inflation). This is too much for you to carry around, and far too many oats for anyone to eat at once.&lt;/p&gt;

  &lt;p&gt;Of course, you are Clever, so you devise a Genius Scheme: instead of sending buschels of oats directly, you mark down on a clay tablet how many oats you owe to who in a ledger (in doing so, you invent Numbers and Writing). Oat creditors can redeem their oats as needed in smaller, more consumable quantities. The oats remain safe and dry in a warehouse.&lt;/p&gt;

  &lt;p&gt;You figure out that you can even gamble and plant the still unclaimed oats to have even more for next year.&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;The system is pretty good.&lt;/p&gt;

  &lt;p&gt;So good, that instead of redeeming the oats, people trade by asking you to move oats from their oat-account to another’s. It works so well that soon everything is priced in oats, even as fewer and fewer people are redeeming numerical-oats for real-oats.&lt;/p&gt;

  &lt;p&gt;This is called: the Money of Account.&lt;/p&gt;

  &lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

  &lt;p&gt;To make it you don’t need to be involved in every transaction, you design an oat-redemption-token. Made of pocketable circles of Bronze equal the to value of redeemable oats, you emblazon it with your oat-seal.&lt;/p&gt;

  &lt;p&gt;“This is a Circular Oat Ingot Note,” you say, holding up a bronze loop, showing it around. “COIN for short. You can use this as a Unit of Exchange. Now please stop bothering me.”&lt;/p&gt;

  &lt;p&gt;And this is called: Cash.&lt;/p&gt;

&lt;/div&gt;

&lt;div class=&quot;pause&quot;&gt;&lt;/div&gt;

&lt;p&gt;Okay, we’ve been having too much fun. Let’s break down what we’ve discovered:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Money can be a number, like “money of account”.&lt;/li&gt;
  &lt;li&gt;Money can be a thing, like cash (or oats).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And, in fact, a number is much more portable than a thing.&lt;/p&gt;
</description>
        <pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/05/23/constructing-money.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/05/23/constructing-money.html</guid>
        
        
      </item>
    
      <item>
        <title>How do React hooks associate functions with state?</title>
        <description>&lt;p&gt;How do React hooks know which component instance is being called, without being passed a stable identifier?&lt;/p&gt;

&lt;p&gt;There are many high quality answers online. For me, they either focus too much on React internals (&lt;a href=&quot;https://stackoverflow.com/a/53730788/7971276&quot;&gt;Fibers&lt;/a&gt;, &lt;a href=&quot;https://eliav2.github.io/how-react-hooks-work/&quot;&gt;render cycle&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com/a/53980190/7971276&quot;&gt;ReactFiberHooks&lt;/a&gt;), or are too high level (&lt;a href=&quot;https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e&quot;&gt;“it’s just arrays”&lt;/a&gt;, closures, call order).&lt;/p&gt;

&lt;p&gt;Here is an answer, written for and by me:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;React stores state for a component instance by using its relative position in the “virtual DOM” tree. &lt;a href=&quot;https://react.dev/reference/rules/rules-of-hooks&quot;&gt;The Rules of Hooks&lt;/a&gt; requires hooks for a component to have a fixed order, so the call order can then be used to index an individual hook’s state.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is alluded to in the &lt;a href=&quot;https://react.dev/learn/preserving-and-resetting-state&quot;&gt;React docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;React keeps track of which state belongs to which component based on their place in the UI tree.&lt;br /&gt;
…&lt;br /&gt;
When you give a component state, you might think the state “lives” inside the component. But the state is actually held inside React. React associates each piece of state it’s holding with the correct component by where that component sits in the render tree.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine keeping a second state tree that mirrors the shape of the UI tree. When we evaluate the UI tree, we walk through the UI tree and state tree at the same positions. Before a functional component is evaluated, its state is loaded from the corresponding state tree node to be used implicitly by its hooks.&lt;/p&gt;

&lt;p&gt;Here is how you might implement a bare bones &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useState&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;hookState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hookIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// global hook &quot;context&quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// (unrelated to React Context API)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 0. on first render, stateNode tree copies vnode (virtual DOM) tree structure&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// visit() will be called per component instance/node when rendering &lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;vnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stateNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 1. before a functional component is called, load in its context&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stateNode&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// reset for the current component&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;renderStuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;vnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 2. evaluates the functional component, calling its hooks&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;vnode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stateNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// called by functional component&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 3. keep track of which hook is being called within the component&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookIndex&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// copy to closure&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// 4. if there is no prior state, this is the first render. initialize!&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initial&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// current value&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// and setter&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hookState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;queueRender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conditionally-rendered-elements&quot;&gt;Conditionally rendered elements&lt;/h2&gt;

&lt;p&gt;How does this work for conditionally rendered elements? If an element is not rendered, then wouldn’t it lose its place in the rendering tree, messing up the rest of the tree?&lt;/p&gt;

&lt;p&gt;My own confusion around this stemmed from the convenient representation of JSX.&lt;/p&gt;

&lt;p&gt;JSX tags are transformed into function calls like this (&lt;a href=&quot;https://babeljs.io/repl&quot;&gt;try for yourself&lt;/a&gt;):&lt;/p&gt;

&lt;style&gt;
.side-by-side {
  display: flex;
  gap: 1rem;
  align-items: stretch;
}
.side-by-side div {
  flex: 1;
  min-width: auto;
}
@media (max-width: 600px) {
  .side-by-side { flex-direction: column; }
}
&lt;/style&gt;

&lt;div class=&quot;side-by-side&quot;&gt;
  &lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Parent&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;example&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;showA&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;_jsxs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;showA&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}),&lt;/span&gt; 
    &lt;span class=&quot;nf&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_jsx&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_jsxs&lt;/code&gt; are implemented by React or your framework of choice.&lt;/p&gt;

&lt;p&gt;Notice that each of the children occupy their own index of an array passed into the parent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_jsxs&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;More importantly, the entire conditional expression &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{showA &amp;amp;&amp;amp; &amp;lt;A/&amp;gt;}&lt;/code&gt; takes its own place in the array. So the index of all the children in the “UI tree” stay the same, even when one child is not rendered. The element that isn’t rendered does lose its state, though.&lt;/p&gt;

&lt;p&gt;For components that return different JSX &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return (showA ? &amp;lt;A/&amp;gt; : &amp;lt;B/&amp;gt;)&lt;/code&gt;, React should reset the state subtree if the subtree’s node structure changes.&lt;/p&gt;

&lt;h2 id=&quot;keys-and-lists-of-elements&quot;&gt;Keys and lists of elements&lt;/h2&gt;

&lt;p&gt;This also explains why lists of elements need a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt;. The entire array producing expression occupies an array index, so it becomes ambiguous which component is which since they share an index.&lt;/p&gt;

&lt;div class=&quot;side-by-side&quot;&gt;
  &lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Parent&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;example&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;_jsxs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})),&lt;/span&gt; 
    &lt;span class=&quot;nf&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;in-summary&quot;&gt;In summary&lt;sup id=&quot;fnref:i-am-not-ai&quot;&gt;&lt;a href=&quot;#fn:i-am-not-ai&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Each JSX tag is transformed into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_jsx&lt;/code&gt; function call, passing in the tag name as a string, properties as an object. A tag’s child tags are passed as an array of expressions.&lt;/li&gt;
  &lt;li&gt;React executes this function call and constructs a “virtual DOM” tree describing the intended DOM structure. React knows how to efficiently update the real DOM from the virtual DOM.&lt;/li&gt;
  &lt;li&gt;React stores local state for each component in a tree mirroring the virtual DOM. When state is updated, it uses the position within the tree to load the right state for hooks.&lt;/li&gt;
  &lt;li&gt;Hooks within the same component are distinguished by call order in that component. This is possible because of the &lt;a href=&quot;https://react.dev/reference/rules/rules-of-hooks&quot;&gt;Rules of Hooks&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;This works with a conditionally rendered component (i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{showThing &amp;amp;&amp;amp; &amp;lt;Thing/&amp;gt;}&lt;/code&gt;) because the entire conditional expression is included in the children array of its parent, so the index of the component is stable.&lt;/li&gt;
  &lt;li&gt;Lists of components need keys because the entire list expression occupies a single index in the children array of its parent, so they need their own stable identifier.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pomb.us/build-your-own-react/&quot;&gt;Build your own React (Rodrigo Pombo)&lt;/a&gt;: this was helpful in understanding how JSX transforms into Javascript, which is the key that a lot of other resources missed or assumed. Very detailed!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.swyx.io/hooks&quot;&gt;Getting Closure on React Hooks (swyx)&lt;/a&gt;: this explains closure for hooks and derives the rules of hooks.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889#:~:text=We%20keep%20a,calling%20your%20component.&quot;&gt;Making Sense of React Hooks (Dan Abramov)&lt;/a&gt;: introduces some benefits of hooks and talks about the implementation at a high level
    &lt;ul&gt;
      &lt;li&gt;Also, the linked &lt;a href=&quot;https://gist.github.com/gaearon/62866046e396f4de9b4827eae861ff19&quot;&gt;pseudocode from @jamiebuilds&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://legacy.reactjs.org/docs/introducing-jsx.html&quot;&gt;Introducing JSX (legacy React docs)&lt;/a&gt;: outdated (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_jsx&lt;/code&gt; is now called instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;React.createElement&lt;/code&gt;) but useful context&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/71731023/7971276&quot;&gt;StackOverflow answer from Knight Industries&lt;/a&gt;: provided some intuition, not as useful on its own&lt;/li&gt;
  &lt;li&gt;Asking questions to &lt;a href=&quot;https://claude.ai/&quot;&gt;Claude&lt;/a&gt; was helpful in honing my understanding, especially for the conditional rendering question with JSX, which I verified with &lt;a href=&quot;https://babeljs.io/repl&quot;&gt;Babel&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://legacy.reactjs.org/docs/hooks-faq.html#how-does-react-associate-hook-calls-with-components&quot;&gt;Hooks FAQ: How does React associate hook calls with components (legacy React docs)&lt;/a&gt;: this is the former “official” answer, but it’s very high level.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://react.dev/learn/preserving-and-resetting-state&quot;&gt;Preserving and Resetting State (React docs)&lt;/a&gt;: mostly helpful, but a bit handwavey around “UI tree”. Its conditional rendering example hides the second, not first, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Counter()&lt;/code&gt;, which hides that the conditional expression is in the UI tree.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://legacy.reactjs.org/docs/reconciliation.html&quot;&gt;Reconciliation (legacy React docs)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://legacy.reactjs.org/docs/faq-internals.html&quot;&gt;Virtual DOM and Internals (legacy React docs)&lt;/a&gt;: clarifies the virtual DOM terminology&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:i-am-not-ai&quot;&gt;
      &lt;p&gt;I like lists and bullets and I am not AI. Tautology.town is written by hand. I like em-dashes too, but that battle has been lost. &lt;a href=&quot;#fnref:i-am-not-ai&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/05/21/react-hooks-explained.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/05/21/react-hooks-explained.html</guid>
        
        
      </item>
    
      <item>
        <title>Doing things ten times</title>
        <description>&lt;style&gt;
.footnotes li {
  font-size: 0.85em;
}

.footnotes ol {
  margin-top: 3rem;
  margin-left: 16px;
}
&lt;/style&gt;

&lt;p&gt;I observed lately that there aren’t many things that I’ve done ten or more times. Ten is low enough to be reasonable, but is high enough that it takes some intention to get to.&lt;/p&gt;

&lt;p&gt;For example: I estimate that over my life I’ve baked 6 pies: 2 blueberry, 2 apple, and 2 pumpkin (arguably a tart). This was surprising to me, since I consider myself a casual yet decent baker of pies. Decent enough for people to ask for my recipes!&lt;/p&gt;

&lt;p&gt;I also consider myself a decent &lt;em&gt;eater&lt;/em&gt; of pies. Even there, I estimate a lifetime total of having tried ten individual pies (including pumpkin, excluding cheesecake).&lt;sup id=&quot;fnref:pies-tried&quot;&gt;&lt;a href=&quot;#fn:pies-tried&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Doing a thing once a year will take you a decade to do ten times. Even once a week will take a few months to hit ten times. Despite my enthusiasm, my pie production (and consumption) is largely limited to Thanksgiving. Meanwhile, professional bakers churn through ten pies in under an hour.&lt;/p&gt;

&lt;h2 id=&quot;ten-is-different&quot;&gt;Ten is different&lt;/h2&gt;

&lt;p&gt;If you want to take something seriously, first commit to doing it ten times.&lt;/p&gt;

&lt;p&gt;After ten times, you will have done way more than “tourists” who have tried it once. You will have done more than once a year “casuals”, even if they’ve done it for years.&lt;/p&gt;

&lt;p&gt;Another data point: the viewership of the math lectures I’m working through.&lt;/p&gt;

&lt;style&gt;
#viewership, #completion {
  width: 200%;
  margin-top: 2rem; 
  margin-bottom: 2rem; 
}

#completion {
  margin-left: -41px;
}

#viewership {
  margin-left: -53px;
  margin-right: -4px;
}
@media (max-width: 800px) {
  #viewership, #completion {
    margin-left: 0;
    margin-top: 2rem; 
    margin-bottom: 2rem; 
  }
}
&lt;/style&gt;

&lt;canvas id=&quot;viewership&quot; width=&quot;600&quot; height=&quot;400&quot;&gt;
&lt;/canvas&gt;

&lt;p&gt;Assuming (!) that most viewers are watching sequentially, only an estimated 28% of the first lecture viewers will watch the second lecture. By the 10th lecture, most of the viewers that will drop off have gone, with 4% left. Only 3% watch last lecture!&lt;/p&gt;

&lt;p&gt;I’m on lecture 6, a group where 20-45% finish the course.&lt;/p&gt;

&lt;canvas id=&quot;completion&quot; width=&quot;600&quot; height=&quot;400&quot;&gt;
&lt;/canvas&gt;

&lt;script src=&quot;/assets/2026/chart.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;
const viewers = [2700, 769, 404, 371, 207, 392, 201, 162, 127, 111, 103, 104, 93, 105, 93, 82, 70, 102, 74, 64, 66, 64, 63, 71, 88];
const lastViewer = viewers[viewers.length - 1];
const completion = viewers.map(v =&gt; Math.min(100, Math.round(lastViewer / v * 100)));

function themeColors() {
  const style = getComputedStyle(document.body);
  return {
    color: style.color,
    fontFamily: style.fontFamily,
    fontSize: parseFloat(style.fontSize)
  };
}

function applyTheme(charts) {
  const { color, fontFamily, fontSize } = themeColors();
  Chart.defaults.font.family = fontFamily;
  Chart.defaults.font.size = fontSize;
  Chart.defaults.color = color;
  for (const chart of charts) {
    chart.data.datasets.forEach(ds =&gt; { ds.borderColor = color; });
    [&apos;x&apos;, &apos;y&apos;].forEach(axis =&gt; {
      const scale = chart.options.scales[axis];
      scale.grid.tickColor = color;
      scale.border.color = color;
      scale.ticks.color = color;
    });
    chart.update();
  }
}

const { color, fontFamily, fontSize } = themeColors();
Chart.defaults.font.family = fontFamily;
Chart.defaults.font.size = fontSize;
Chart.defaults.color = color;

const lineChart = (canvasId, data, title, titlePad) =&gt; new Chart(document.getElementById(canvasId), {
  type: &apos;line&apos;,
  data: {
    labels: Array.from({length: 25}, (_, i) =&gt; i + 1),
    datasets: [{
      data,
      borderColor: color,
      backgroundColor: &apos;transparent&apos;,
      pointRadius: 3,
      tension: 0.1
    }]
  },
  options: {
    aspectRatio: 1.5,
    plugins: {
      legend: { display: false },
      title: { display: true, text: title, padding: { bottom: titlePad }}
    },
    scales: {
      x: {
        grid: { color: &apos;transparent&apos;, drawTicks: true, tickLength: 5, tickColor: color },
        border: { color: color },
        ticks: { color: color }
      },
      y: {
        grid: { color: &apos;transparent&apos;, drawTicks: true, tickLength: 5, tickColor: color },
        border: { color: color },
        ticks: { color: color }
      }
    }
  }
});

const charts = [
  lineChart(&apos;viewership&apos;, viewers, &apos;Views per lecture, in thousands&apos;, 24),
  lineChart(&apos;completion&apos;, completion, &apos;Estimated rate of completing the course&apos;, 36)
];

window.matchMedia(&apos;(prefers-color-scheme: dark)&apos;).addEventListener(&apos;change&apos;, () =&gt; applyTheme(charts));
&lt;/script&gt;

&lt;p&gt;By ten lectures, you have more in common with the completionists than the dropouts — 80% of those who make it to lecture ten continue on complete the course.&lt;/p&gt;

&lt;h2 id=&quot;a-public-commitment&quot;&gt;A public commitment&lt;/h2&gt;

&lt;p&gt;Staying committed is a differentiator, especially when fall off is steep, and it often is. &lt;sup id=&quot;fnref:log-normal&quot;&gt;&lt;a href=&quot;#fn:log-normal&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;This post (10th on this blog) is my public commitment to start doing things ten times. Starting with four more pies.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:pies-tried&quot;&gt;
      &lt;p&gt;This is individual pies tried, not &lt;em&gt;kinds&lt;/em&gt; of pies tried. Why distinct-pies-tried? I think it gets best at the heart of the matter of what we care about. Better than slices, since it is easy to eat multiple slices in one sitting. Better than volumes of entire pie consumer, since pies are meant to be shared. I considered days-where-pie-was-eaten, but most pies at home are picked at over a long period. Cumulatively, I think I have eaten much more than ten slices of pie, on more than ten days, but much less than ten pies-volumes of pie. &lt;a href=&quot;#fnref:pies-tried&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:log-normal&quot;&gt;
      &lt;p&gt;Retention is often log-normal, the limit distribution of multiplying random variables. &lt;a href=&quot;#fnref:log-normal&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/04/03/ten-times.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/04/03/ten-times.html</guid>
        
        
      </item>
    
      <item>
        <title>Learning how to charcoal grill</title>
        <description>&lt;p&gt;I got a charcoal grill. A portable, no-frills, &lt;a href=&quot;https://www.weber.com/US/en/portable-grills/jumbo-joe/&quot;&gt;Weber Jumbo Joe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a cute, round, and pleasingly-shaped little thing. You can get one used very cheaply. Contrary to its name, “Jumbo” Joe is one of the smaller grills you can get. (Smaller yet is “Smokey” Joe. Who is Joe?)&lt;/p&gt;

&lt;p&gt;There was a Reddit comment I saw that just made me get up and go do it, which I had been hemming and hawing about before. I couldn’t find the comment again, but it went something like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;was going to end it all on x-mas. my daughter in law gifted me a weber jumbo joe. been grilling 15 years since, never looked back&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What a testimonial! I got my Jumbo Joe the very next day.&lt;/p&gt;

&lt;p&gt;I’ve grilled on it four times, a little less than once a week since setting it up. There are more detailed guides online, but the absolute basics are quite simple.&lt;/p&gt;

&lt;h2 id=&quot;the-absolute-basics&quot;&gt;The absolute basics&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;First;&lt;/em&gt; invite your friends over. Have some cold drinks!&lt;/p&gt;

&lt;p&gt;There are two ways to cook: direct heat, and indirect heat. The “two-zone” technique is putting the charcoal on one side of the grill for direct heat searing, leaving the other half for indirect heat slow cooking via convection with the lid on. Charcoal stacked together helps keep it hot.&lt;/p&gt;

&lt;p&gt;For temperature control, the grill has a bottom and a top vent that can be adjusted to control airflow. Open vents will pull more oxygen and the charcoal will faster and hotter.&lt;/p&gt;

&lt;p&gt;Generally, &lt;a href=&quot;https://www.weber.com/IE/en/blog/grill-on/its-all-about-the-lid/weber-169764.html&quot;&gt;try to keep the lid on&lt;/a&gt; to control temperatures and prevent flare ups. If there’s flare up, put the lid on and reduce the top vent. Remove some food; it may be dripping too much grease. Interestingly, the distinctive smokey flavor comes from fat drippings hitting the charcoal and turning to smoke.&lt;/p&gt;

&lt;p&gt;There will be hotter and cooler spots you’ll notice as you cook. Flip and move the food around with tongs and a spatula. I use the &lt;a href=&quot;https://www.oxo.com/grilling-tongs-and-turner-set.html&quot;&gt;OXO turner and tongs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can check the internal temperature with an instant read thermometer to see when meat is done cooking. It’s done at 160 degree Fahrenheit for beef and pork, and 165 for chicken. I use a &lt;a href=&quot;https://www.thermoworks.com/thermopop-2/&quot;&gt;ThermoPop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just in case, have a pair of heat resistant grilling gloves.&lt;/p&gt;

&lt;p&gt;To start the charcoals, use a chimney starter and start the charcoal with fatwood. Avoid match-light charcoal and lighter fluid which can impart a chemical taste. I use briquettes, which are supposed to be easier than lump charcoal for beginners.&lt;/p&gt;

&lt;p&gt;Once you’re done cooking, put the lid on and close both vents to snuff out the flame.&lt;/p&gt;

&lt;p&gt;For clean up, I throw the steel grate and grill tools into the dishwasher the next day and use steel wool to clean off any burnt food. I don’t use a barbeque wire brush because I saw a TV episode about the dangers of &lt;a href=&quot;https://www.cdc.gov/mmwr/preview/mmwrhtml/mm6126a4.htm&quot;&gt;ingesting a stiff metal bristle&lt;/a&gt;. This technique seems to work better, anyways.&lt;/p&gt;

&lt;h2 id=&quot;things-to-grill&quot;&gt;Things to grill&lt;/h2&gt;

&lt;p&gt;The most popular thing I’ve grilled is bratwurst. Of course, served with whole grain or spicy brown mustard in a bun. Even better are cheddar brats!&lt;/p&gt;

&lt;p&gt;The surprise favorite is corn, which has become my traditional way of ending a meal. When you grill corn alongside meat, it is beautifully sweet with a slightly savory smoke. Sweet potato, cut open and finished on the grill works similarly too.&lt;/p&gt;

&lt;h2 id=&quot;things-ive-yet-to-learn&quot;&gt;Things I’ve yet to learn&lt;/h2&gt;

&lt;p&gt;As I’m just getting started, I have a lot to learn: smoking meats for a long time (charcoal snake method, water pan), ribs, reverse searing steaks, using temperature probes, different cuts of meat, wood smoke, mackerel, vortex grilling, bread, rotisserie.&lt;/p&gt;

&lt;h2 id=&quot;you-should-grill-too&quot;&gt;You should grill too&lt;/h2&gt;

&lt;p&gt;Charcoal grilling is essentially a more sophisticated form of the most ancient form of cooking, cooking over a fire. It is &lt;a href=&quot;https://en.wikipedia.org/wiki/Control_of_fire_by_early_humans#The_cooking_hypothesis&quot;&gt;innately human&lt;/a&gt;; we are practically born to do it.&lt;/p&gt;

&lt;p&gt;I have long maintained that man has &lt;a href=&quot;https://en.wikipedia.org/wiki/Lindy_effect&quot;&gt;long known&lt;/a&gt; the ways to a good life, and that they are simple and should be easy to do. This includes good food, good company, and good times. Grilling can save lives.&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;hyphens: none;&quot;&gt;
It is intuitive, it is forgiving (cut off the burnt parts), it is delicious, it is fun, and it is even better with others. You should grill too.
&lt;/span&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/02/14/learning-charcoal-grilling.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/02/14/learning-charcoal-grilling.html</guid>
        
        
      </item>
    
      <item>
        <title>Notes on watering houseplants</title>
        <description>&lt;p&gt;Overwatering and underwatering have similar symptoms, but an overwatered plant is much harder to save. Overwatering causes &lt;a href=&quot;https://en.wikipedia.org/wiki/Root_rot&quot;&gt;root rot&lt;/a&gt; which dries out plants.&lt;/p&gt;

&lt;p&gt;A good cadence to water is often every two weeks, or even less.&lt;/p&gt;

&lt;p&gt;Water when poking a finger into the soil feels dry or the leaves are curling up.&lt;/p&gt;

&lt;p&gt;Instead of watering a little bit all the time, fully water your plant when it’s dry. This means thoroughly saturating the soil and letting the excess run off.&lt;/p&gt;

&lt;p&gt;An easy way to fully water is from the bottom up: place your planter in a container of water and let it sit for a while to draw water up the drainage holes. This avoids problems with &lt;a href=&quot;https://ucanr.edu/site/uc-master-gardeners-santa-clara-county/watering-hydrophobic-soil&quot;&gt;hydrophobic soil&lt;/a&gt;. I like to use a clear deli container so I can see the water level go down.&lt;/p&gt;

&lt;p&gt;Also, have drainage holes in your planters!&lt;/p&gt;

&lt;p&gt;It can help to write down when you’ve last watered, which may be more or less recently than you thought.&lt;/p&gt;

&lt;p&gt;If you are an overbearing plant parent with a habit of overwatering, get a spray bottle to regularly mist your plant with instead.&lt;/p&gt;

&lt;p&gt;Use filtered or distilled water instead of tap water.&lt;/p&gt;

&lt;h2 id=&quot;interesting-links&quot;&gt;Interesting links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/houseplants/comments/128it4s/stupid_question_why_is_it_when_plants_are_in/&quot;&gt;“Stupid Question: why is it when plants are in water they don’t get root rot from exposure to too much water but when they are in soil and are in water too long they get suffocate and rot away…” (r/houseplants)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://youtu.be/b34al8YmQSA?t=204&quot;&gt;“Where does a tree come from?” from
“Why almost all coal was made at the same time” (Steve Mould)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/01/10/watering-houseplants.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/01/10/watering-houseplants.html</guid>
        
        
      </item>
    
      <item>
        <title>DOM clobbering 1Password to fix syntax highlighting</title>
        <description>&lt;p&gt;&lt;strong&gt;TLDR;&lt;/strong&gt; Add this to your site if 1Password’s browser extension is &lt;a href=&quot;https://www.1password.community/discussions/developers/1password-chrome-extension-is-incorrectly-manipulating--blocks/165639/replies/165982&quot;&gt;breaking syntax highlighting&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Prism&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Prism&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;manual&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Skip to the &lt;a href=&quot;#payload&quot;&gt;breakdown&lt;/a&gt;, or read on.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;backstory&quot;&gt;Backstory&lt;/h2&gt;

&lt;p&gt;About two weeks ago, I noticed that this blog’s code syntax highlighting stopped working. This blog (&lt;a href=&quot;https://tautology.town&quot;&gt;tautology.town&lt;/a&gt;) uses &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;, which comes with &lt;a href=&quot;https://github.com/rouge-ruby/rouge&quot;&gt;Rouge&lt;/a&gt; syntax highlighting out of the box.&lt;/p&gt;

&lt;p&gt;Eventually, I discovered that 1Password’s browser extension was inadvertently overriding syntax highlighting in all websites (&lt;a href=&quot;https://www.1password.community/discussions/developers/1password-chrome-extension-is-incorrectly-manipulating--blocks/165639/replies/165982&quot;&gt;bug report&lt;/a&gt;). In a recent update, 1Password added &lt;a href=&quot;https://prismjs.com/&quot;&gt;Prism.js&lt;/a&gt; as part of an experimental &lt;a href=&quot;https://1password.com/blog/product-update-features-and-security-q3-2024#:~:text=In%20labs%3A%20Generate%20and%20fill%20formatted%20content%20with%20secure%20snippets&quot;&gt;snippets feature&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Prism.js &lt;a href=&quot;https://prismjs.com/#manual-highlighting&quot;&gt;highlights everything by default&lt;/a&gt;, so bundling it into the content script without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Prism.manual = true&lt;/code&gt; means it runs on any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.language-*&lt;/code&gt; class code block on every website.&lt;/p&gt;

&lt;p&gt;The immediate fix for 1Password is simple: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Prism.manual = true&lt;/code&gt; and manually invoke Prism.&lt;/p&gt;

&lt;p&gt;More importantly, &lt;em&gt;Prism.js should not be in the content script at all&lt;/em&gt;. The feature is restricted to the 1Password vault, so Prism should be moved to the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Popups&quot;&gt;extension’s popup script&lt;/a&gt;. It seems like a major oversight for this to have been included.&lt;/p&gt;

&lt;p&gt;Worst of all, it appears to be a useless dependency. I couldn’t find the snippet feature in the browser extension. The desktop app has snippets but doesn’t highlight code.&lt;/p&gt;

&lt;!-- From https://stackoverflow.com/a/19360305/7971276 --&gt;
&lt;figure class=&quot;image&quot;&gt;
&lt;!-- Need to | escape the description so I can include quotation marks --&gt;
&lt;img src=&quot;/assets/2026/1password-desktop-snippets.png&quot; alt=&quot;Screenshot of 1Password desktop, showing an unhighlighted code snippet&quot; /&gt;
  &lt;figcaption&gt;Screenshot of 1Password desktop, showing an unhighlighted code snippet&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I believe Prism.js is only included as a dependency of &lt;a href=&quot;https://github.com/facebook/lexical/blob/5bbbe849bd229e1db0e7b536e6a919520ada7bb2/packages/lexical-code/src/FacadePrism.ts&quot;&gt;@lexical/code&lt;/a&gt;, part of the editor used for snippets. If so, Prism.js could be omitted entirely.&lt;/p&gt;

&lt;p&gt;I assumed this would be &lt;a href=&quot;https://www.1password.community/discussions/developers/1password-chrome-extension-is-incorrectly-manipulating--blocks/165639/replies/165687&quot;&gt;fixed quickly&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;some-time-later&quot;&gt;Some time later…&lt;/h2&gt;

&lt;p&gt;Two weeks later, the issue was still not fixed and managed to pick up &lt;a href=&quot;https://x.com/youyuxi/status/2005904473332564339&quot;&gt;quite a bit of attention online (2.6k likes)&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote class=&quot;card&quot;&gt;
&lt;p&gt;youyuxi: .@1Password browser extension is injecting Prism.js &lt;em&gt;globally&lt;/em&gt; on every page, which then applies its syntax highlighting logic on all &amp;lt;code&amp;gt; blocks matching [lang=*] regardless of whether it’s meant to be compatible, thus breaking original highlighting.&lt;/p&gt;

&lt;p&gt;Terrible negligence and even more so that this made to prod while already flagged during beta.&lt;/p&gt;

&lt;p&gt;Been a user for a long time but this will def push me to an alternative if not fixed soon.&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;Evan You (@youyuxi) is the creator of Vue and Vite.&lt;/p&gt;

&lt;p&gt;I think this was the push for 1Password to act. They are finally &lt;a href=&quot;https://www.1password.community/discussions/developers/1password-chrome-extension-is-incorrectly-manipulating--blocks/165639/replies/165982&quot;&gt;deploying a fix&lt;/a&gt; and promise a postmortem.&lt;/p&gt;

&lt;p&gt;At the time of writing, the fix is not yet live for me. I see an update in the Chrome store, but not in Firefox or Safari (both iOS and MacOS).&lt;/p&gt;

&lt;p&gt;While we wait, we can take matters into our own hands.&lt;/p&gt;

&lt;h2 id=&quot;dom-clobbering&quot;&gt;DOM clobbering&lt;/h2&gt;

&lt;p&gt;Web extension content scripts get access to the DOM (to do stuff) but run in an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#dom_access&quot;&gt;isolated Javascript context&lt;/a&gt; to prevent web content from accessing or overriding their behavior. Without this, they could be vulnerable to something like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Prototype_pollution&quot;&gt;prototype pollution&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, scripts with DOM access are still susceptible to &lt;em&gt;DOM clobbering&lt;/em&gt;. DOM clobbering is using the DOM to define/redefine things expected by the running Javascript.&lt;/p&gt;

&lt;p&gt;The HTML standard allows for “named property access” on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window#named_properties&quot;&gt;docs&lt;/a&gt;) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document&lt;/code&gt; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document#named_properties&quot;&gt;docs&lt;/a&gt;), which means HTML DOM elements with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; set can be accessed as if they were Javascript properties. For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For each &amp;lt;embed&amp;gt;, &amp;lt;form&amp;gt;, &amp;lt;iframe&amp;gt;, &amp;lt;img&amp;gt;, and &amp;lt;object&amp;gt; element, its name (if non-empty) is exposed. For example, if the document contains &amp;lt;form name=”my_form”&amp;gt;, then window[“my_form”] &lt;strong&gt;(and its equivalent window.my_form) returns a reference to that element.&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;For each HTML element, its id (if non-empty) is exposed.&lt;/p&gt;

  &lt;p&gt;If a property corresponds to a single element, that element is directly returned. If the property corresponds to multiple elements, then an HTMLCollection is returned containing all of them. If any of the elements is a navigable &amp;lt;iframe&amp;gt; or &amp;lt;object&amp;gt;, then the contentWindow of first such iframe is returned instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recall that properties of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt; are implicitly global, so in the example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_form&lt;/code&gt; also returns the form.&lt;/p&gt;

&lt;p&gt;Even DOM APIs like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.addEventListener&lt;/code&gt; can be &lt;a href=&quot;https://domclob.xyz/domc_wiki/browsers/browserAPIs.html&quot;&gt;overridden in this way&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;addEventListener&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;cm&quot;&gt;/* prints &amp;lt;form name=&quot;addEventListener&quot;&amp;gt;&amp;lt;/form&amp;gt; */&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are some limitations to this attack:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It only works if the corresponding value is not already defined by the Javascript context.&lt;/li&gt;
  &lt;li&gt;You can only return an HTML element or an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTMLCollection&lt;/code&gt;, not arbitrary values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, you can take advantage of things like:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTMLCollection&lt;/code&gt; access by id/name to &lt;a href=&quot;https://aszx87410.github.io/beyond-xss/en/ch3/dom-clobbering/#nested-dom-clobbering&quot;&gt;DOM clobber nested properties&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;HTML elements are truthy, so you can override false values.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toString()&lt;/code&gt; gives the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;href&lt;/code&gt; value, and can be coerced with concatenation.&lt;/li&gt;
  &lt;li&gt;Convenient property access that matches HTML elements: e.g. if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; is accessed you can clobber with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input value=&quot;overridden-value&quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can use the first two tricks to clobber 1Password’s Prism.js.&lt;/p&gt;

&lt;h2 id=&quot;clobbering-prismjs&quot;&gt;Clobbering Prism.js&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://prismjs.com/#manual-highlighting&quot;&gt;Prism.js docs&lt;/a&gt; show how to normally disable automatic highlighting:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Prism&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Prism&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Prism&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;manual&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since 1Password’s content script is in an isolated context, including this script on a site won’t affect the content script. Its Prism.js won’t ever see  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.Prism.manual = true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s look at the Prism.js source to see how this setting is evaluated. On &lt;a href=&quot;https://github.com/PrismJS/prism/blob/298b75f1764c4abfe76d997394b7b149845f685a/components/prism-core.js#L51&quot;&gt;line 51 of&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prism-core.js&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;manual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Prism&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Prism&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;manual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s checked &lt;a href=&quot;https://github.com/PrismJS/prism/blob/298b75f1764c4abfe76d997394b7b149845f685a/components/prism-core.js#L1184C2-L1184C18&quot;&gt;on line 1184&lt;/a&gt; before auto-highlighting:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;manual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_self&lt;/code&gt; is set &lt;a href=&quot;https://github.com/PrismJS/prism/blob/298b75f1764c4abfe76d997394b7b149845f685a/components/prism-core.js#L3&quot;&gt;on line 3&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_self&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// if in browser&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;WorkerGlobalScope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;WorkerGlobalScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// if in worker&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// if in node js&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manual&lt;/code&gt; is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.Prism.manual&lt;/code&gt; and can be DOM clobbered!&lt;/p&gt;

&lt;h3 id=&quot;payload&quot;&gt;Payload&lt;/h3&gt;

&lt;p&gt;Let’s craft our payload and put it in our HTML:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Prism&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hidden&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Prism&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;manual&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;breakdown&quot;&gt;Breakdown&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;There are two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags with matching &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id=Prism&lt;/code&gt;, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.Prism&lt;/code&gt; will return a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTMLCollection&lt;/code&gt; with both.&lt;/li&gt;
  &lt;li&gt;An HTMLCollection’s elements can be accessed by their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;, so we give the second tag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name=&quot;manual&quot;&lt;/code&gt; to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manual&lt;/code&gt; a property of the collection that returns the second &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a&amp;gt;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Finally, an HTML element is truthy, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!window.Prism.manual&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;, stopping automatic highlighting.&lt;/li&gt;
  &lt;li&gt;Also, we add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hidden&lt;/code&gt; so they aren’t displayed anywhere + ignored by screen readers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can check in the console:&lt;/p&gt;

&lt;!--bash looks better than javascript or html for this devtools block--&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; window.Prism
HTMLCollection&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;a#Prism, a#Prism, Prism: a#Prism, manual: a#Prism]

&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; window.Prism.manual
&amp;lt;a hidden &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Prism&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;manual&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;lt;/a&amp;gt;

&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;window.Prism.manual
&lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And it works! This is the fix currently deployed to this site, if you need further proof it works.&lt;/p&gt;

&lt;h2 id=&quot;security-implications&quot;&gt;Security implications&lt;/h2&gt;

&lt;h3 id=&quot;1password-the-app&quot;&gt;1Password, the app&lt;/h3&gt;

&lt;p&gt;You may be (rightfully) concerned about other ways 1Password could be DOM clobbered via Prism.js.&lt;/p&gt;

&lt;p&gt;In fact, I learned about DOM clobbering when looking into the security of Prism.js.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2024-53382&quot;&gt;CVE-2024-53382&lt;/a&gt; describes an XSS on Prism.js 1.29.0 using DOM clobbering. The &lt;a href=&quot;https://gist.github.com/jackfromeast/aeb128e44f05f95828a1a824708df660&quot;&gt;original report&lt;/a&gt; gives a great summary of the vulnerability. At a high level, Prism’s autoloader plugin uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.currentScript&lt;/code&gt; to dynamically load language definitions that an attacker could clobber to load their own script. It was patched in 1.30 by checking if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.currentScript.tagName&lt;/code&gt; equals &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;SCRIPT&apos;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thankfully, as far as I could tell, the version deployed by 1Password is not vulnerable to XSS via Prism. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manual&lt;/code&gt; parameter is designed to be overridden, which inadvertently allows for DOM clobbering. Other things on Prism don’t appear to be clobberable.&lt;/p&gt;

&lt;p&gt;I did discover XSS vulnerabilities in v2 of Prism.js and will submit a fix or issue. That version has been in development &lt;a href=&quot;https://github.com/orgs/PrismJS/discussions/3531&quot;&gt;since 2022&lt;/a&gt; and is the &lt;a href=&quot;https://github.com/PrismJS/prism&quot;&gt;default branch on GitHub&lt;/a&gt;, but isn’t yet released.&lt;/p&gt;

&lt;h3 id=&quot;1password-the-company&quot;&gt;1Password, the company&lt;/h3&gt;

&lt;p&gt;The bigger question is what this signals about 1Password, the company.&lt;/p&gt;

&lt;p&gt;I’ve historically trusted 1Password over other password managers due to their &lt;a href=&quot;https://support.1password.com/secret-key-security/&quot;&gt;secret key&lt;/a&gt; based security model (especially over &lt;a href=&quot;https://en.wikipedia.org/wiki/LastPass#Security_Criticism&quot;&gt;LastPass&lt;/a&gt;, which is not safe at all). 1Password has a good reputation from publishing about security and their approach.&lt;/p&gt;

&lt;p&gt;This incident has me second guessing. It shouldn’t have been addressed so slowly (2+ weeks), nor should it have passed code review. Adding stuff to the content script is a big deal for extension development.&lt;/p&gt;

&lt;p&gt;It appears to stem from improper bundling in the shared Javascript between the Electron desktop app and the browser extension. &lt;a href=&quot;https://pxlnv.com/linklog/1password-goes-electron/&quot;&gt;Moving from native to Electron&lt;/a&gt; already seemed like a cost-saving measure.&lt;/p&gt;

&lt;p&gt;Security is largely a function of &lt;a href=&quot;https://google.github.io/building-secure-and-reliable-systems/raw/ch21.html&quot;&gt;organizational culture&lt;/a&gt;. I worry they’re &lt;a href=&quot;https://1password.com/press/2025/nov/1password-strengthens-leadership-amid-growth-milestone&quot;&gt;prioritizing business and growth&lt;/a&gt; over product and security.&lt;/p&gt;

&lt;p&gt;Let’s hope I’m wrong. We’ll find out more if/when they publish the postmortem.&lt;/p&gt;

&lt;p&gt;Trust is hard to gain, easy to lose, and harder still to get back.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;I like this writeup of DOM clobbering best: &lt;a href=&quot;https://aszx87410.github.io/beyond-xss/en/ch3/dom-clobbering/&quot;&gt;Beyond XSS: Can HTML affect JavaScript? Introduction to DOM clobbering&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A useful table of common patterns: &lt;a href=&quot;https://domclob.xyz/domc_wiki/indicators/patterns.html&quot;&gt;DOM Clobbering Wiki: Patterns and Guidelines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Another developer’s very similar experience: &lt;a href=&quot;https://borretti.me/article/1password-dependency-breaks-syntax-highlighting&quot;&gt;1Password Dependency Breaks Syntax Highlighting
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might also find interesting the WHATWG specs for named properties:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/nav-history-apis.html#named-access-on-the-window-object:window-3&quot;&gt;7.2.2.3 Named access on the Window object&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/dom.html#dom-document-namedItem-which&quot;&gt;3.1.6 DOM tree accessors&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2026/01/02/dom-clobbering-1password-prism.html</link>
        <guid isPermaLink="true">http://tautology.town/2026/01/02/dom-clobbering-1password-prism.html</guid>
        
        
      </item>
    
      <item>
        <title>Learning about IP address routing</title>
        <description>&lt;p&gt;One question I had for a long time: What makes a destination IP address authoritative?&lt;/p&gt;

&lt;p&gt;I know you can &lt;a href=&quot;https://www.cloudflare.com/learning/ddos/glossary/ip-spoofing/&quot;&gt;spoof sending a message as a different IP address&lt;/a&gt;, but even if your ISP didn’t prevent you, you wouldn’t receive a response because it would be routed to the actual location of the address you spoofed. This had me wondering:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What prevents a destination IP address from being spoofed?&lt;/li&gt;
  &lt;li&gt;How do we know an IP address we connect to is legitimate?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-short-answer&quot;&gt;The short answer&lt;/h2&gt;

&lt;p&gt;The destination of an IP address is determined through decentralized routing, a protocol called BGP, loose coordination between large computer networks, regional registries for IP address ranges, and lots of trust!&lt;/p&gt;

&lt;p&gt;“Spoofing a destination IP address” is possible, and is known as &lt;em&gt;IP hijacking&lt;/em&gt;. One way this can happen is if a malicious or misconfigured actor advertises incorrect routing to the rest of the network, known as &lt;a href=&quot;https://www.cloudflare.com/learning/security/glossary/what-is-bgp/#learning-content-h2&quot;&gt;BGP hijacking&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;a-long-explanation&quot;&gt;A long explanation&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Internet&lt;/strong&gt; is a network that connects other networks. An “inter-network” network, if you will. An &lt;strong&gt;&lt;a href=&quot;https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/&quot;&gt;Autonomous System (AS)&lt;/a&gt;&lt;/strong&gt; is the largest network “unit” that the Internet interconnects.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Each AS has IP address ranges it controls, registered with a &lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Regional_Internet_registry&quot;&gt;Regional Internet Registry (RIR)&lt;/a&gt;&lt;/strong&gt;. An AS knows how to reach any IP address in its ranges.&lt;/li&gt;
  &lt;li&gt;Each AS is identified by an &lt;strong&gt;Autonomous System Number (ASN)&lt;/strong&gt; assigned by their RIR.&lt;/li&gt;
  &lt;li&gt;Autonomous Systems are connected to other Autonomous Systems and share reachability using the &lt;strong&gt;&lt;a href=&quot;https://www.cloudflare.com/learning/security/glossary/what-is-bgp/&quot;&gt;Border Gateway Protocol (BGP)&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BGP messages say something like: “&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4271#section-5.1.3&quot;&gt;This is the next IP&lt;/a&gt; to follow to &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4271#autoid-11:~:text=Network%20Layer%20Reachability%20Information%3A&quot;&gt;reach IPs with this prefix&lt;/a&gt;, via &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4271#section-5.1.2&quot;&gt;this path of ASNs&lt;/a&gt;”. This information is propagated transitively through Autonomous Systems and processed by each router to build their own &lt;a href=&quot;https://en.wikipedia.org/wiki/Routing_table&quot;&gt;routing tables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A routing table says “to reach the IPs in this range, the next IP in the path is this”.&lt;/p&gt;

&lt;h3 id=&quot;routing-packets&quot;&gt;Routing packets&lt;/h3&gt;

&lt;p&gt;When you send a packet, it’s &lt;a href=&quot;https://en.wikipedia.org/wiki/IP_routing#Routing_algorithm&quot;&gt;passed from router to router&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;When a packet arrives at a router, the router looks up its routing table for a prefix matching the destination IP address.&lt;/li&gt;
  &lt;li&gt;The router forwards the packet to the “next hop” IP address for that prefix. I think the next hop IP address is usually the local IP interface to a connected router.&lt;/li&gt;
  &lt;li&gt;The process repeats, until the destination is reached.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On my home network, the ISP assigns me a public IP address. The process works the same in reverse when receiving a response, until a packet reaches my router via my public IP and my router forwards the packet to my computer somehow.&lt;/p&gt;

&lt;h3 id=&quot;trust-and-coordination&quot;&gt;Trust and coordination&lt;/h3&gt;

&lt;p&gt;By design, the Internet is decentralized. The hop-by-hop nature of routing means an AS need only talk to its own neighbors in order to reach the entire connected set.&lt;/p&gt;

&lt;p&gt;A few things to notice:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Autonomous Systems have full discretion with what they announce with BGP.&lt;/li&gt;
  &lt;li&gt;Reachability information from one node is propagated throughout the network.&lt;/li&gt;
  &lt;li&gt;Routers have full discretion to decide how to route a packet they’re forwarded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any router along the forwarded path could respond or tamper with a packet (&lt;a href=&quot;https://en.wikipedia.org/wiki/Man-in-the-middle_attack&quot;&gt;man-in-the-middle (MITM) attack&lt;/a&gt;). You place a lot of trust into your ISP!&lt;/p&gt;

&lt;p&gt;The rogue router could &lt;a href=&quot;https://blog.apnic.net/2021/05/24/a-tool-to-detect-bgp-lies/&quot;&gt;lie about the ASN path&lt;/a&gt; to advertise a fake shorter or more specific route with BGP, leading to traffic consistently being routed to it. This is exactly what BGP hijacking is. On top of malicious hacks, this happens at lot by accident (e.g. &lt;a href=&quot;https://blog.apnic.net/2025/05/06/analysis-of-a-route-leak/&quot;&gt;route leaks&lt;/a&gt;), causing some famous outages.&lt;/p&gt;

&lt;p&gt;It’s a big enough problem that Cloudflare has a dedicated microsite: &lt;a href=&quot;https://isbgpsafeyet.com/&quot;&gt;isbgpsafeyet.com&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is BGP safe yet? No.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many &lt;a href=&quot;https://networkphil.com/2024/02/20/best-practices-for-enhancing-bgp-security/&quot;&gt;strategies to improve BGP security&lt;/a&gt;, but the simplest is to cross check against another source. RIRs know what IP ranges go to which ASNs, so an AS announcing a route origin can be cross checked against the RIR’s signed records, known as &lt;strong&gt;Resource Public Key Infrastructure (RPKI)&lt;/strong&gt;. There are also databases called &lt;strong&gt;Internet Routing Registries (IRR)&lt;/strong&gt; that Autonomous Systems publish routes to that can be used to filter obviously invalid routes. In practice, a large AS will be selective with who they accept BGP messages from and have &lt;a href=&quot;https://www.cloudflare.com/learning/network-layer/what-is-peering/&quot;&gt;different peering arrangements&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This isn’t perfect: we still have the man-in-the-middle problem, and an AS could still lie while publishing a legitimate-looking route.&lt;/p&gt;

&lt;p&gt;This is why things like HTTPS are important — although, you &lt;a href=&quot;https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_ReflectionsonTrustingTrust.pdf&quot;&gt;still need to trust somebody&lt;/a&gt;, in this case the root cert.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;addendum-a-few-things-to-try&quot;&gt;Addendum: a few things to try&lt;/h2&gt;

&lt;h3 id=&quot;routing-tables&quot;&gt;Routing tables&lt;/h3&gt;

&lt;p&gt;Your computer has a routing table. Here’s the one on my laptop (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;routel&lt;/code&gt; on Linux prints this nicer):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ip route
default via 192.168.0.1 dev en0
127.0.0.0/8 via 127.0.0.1 dev lo0
127.0.0.1/32 via 127.0.0.1 dev lo0
169.254.0.0/16 dev en0 scope link
192.168.0.0/24 dev en0 scope link
192.168.0.1/32 dev en0 scope link
192.168.0.197/32 dev en0 scope link
224.0.0.0/4 dev en0 scope link
255.255.255.255/32 dev en0 scope link
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first line is saying the default route to send traffic through is via 192.168.0.1 and device en0, if no other prefix is matched. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;en0&lt;/code&gt; is the network interface, and apparently refers to my laptop’s network card. If I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arp -a | grep &quot;(192.168.0.1)&quot;&lt;/code&gt;, I think it prints the MAC address of my wifi router.&lt;/p&gt;

&lt;p&gt;So, this is saying that the first hop goes through network card to my router.&lt;/p&gt;

&lt;h3 id=&quot;tracing-routes&quot;&gt;Tracing routes&lt;/h3&gt;

&lt;p&gt;We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traceroute&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mtr&lt;/code&gt;) to look at the path that a packet can take.&lt;/p&gt;

&lt;p&gt;Because no single router knows the full path of a request, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traceroute&lt;/code&gt; works by &lt;a href=&quot;https://en.wikipedia.org/wiki/Traceroute&quot;&gt;repeatedly sending packets with an incrementing hop limit&lt;/a&gt; then using the source IP of the error message response and hop count to infer the path of a request. (Interestingly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traceroute&lt;/code&gt; to this blog does not work, possibly because the Github Pages CDN does not reply, although &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mtr&lt;/code&gt; works.)&lt;/p&gt;

&lt;p&gt;Here’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traceroute&lt;/code&gt; from a DigitalOcean droplet to “example.com” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-q 1&lt;/code&gt; so only 1 IP is sampled per hop count):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ traceroute -q 1 example.com
traceroute to example.com (23.192.228.84), 30 hops max, 60 byte packets
 1  138.68.34.248 (138.68.34.248)  1.054 ms
 2  143.244.192.80 (143.244.192.80)  1.841 ms
 3  143.244.227.102 (143.244.227.102)  1.654 ms
 4  143.244.225.47 (143.244.225.47)  1.618 ms
 5  *
 6  192.168.224.135 (192.168.224.135)  1.676 ms
 7  192.168.226.131 (192.168.226.131)  1.446 ms
 8  a23-192-228-84.deploy.static.akamaitechnologies.com (23.192.228.84)  1.542 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I looked up the ASNs associated with each IP by looking it up on &lt;a href=&quot;https://bgp.tools/&quot;&gt;bgp.tools&lt;/a&gt;. Surprisingly, most were not found by bgp.tools although most were at least found in RIR registries.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;138.68.34.248&lt;/code&gt;: &lt;a href=&quot;https://bgp.tools/prefix/138.68.34.0/24&quot;&gt;138.68.34.0/24&lt;/a&gt; originated by AS14061 (DigitalOcean).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;143.244.192.80&lt;/code&gt;: No prefix match, but allocated to &lt;a href=&quot;https://bgp.tools/rir-owner/ARIN-DO-13&quot;&gt;ARIN-DO-13&lt;/a&gt; (DigitalOcean).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;143.244.227.102&lt;/code&gt;: No prefix match, but allocated to &lt;a href=&quot;https://bgp.tools/rir-owner/ARIN-DO-13&quot;&gt;ARIN-DO-13&lt;/a&gt; (DigitalOcean).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;143.244.225.47&lt;/code&gt;: No prefix match, but allocated to &lt;a href=&quot;https://bgp.tools/rir-owner/ARIN-DO-13&quot;&gt;ARIN-DO-13&lt;/a&gt; (DigitalOcean).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.224.135&lt;/code&gt;: Not found anywhere, 192.168.0.0/16 is a private address space.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.226.131&lt;/code&gt;: Not found anywhere, 192.168.0.0/16 is a private address space.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;23.192.228.84&lt;/code&gt;: Overlapping Prefixes Detected, both under &lt;a href=&quot;https://bgp.tools/prefix-selector?ip=23.192.228.84&quot;&gt;AS20940&lt;/a&gt; (Akamai).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the unannounced ASNs, I assume these are part of the private network of DigitalOcean as the request finds its way out of the VPS network. I assume the private address spaces are related to that, and that Akamai and DigitalOcean are connected somewhere in an &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_exchange_point&quot;&gt;Internet Exchange Point (IXP)&lt;/a&gt; with some internal infrastucture corresponding to the two private IP addresses.&lt;/p&gt;

&lt;p&gt;To learn more, you might like &lt;a href=&quot;https://jvns.ca/blog/2021/10/05/tools-to-look-at-bgp-routes/&quot;&gt;this post&lt;/a&gt; from Julia Evans.&lt;/p&gt;
</description>
        <pubDate>Sun, 14 Dec 2025 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2025/12/14/ip-address-routing.html</link>
        <guid isPermaLink="true">http://tautology.town/2025/12/14/ip-address-routing.html</guid>
        
        
      </item>
    
      <item>
        <title>Ruby&apos;s object model: eigenclasses, metaclasses, and more</title>
        <description>&lt;p&gt;&lt;em&gt;Or, a Class class masterclass.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One way or another, a famous question will cross every Rubyist’s mind, probably many times: &lt;em&gt;what’s an eigenclass?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the Ruby docs, there’s a tremendous diagram under &lt;a href=&quot;https://docs.ruby-lang.org/en/3.4/Class.html#method-i-new:~:text=In%20the%20diagram%20that%20follows%2C%20the%20vertical%20arrows%20represent%20inheritance%2C%20and%20the%20parentheses%20metaclasses.&quot;&gt;the page for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class Class&lt;/code&gt;&lt;/a&gt; that made me think of this again:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Classes, modules, and objects are interrelated. In the diagram that follows, the vertical arrows represent inheritance, and the parentheses metaclasses. All metaclasses are instances of the class ‘Class’.&lt;/p&gt;

  &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                         +---------+             +-...
                         |         |             |
         BasicObject-----|--&amp;gt;(BasicObject)-------|-...
             ^           |         ^             |
             |           |         |             |
          Object---------|-----&amp;gt;(Object)---------|-...
             ^           |         ^             |
             |           |         |             |
             +-------+   |         +--------+    |
             |       |   |         |        |    |
             |    Module-|---------|---&amp;gt;(Module)-|-...
             |       ^   |         |        ^    |
             |       |   |         |        |    |
             |     Class-|---------|----&amp;gt;(Class)-|-...
             |       ^   |         |        ^    |
             |       +---+         |        +----+
             |                     |
obj---&amp;gt;OtherClass----------&amp;gt;(OtherClass)-----------...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought I more or less understood Ruby classes, eigenclasses, and what have you, until I came across this. I don’t know about you, but this made very little sense to me. Is a metaclass the same as an eigenclass?&lt;/p&gt;

&lt;p&gt;This Question is famous and Frequently Asked enough that Ruby has &lt;a href=&quot;https://www.ruby-lang.org/en/documentation/faq/8/#:~:text=What%20is%20a%20singleton%20class%3F&quot;&gt;an official FAQ answer&lt;/a&gt; (also: why is this in an FAQ instead of &lt;a href=&quot;https://gds.blog.gov.uk/2013/07/25/faqs-why-we-dont-have-them/&quot;&gt;having better docs&lt;/a&gt;?).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A singleton class is an anonymous class that is created by subclassing the class associated with a particular object. Singleton classes are another way of extending the functionality associated with just one object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Better. By the way, singleton class is another word for eigenclass. Does the diagram make any more sense?&lt;/p&gt;

&lt;p&gt;After an exciting afternoon, I think I have the definitive answer. Let me now try to answer the question, by way of answering everything.&lt;/p&gt;

&lt;h3 id=&quot;objects-have-a-class&quot;&gt;Objects have a class&lt;/h3&gt;

&lt;p&gt;Everything in Ruby is an object.&lt;/p&gt;

&lt;p&gt;Every object in Ruby has a class.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;object -- has a --&amp;gt; a class 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even “primitive” types are objects.&lt;/p&gt;

&lt;p&gt;Every object in Ruby has a class.&lt;/p&gt;

&lt;p&gt;The class is available in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; method, which returns the class of an object.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;String&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Integer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;classes-have-ancestors&quot;&gt;Classes have ancestors&lt;/h3&gt;

&lt;p&gt;We can define our own classes with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; keyword. Then make an instance of that class with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x0000000101197e20&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A class has ancestors which it inherits from. On the class itself there is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ancestors&lt;/code&gt; method, which returns the order of classes that it inherits from.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;              (... rest of ancestors)
                      |  
                    a class
                      ^
                      |
                inherits from  
                      |
object -- has a --&amp;gt; a class 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BasicObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;method-resolution&quot;&gt;Method resolution&lt;/h4&gt;

&lt;p&gt;When we call a method for an object, we look up its class and its ancestors, then look for a method with that name in each class, up the ancestor chain (left to right).&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;say&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;aye&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:say&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;say&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;bye&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;010&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:say&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;011&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BasicObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;012&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BasicObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Every class inherits from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt;. The root of every class heirarchy is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BasicObject&lt;/code&gt;. These define the most basic methods every object (everything) can call.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BasicObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_methods&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:equal?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:__send__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:==&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:__id__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:instance_eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:instance_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;classes-are-objects&quot;&gt;Classes are objects&lt;/h3&gt;

&lt;p&gt;Everything in Ruby is an object.&lt;/p&gt;

&lt;p&gt;Every object in Ruby has a class.&lt;/p&gt;

&lt;p&gt;Even classes are objects. So they have a class too. They are objects with class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; (uppercase).&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_of?&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; keyword is just one way to give a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; a name.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:0x0000000102778488&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x00000001026e7c80&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The class of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; is … itself. Because it’s a class!&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is an instance of itself.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_of?&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; also has ancestors.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BasicObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It turns out all classes inherit from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Module&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Module&lt;/code&gt; is also a class. Modules objects that are instances of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Module&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;B&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A lot of things are a class, because everything is an object and objects have a class.&lt;/p&gt;

&lt;h3 id=&quot;everything-is-an-object&quot;&gt;Everything is an object&lt;/h3&gt;

&lt;p&gt;A “class” is an object that has a class of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It can help to think of an object’s class not as “what it is”, but &lt;em&gt;as a property of the object&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Indeed, in Ruby’s internals, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;klass&lt;/code&gt; is &lt;a href=&quot;https://docs.ruby-lang.org/capi/en/master/d2/d22/struct_r_basic.html#a16e74a53ecb346b88c35e813bae8fe32&quot;&gt;a property on a C struct&lt;/a&gt; included on all objects. And everything is an object.&lt;/p&gt;

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

  &lt;p&gt;Class of an object.&lt;/p&gt;

  &lt;p&gt;Every object has its class. Also, everything is an object in Ruby. This means classes are also objects. Classes have their own classes, classes of classes have their classes too, and it recursively continues forever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; is an object with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;klass&lt;/code&gt; pointer to itself.&lt;/p&gt;

&lt;h3 id=&quot;singleton-classes-and-eigenclasses&quot;&gt;Singleton classes and eigenclasses&lt;/h3&gt;

&lt;p&gt;So what’s an eigenclass?&lt;/p&gt;

&lt;p&gt;It’s a singleton class, of course! Singleton class is the official terminology now.&lt;/p&gt;

&lt;p&gt;Okay, so what’s a singleton class?&lt;/p&gt;

&lt;p&gt;When you create an object from a class, the object creates and stores an anonymous class that inherits from the original class.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x00000001032e7da0&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;A:0x00000001032e7da0&amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;A:0x00000001032e7da0&amp;gt;&amp;gt;, A, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Importantly, because this singleton class is created per instance, any methods set on the object’s singleton class do not modify the object’s regular class.&lt;/p&gt;

&lt;p&gt;When we call a method, we look up the method name on the instance-specific singleton class first. In Ruby internals, an object’s class is &lt;a href=&quot;https://noelrappin.com/blog/2025/03/better-know-a-ruby-thing-method-lookup/#but-i-thought-we-said-something-about-singleton-classes&quot;&gt;secretly actually the singleton class&lt;/a&gt;, which inherits from the class the object was instantiated from and allows method resolution to work down the ancestor chain as expected.&lt;/p&gt;

&lt;p&gt;This is the point of the singleton class! It allows us to add or override methods at the object level without affecting other instances of the object’s class.&lt;/p&gt;

&lt;p&gt;We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def {obj}.{method_name}&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; {obj}&lt;/code&gt; syntax to define a method on the singleton class for that instance.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x00000001010976b0&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x0000000101097430&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;a1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;hi&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:hello&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bye&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;010&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;cya&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;011&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;012&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:bye&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;013&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hi&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;014&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bye&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cya&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;015&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;NoMethodError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;hello&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x0000000101097430&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;016&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x0000000101096738&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;017&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hello&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;NoMethodError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;hello&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bye&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;NoMethodError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bye&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x00000001010976b0&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bye&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;NoMethodError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bye&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;class-methods&quot;&gt;Class methods&lt;/h3&gt;

&lt;p&gt;This is also what allows class methods to be defined with the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def self.{method_name}&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; syntax in a class body.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WithClassMethods&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_method_1&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;one&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_method_2&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;two&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WithClassMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_method_1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WithClassMethods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class_method_2&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# WithClassMethods.new.class_method_1 =&amp;gt; NoMethodError&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# WithClassMethods.new.class_method_2 =&amp;gt; NoMethodError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All classes are objects, all objects have singletons, so class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; (which is an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt;) has a singleton class that we’re modifying with the same syntax! The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; in the class body points to class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:A&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on_the_class_singleton&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;yes&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:on_the_class_singleton&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instance_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:on_the_class_singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The class singleton class always inherits from the singleton classes of its ancestors, which is how you can call class methods defined on a parent class on a child class.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:B&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:B&amp;gt;, #&amp;lt;Class:A&amp;gt;, #&amp;lt;Class:Object&amp;gt;, #&amp;lt;Class:BasicObject&amp;gt;, Class, Module, Object, Kernel, BasicObject]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;B:0x0000000100f47378&amp;gt;&amp;gt;, B, A, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because every class is an object, class singletons are also an object, so this chain of inheritance repeats infinitely. Ruby lazily creates singleton classes, so does not repeat infinitely in practice, although you could access them infinitely.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;A:0x00000001003a7b58&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;A:0x00000001003a7b58&amp;gt;&amp;gt;, A, Object, Kernel, BasicObject]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:A&amp;gt;, #&amp;lt;Class:Object&amp;gt;, #&amp;lt;Class:BasicObject&amp;gt;, Class, Module, Object, Kernel, BasicObject]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;Class:A&amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;Class:A&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:Object&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:BasicObject&amp;gt;&amp;gt;, #&amp;lt;Class:Class&amp;gt;, #&amp;lt;Class:Module&amp;gt;, #&amp;lt;Class:Object&amp;gt;, #&amp;lt;Class:BasicObject&amp;gt;, Class, Module, Object, Kernel, BasicObject]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;singleton_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ancestors&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;#&amp;lt;Class:#&amp;lt;Class:#&amp;lt;Class:A&amp;gt;&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:#&amp;lt;Class:Object&amp;gt;&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:#&amp;lt;Class:BasicObject&amp;gt;&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:Class&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:Module&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:Object&amp;gt;&amp;gt;, #&amp;lt;Class:#&amp;lt;Class:BasicObject&amp;gt;&amp;gt;, #&amp;lt;Class:Class&amp;gt;, #&amp;lt;Class:Module&amp;gt;, #&amp;lt;Class:Object&amp;gt;, #&amp;lt;Class:BasicObject&amp;gt;, Class, Module, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&amp;lt;Class:A&amp;gt;&lt;/code&gt; is the singleton class of class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; (not the singleton class of an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Because it is a class singleton class, it inherits the singleton classes of the ancestors of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;, so the singleton classes of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&amp;lt;Class:Object&amp;gt;&lt;/code&gt;) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BasicObject&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&amp;lt;Class:BasicObject&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Since those singleton classes are themselves classes, the they eventually inherit from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt;. We could repeat this for another layer, then we’d have the singleton class of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt;, and so on and so forth.&lt;/p&gt;

&lt;p&gt;In the diagram, the horizontal arrows going right should be labeled “is of class” and “has singleton class”.&lt;/p&gt;

&lt;h3 id=&quot;language-is-hard&quot;&gt;Language is hard&lt;/h3&gt;

&lt;p&gt;Finally, “metaclass” means “class of a class”. Only singleton classes of classes are metaclasses, what you use to you define a class method. A singleton class of a class instance is not a metaclass (in the past this terminology was conflated).&lt;/p&gt;

&lt;p&gt;In this case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&amp;lt;Class:A&amp;gt;&lt;/code&gt; is a metaclass. Metaclasses eventually inherit from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt;, since they are classes.&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Everything is an object&lt;/li&gt;
  &lt;li&gt;Objects have a class&lt;/li&gt;
  &lt;li&gt;Classes have methods&lt;/li&gt;
  &lt;li&gt;Classes have ancestor classes&lt;/li&gt;
  &lt;li&gt;Methods are looked up on a class and its ancestors&lt;/li&gt;
  &lt;li&gt;Classes are objects&lt;/li&gt;
  &lt;li&gt;Classes have a class&lt;/li&gt;
  &lt;li&gt;Objects create a singleton class that inherits from their regular class&lt;/li&gt;
  &lt;li&gt;Objects look up methods on their singleton class first&lt;/li&gt;
  &lt;li&gt;Class methods are methods on the singleton class of a class&lt;/li&gt;
  &lt;li&gt;Singleton classes have a singleton class&lt;/li&gt;
  &lt;li&gt;The singleton class of a class inherits from singleton classes of the class’s ancestors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I think this explains what an eigenclass is, the entire infinitely recurring diagram from the beginning, as well as the rest of Ruby’s object model.&lt;/p&gt;

&lt;p&gt;Here is my version of the diagram (where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bob = Dog.new&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class Dog &amp;lt; Animal&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                      +---------+             +-...
                      |         |             |
      BasicObject-----|--&amp;gt;(BasicObject)-------|-...
          ^           |         ^             |
          |           |         |             |
      Object----------|-----&amp;gt;(Object)---------|-...
          ^           |         ^             |
          |           |         |             |
          +-------+   |         +--------+    |
          |       |   |         |        |    |
          |    Module-|---------|---&amp;gt;(Module)-|-...
          |       ^   |         |        ^    |
          |       |   |         |        |    |
          |     Class-|---------|----&amp;gt;(Class)-|-...
          |       ^   |         |        ^    |
          |       +---+         |        +----+
          |                     |
        Animal--------------&amp;gt;(Animal)-----------...
          ^                     ^
          |                     |
         Dog-----------------&amp;gt;(Dog)-------------...
          ^
          |
bob---&amp;gt;&quot;eigen-bob&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vertical lines represent inheritance, horizontal lines represent singleton classes, and parentheses represent metaclasses.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;p&gt;Noel Rappin’s “Better Know A Ruby Thing” series on Ruby singletons is the most valuable resource I used:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://noelrappin.com/blog/2025/01/better-know-a-ruby-thing-singleton-classes/&quot;&gt;Better Know A Ruby Thing: Singleton Classes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://noelrappin.com/blog/2025/03/better-know-a-ruby-thing-method-lookup/&quot;&gt;Better Know A Ruby Thing: Method Lookup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As short as the Ruby FAQ, but much more informational: &lt;a href=&quot;https://codequizzes.wordpress.com/2014/04/11/singleton-classes-in-ruby-aka-eigenclasses/&quot;&gt;Singleton Classes in Ruby (aka eigenclasses)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.ruby-lang.org/en/3.4/syntax_rdoc.html&quot;&gt;Official Ruby syntax docs&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.ruby-lang.org/en/3.4/syntax/modules_and_classes_rdoc.html#label-Singleton+Classes&quot;&gt;Ruby docs: Singleton Classes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.ruby-lang.org/en/3.4/syntax/calling_methods_rdoc.html#label-Method+Lookup&quot;&gt;Ruby docs: Method Lookup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post from Yehuda Katz is good but I think predates standard terminology, so mentally replace every occurrence of “metaclass” with “singleton class”: &lt;a href=&quot;https://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/&quot;&gt;Metaprogramming in Ruby: It’s All About the Self&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate>
        <link>http://tautology.town/2025/12/01/ruby-object-model.html</link>
        <guid isPermaLink="true">http://tautology.town/2025/12/01/ruby-object-model.html</guid>
        
        
      </item>
    
  </channel>
</rss>
