<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Vondrak</title>
    <description>The latest articles on DEV Community by Alex Vondrak (@ajvondrak).</description>
    <link>https://dev.to/ajvondrak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F308469%2F9ad5a268-8d44-46f6-8bdd-509c5d24698d.jpeg</url>
      <title>DEV Community: Alex Vondrak</title>
      <link>https://dev.to/ajvondrak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ajvondrak"/>
    <language>en</language>
    <item>
      <title>On RSpec, Minitest, and Doing Magic Right</title>
      <dc:creator>Alex Vondrak</dc:creator>
      <pubDate>Fri, 08 May 2020 04:19:16 +0000</pubDate>
      <link>https://dev.to/ajvondrak/on-rspec-minitest-and-doing-magic-right-3k16</link>
      <guid>https://dev.to/ajvondrak/on-rspec-minitest-and-doing-magic-right-3k16</guid>
      <description>&lt;p&gt;I started programming Ruby professionally in 2014, which meant I had to learn &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt;. To this day, it remains the de facto standard for writing tests. And to this day, I still don't really get it.&lt;/p&gt;

&lt;p&gt;Back then, I didn't get how to use it: contexts, hooks, expectations, mocks. It's a lot to learn, especially for inexperienced programmers. Still, I persisted. I fumbled my way through the syntax. Resources like the &lt;a href="https://rspec.rubystyle.guide/"&gt;RSpec Style Guide&lt;/a&gt; taught me some conventions. I wrote test suites large enough to leverage features like &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/example-groups/shared-examples"&gt;shared examples&lt;/a&gt; and &lt;a href="https://relishapp.com/rspec/rspec-expectations/docs/custom-matchers"&gt;custom matchers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nowadays, I don't get why to use RSpec in the first place. Is it because the DSL reads vaguely like English? Is it because the output is human readable? Is it because, in the &lt;a href="https://stackoverflow.com/a/12480737"&gt;words of one of its developers&lt;/a&gt;, "it reifies so many testing concepts into first class objects"?&lt;/p&gt;

&lt;p&gt;Those are the things RSpec says on the tin, anyway. I don't fully get why they're virtues. And to the extent that they are, I don't see how RSpec delivers &lt;em&gt;well&lt;/em&gt; on any of them.&lt;/p&gt;

&lt;h1&gt;
  
  
  The DSL
&lt;/h1&gt;

&lt;p&gt;The Ruby community at large is enamored with &lt;a href="https://en.wikipedia.org/wiki/Domain-specific_language"&gt;DSLs&lt;/a&gt;, be it &lt;a href="http://sinatrarb.com/"&gt;Sinatra&lt;/a&gt;, &lt;a href="https://github.com/javan/whenever"&gt;Whenever&lt;/a&gt;, &lt;a href="https://github.com/ruby/rake"&gt;Rake&lt;/a&gt;, &lt;a href="https://docs.chef.io/recipes/"&gt;Chef&lt;/a&gt;, or things that probably &lt;a href="https://www.infoq.com/news/2007/06/dsl-or-not/"&gt;don't even count&lt;/a&gt;. One of the most common criticisms of DSLs is that they're &lt;a href="https://en.wikipedia.org/wiki/Magic_%28programming%29"&gt;"magic"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But I think the problem with RSpec is exactly the opposite: &lt;a href="https://www.youtube.com/watch?v=Libc0-0TRg4"&gt;it's not actually magic&lt;/a&gt;. If it were magic, I would be able to read it and &lt;em&gt;not&lt;/em&gt; know what it's doing underneath. But in order to make sense of any of it, I &lt;em&gt;have&lt;/em&gt; to know how it's implemented.&lt;/p&gt;

&lt;p&gt;Take the following example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;described_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#header'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/[[:punct:]]/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when the title is over 50 characters long'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paragraph_by_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;number: &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'gets truncated'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;end_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'keeps words intact'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;match_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pretty standard RSpec code, right? It's not even doing anything fancy. Yet already I have to grok several things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RSpec.describe&lt;/code&gt; is used at the top level to &lt;a href="https://relishapp.com/rspec/rspec-core/docs/configuration/global-namespace-dsl"&gt;avoid namespace pollution&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Inside the block, you &lt;a href="https://github.com/rspec/rspec-core/blob/9f55fafe62ea367a4801ebed2ca19cf6dfdceb39/lib/rspec/core/example_group.rb#L254-L256"&gt;have to&lt;/a&gt; use just &lt;code&gt;describe&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/example-groups"&gt;Example groups&lt;/a&gt; are essentially syntactic sugar for defining classes.&lt;/li&gt;
&lt;li&gt;Knowing this, I remember you can define &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/helper-methods/arbitrary-helper-methods"&gt;arbitrary helper methods&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Similarly, I think about how &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/subject/explicit-subject"&gt;&lt;code&gt;subject&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/helper-methods/let-and-let"&gt;&lt;code&gt;let&lt;/code&gt;&lt;/a&gt; translate down into memoized methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then there are the matchers... When I first started learning RSpec, I always got the syntax wrong. Where do the dots go? the spaces? the underscores? the parentheses? In order to keep any of it straight, I had to know the actual implementation.&lt;/p&gt;

&lt;p&gt;I still can't make sense of &lt;code&gt;expect(header.length).to be &amp;lt;= 50&lt;/code&gt; without the extra mental work of parsing it in my head. I don't see the English, I see Ruby, and my brain has to be the interpreter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; expect(header.length).to(be.&amp;lt;=(50))&lt;/span&gt;

&lt;span class="n"&gt;expectation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; RSpec::Expectations::ExpectationTarget.new(header.length)&lt;/span&gt;

&lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; RSpec::Matchers::BuiltIn::Be.new&lt;/span&gt;

&lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;&amp;lt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; RSpec::Matchers::BuiltIn::BeComparedTo.new(50, :&amp;lt;=)&lt;/span&gt;

&lt;span class="n"&gt;expectation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I can't help but see all the guts. And as used to RSpec as I am, it still trips me up. Or at best, it slows me down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I know &lt;code&gt;end_with&lt;/code&gt; and &lt;code&gt;match_array&lt;/code&gt; by heart as some of the many &lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/built-in-matchers"&gt;built-in matchers&lt;/a&gt;. But the list is long; I still find myself having to look it up.&lt;/li&gt;
&lt;li&gt;Some matchers have to evaluate an expression multiple times, so you need to wrap things in blocks. Even more punctuation to get in the way of your "English-y" syntax, like &lt;code&gt;expect { counter.increment }.to change { counter.value }.from(0).to(1)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;There are infinitely many &lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/built-in-matchers/predicate-matchers"&gt;predicate matchers&lt;/a&gt;, since they use &lt;a href="https://ruby-doc.org/core-2.7.1/BasicObject.html#method-i-method_missing"&gt;&lt;code&gt;method_missing&lt;/code&gt;&lt;/a&gt; to delegate to the expectation target. Imagine the frustration of junior me, unable to find any documentation on some weird-looking matcher I saw in the wild, only to find it was calling a method dynamically.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;be_xxx&lt;/code&gt; wasn't enough, there could be &lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/custom-matchers/define-a-custom-matcher"&gt;custom matchers&lt;/a&gt;, &lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/composing-matchers"&gt;noun-phrase aliases&lt;/a&gt;, or &lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/define-negated-matcher"&gt;negated matchers&lt;/a&gt; floating around the namespace. I've wasted a not-insignificant amount of time hunting down the names &amp;amp; implementations of matchers littered around the test suite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So why even bother with this language in the first place? I still have to understand it in terms of the Ruby it seeks to abstract. This isn't magic. It's a &lt;a href="https://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky abstraction&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Documentation
&lt;/h1&gt;

&lt;p&gt;I guess even if I don't find the code readable, the output is pretty legible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array
  behaves like a collection
    initialized with 3 items
      says it has three items
    #include?
      with an item that is in the collection
        returns true
      with an item that is not in the collection
        returns false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Is it really useful, though?&lt;/p&gt;

&lt;p&gt;To start, it's barely proper English (even ignoring flagrant misuse of RSpec's syntax, like &lt;a href="https://github.com/betterspecs/betterspecs/issues/2#issuecomment-9171781"&gt;&lt;code&gt;it "uniqueness of title"&lt;/code&gt;&lt;/a&gt;). If you were asked about an array's behavior, would you say "Array behaves like a collection initialized with 3 items says it has three items" out loud? The indentation is what implies any complete sentence structure: "Array behaves like a collection, &lt;em&gt;so when it's&lt;/em&gt; initialized with 3 items, &lt;em&gt;it&lt;/em&gt; says it has three items".&lt;/p&gt;

&lt;p&gt;The output's structure, of course, comes from the nested &lt;code&gt;context&lt;/code&gt; blocks + implied &lt;code&gt;it&lt;/code&gt;s when you read the code. But I know that when I'm looking for a failed example, I'm rarely able to trace the convoluted sentence back through some deep nesting of contexts. I'm just going to rely on the line numbers reported at the end of the run. For that matter, I'm just going to be using the &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/command-line/format-option#progress-bar-format-%28default%29"&gt;progress bar format&lt;/a&gt; anyway. 🤷&lt;/p&gt;

&lt;p&gt;I hear rumors of these &lt;a href="https://en.wikipedia.org/wiki/Behavior-driven_development"&gt;BDD&lt;/a&gt; teams where specs are shared between technical &amp;amp; nontechnical folk alike, forming an executable set of human-readable requirements that everyone can agree on. It sounds nice in theory. I've never worked on such a team, so I'm speaking out of my depth. But I can't help noticing that RSpec is still used wholesale for everything from acceptance tests down to unit tests. It's even considered &lt;em&gt;good style&lt;/em&gt; to &lt;a href="https://rspec.rubystyle.guide/#keep-example-descriptions-short"&gt;keep example descriptions short&lt;/a&gt; and &lt;a href="https://rspec.rubystyle.guide/#describe-the-methods"&gt;use class/method names&lt;/a&gt;. Short descriptions are hardly conducive to thorough documentation. And should your nontechnical team &lt;em&gt;really&lt;/em&gt; be caring about the code on the classes-and-methods level? I highly doubt it.&lt;/p&gt;

&lt;p&gt;If you're going to do something, do it right. At least with &lt;a href="https://cucumber.io/"&gt;Cucumber&lt;/a&gt; you could write honest-to-god executable documentation in complete sentences and paragraphs. Even &lt;a href="https://relishapp.com/rspec/"&gt;RSpec's docs&lt;/a&gt; are generated this way. Again, I've never actually been on a team that &lt;em&gt;does&lt;/em&gt; this; but RSpec strikes me as a half-measure by comparison.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Magic
&lt;/h1&gt;

&lt;p&gt;So if programmers are the only ones who truly care about the tests at the level they're being written with RSpec, why not use something simpler? Well, maybe RSpec is just that gosh dang &lt;em&gt;powerful&lt;/em&gt;. I mean, the library certainly does a whole lot of stuff.&lt;/p&gt;

&lt;p&gt;But then there's &lt;a href="https://github.com/seattlerb/minitest"&gt;Minitest&lt;/a&gt;, the standard foil to RSpec. It shows how the same testing can still be done in plain old Ruby. RSpec's general syntax can readily be transliterated to classes &amp;amp; methods. That way, you don't have to learn a new language to write your tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Minitest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;article&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;title&lt;/span&gt;
    &lt;span class="vi"&gt;@title&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Fake&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;header&lt;/span&gt;
      &lt;span class="vi"&gt;@header&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/[[:punct:]]/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WhenTheTitleIsOver50CharacterLong&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;title&lt;/span&gt;
        &lt;span class="vi"&gt;@title&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paragraph_by_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;number: &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_gets_truncated&lt;/span&gt;
        &lt;span class="c1"&gt;# ...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_keeps_words_intact&lt;/span&gt;
        &lt;span class="c1"&gt;# ...&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Granted, the nesting is so awkward that you usually won't do this in Minitest. A more faithful translation would probably be flatter, with simpler method names. (This is an advantage, if you ask me.) Then, instead of using the expectations syntax, &lt;code&gt;Minitest::Test&lt;/code&gt; defines &lt;a href="http://docs.seattlerb.org/minitest/Minitest/Assertions.html"&gt;assertion methods&lt;/a&gt; that your class will inherit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Minitest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/[[:punct:]]/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_long_headers_get_truncated&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paragraph_by_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;number: &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;assert_operator&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:&amp;lt;=&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_header_keeps_words_intact&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Lorem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paragraph_by_chars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;number: &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;assert_includes&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;MSG&lt;/span&gt;&lt;span class="sh"&gt;
        &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; is in the header &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
        but not in the title &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;      MSG&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The "first class features" of matchers are generally "first class features" of Minitest—or indeed, the Ruby programming language.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/define-negated-matcher"&gt;Negation&lt;/a&gt; is done with the &lt;code&gt;refute_*&lt;/code&gt; counterparts to &lt;code&gt;assert_*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/customized-message"&gt;Customized messages&lt;/a&gt; can be given as the last argument to any Minitest assertion (except &lt;a href="http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_output"&gt;&lt;code&gt;assert_output&lt;/code&gt;&lt;/a&gt;), as demonstrated above.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/custom-matchers"&gt;Custom matchers&lt;/a&gt; simply become Ruby methods that delegate to the Minitest &lt;code&gt;assert_*&lt;/code&gt; primitives.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/compound-expectations"&gt;Compound expectations&lt;/a&gt; could just as well be separate assertions for conjunction (&lt;code&gt;assert one; assert two&lt;/code&gt;) or plain boolean operators for disjunction (&lt;code&gt;assert one || two, 'neither one nor two'&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, RSpec hooks that Minitest &lt;a href="https://github.com/seattlerb/minitest/issues/61"&gt;excludes&lt;/a&gt; can be accomplished with &lt;a href="https://github.com/seattlerb/minitest/#label-How+to+run+code+before+a+group+of+tests-3F"&gt;plain Ruby&lt;/a&gt;. Shared contexts &amp;amp; example groups can be arranged as &lt;a href="https://github.com/seattlerb/minitest/#label-How+to+share+code+across+test+classes-3F"&gt;modules&lt;/a&gt; and &lt;a href="http://wojtekmach.pl/blog/2012/07/17/liskov-principle-and-minitest/"&gt;classes&lt;/a&gt;. &lt;a href="https://relishapp.com/rspec/rspec-mocks/docs"&gt;RSpec mocks&lt;/a&gt; are more or less comparable to Minitest's &lt;a href="https://github.com/seattlerb/minitest#label-Mocks"&gt;equivalents&lt;/a&gt;. By and large, Minitest doesn't reinvent Ruby's wheel.&lt;/p&gt;

&lt;p&gt;Furthermore, almost just to prove that it's possible, Minitest implements essentially a &lt;a href="https://github.com/seattlerb/minitest#label-Specs"&gt;subset of RSpec's DSL&lt;/a&gt;. But no matter how small its implementation (a dubious virtue), I have the same distaste for it as I do for RSpec. Why bother in the first place?&lt;/p&gt;

&lt;p&gt;The one thing I'll give RSpec is that strings are easier to type out than coming up with &lt;code&gt;test_*&lt;/code&gt; method names. But you don't need a whole framework to achieve this. &lt;a href="https://guides.rubyonrails.org/testing.html#rails-meets-minitest"&gt;&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt;&lt;/a&gt; has a &lt;a href="https://github.com/rails/rails/blob/0e35c10659b4dba65d580b76df267be1eef0c5dd/activesupport/lib/active_support/testing/declarative.rb#L13-L24"&gt;lightweight &lt;code&gt;test&lt;/code&gt; method&lt;/a&gt; that's almost literally &lt;code&gt;def&lt;/code&gt; with a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;words_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'long headers get truncated'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'header keeps words intact'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;ArticleTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_methods&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_methods&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; [&lt;/span&gt;
&lt;span class="c1"&gt;#   :test_long_headers_get_truncated,&lt;/span&gt;
&lt;span class="c1"&gt;#   :test_header_keeps_words_intact,&lt;/span&gt;
&lt;span class="c1"&gt;#   :words_in,&lt;/span&gt;
&lt;span class="c1"&gt;# ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The times I &lt;em&gt;do&lt;/em&gt; need to group together tests, I admittedly miss having &lt;code&gt;context&lt;/code&gt;. For that, you could use something as beautifully small as &lt;a href="https://github.com/arenaflowers/minitest-context"&gt;minitest-context&lt;/a&gt; or as relatively big as &lt;a href="https://github.com/thoughtbot/shoulda-context"&gt;shoulda-context&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Going further, Minitest plugins fill in various gaps. &lt;a href="https://github.com/splattael/minitest-around"&gt;minitest-around&lt;/a&gt; gives you &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/hooks/around-hooks"&gt;&lt;code&gt;around&lt;/code&gt; hooks&lt;/a&gt;. &lt;a href="https://github.com/wojtekmach/minitest-metadata"&gt;minitest-metadata&lt;/a&gt; gives you &lt;a href="https://relishapp.com/rspec/rspec-core/docs/metadata/user-defined-metadata"&gt;metadata&lt;/a&gt; (which I've personally never actually needed). &lt;a href="https://github.com/kern/minitest-reporters"&gt;minitest-reporters&lt;/a&gt; gives you nicer output akin to &lt;a href="https://relishapp.com/rspec/rspec-core/v/3-9/docs/command-line/format-option"&gt;RSpec's formatters&lt;/a&gt;. And so on.&lt;/p&gt;

&lt;p&gt;I agree that all this patchwork isn't a great experience out of the box. Even basic CLI tooling is hand-rolled. So you aren't really comparing RSpec to &lt;em&gt;just&lt;/em&gt; Minitest; you're comparing it to Minitest and an ecosystem of plugins &amp;amp; boilerplate. Be that as it may, RSpec's killer features are met by this ecosystem—plus a dose of basic Ruby programming skills. Perhaps it's less cohesive, but I don't think you're throwing the baby out with the bathwater by ditching RSpec.&lt;/p&gt;

&lt;h1&gt;
  
  
  Magic Done Wrong
&lt;/h1&gt;

&lt;p&gt;Complex problems often demand complex solutions. But RSpec is the sort of complexity that &lt;em&gt;increases&lt;/em&gt; cognitive load. If you're going to lean into the so-called magic, why would you use it to make your job harder?&lt;/p&gt;

&lt;p&gt;I believe it's possible to use magic for good. To do something complex in order to make something simple.&lt;/p&gt;

&lt;p&gt;I don't even think Minitest accomplishes this. If you were to ask around, it'd certainly be perceived as less "magic", and it &lt;em&gt;is&lt;/em&gt; less complicated than a spec-style DSL. But you still carry a lot of mental weight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;assert_same&lt;/code&gt; uses &lt;code&gt;equal?&lt;/code&gt;, but &lt;code&gt;assert_equal&lt;/code&gt; uses &lt;code&gt;==&lt;/code&gt;, and &lt;code&gt;assert_nil&lt;/code&gt; has to be used explicitly &lt;a href="https://github.com/seattlerb/minitest/commit/922bc9151a622cb3ef0b9f170aa09c3bb72c7eb8"&gt;come Minitest 6&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Remember to use the order &lt;code&gt;assert_equal expect, actual&lt;/code&gt; for nicer failure messages.&lt;/li&gt;
&lt;li&gt;And it's &lt;code&gt;assert_includes collection, object&lt;/code&gt;, not &lt;code&gt;assert_includes object, collection&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Oh yeah, &lt;code&gt;assert_predicate&lt;/code&gt; exists. Maybe I should use that for better failure messages instead of &lt;code&gt;assert object.predicate?&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Does anyone else always have to think about what &lt;code&gt;assert_in_epsilon&lt;/code&gt; means, or is it just me?&lt;/li&gt;
&lt;li&gt;I've made the mistake before of thinking &lt;code&gt;assert_raises "message"&lt;/code&gt; does an &lt;code&gt;assert_equal "message", e.message&lt;/code&gt;, but it's just the Minitest failure message. Oops. 😅&lt;/li&gt;
&lt;li&gt;Stubbing is (purposefully) painful. I definitely avoid it—I've been burned enough times by bad stubs. But sometimes I legitimately need it, and making the process more restrictive doesn't help.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Much like I have to open up RSpec's docs to figure out how to use it, I regularly crack open Minitest's source code to remember how a certain method works (usually the stubbing). Minitest is a breath of fresh air compared to RSpec, but I have to admit the mental drag is still there years after adopting it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Magic Done Right
&lt;/h1&gt;

&lt;p&gt;So what &lt;em&gt;does&lt;/em&gt; qualify as good magic?&lt;/p&gt;

&lt;p&gt;It wasn't until I used &lt;a href="https://docs.pytest.org/"&gt;pytest&lt;/a&gt; in a Python project that it clicked. Specifically, all of its &lt;a href="https://docs.pytest.org/en/latest/assert.html"&gt;assertions&lt;/a&gt; are written plainly as Python &lt;a href="https://docs.python.org/3/reference/simple_stmts.html#assert"&gt;&lt;code&gt;assert&lt;/code&gt; statements&lt;/a&gt;. (Although its &lt;a href="https://docs.pytest.org/en/latest/fixture.html"&gt;fixtures&lt;/a&gt; are pretty fuckin' cool, too.)&lt;/p&gt;

&lt;p&gt;We could do the same in Minitest, saying &lt;code&gt;assert a == b&lt;/code&gt; instead of &lt;code&gt;assert_equal a, b&lt;/code&gt;. But the whole problem is that we won't get useful output when something fails. The plain &lt;code&gt;assert&lt;/code&gt; is being passed in a boolean, so its failure would look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Minitest::Assertion: Expected false to be truthy.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;whereas the &lt;code&gt;assert_equal&lt;/code&gt; could say&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Minitest::Assertion: Expected: 1
  Actual: 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So what pytest does is &lt;a href="http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html"&gt;rewrite the whole damn program&lt;/a&gt; behind the scenes to decorate every &lt;code&gt;assert&lt;/code&gt; with rich diffs. You, the tester, needn't be any the wiser. If your test fails, you just get a useful error message. You don't have to remember the difference between &lt;code&gt;assert_same&lt;/code&gt; and &lt;code&gt;assert_equal&lt;/code&gt;, or the order of arguments to &lt;code&gt;assert_includes&lt;/code&gt;. Its complexity is entirely hidden from you, provides concrete value, and it doesn't ask anything special in return. How's &lt;em&gt;that&lt;/em&gt; for magic?&lt;/p&gt;

&lt;p&gt;The closest Ruby version I've seen is &lt;a href="https://github.com/ruby/power_assert"&gt;Power Assert&lt;/a&gt;, which combs over a Ruby expression so you get thorough output like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failure:
   assert { 3.times.to_a.include?(3) }
              |     |    |
              |     |    false
              |     [0, 1, 2]
              #&amp;lt;Enumerator: 3:times&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I haven't had a chance to convert my existing Minitest suites over to see how well this works in practice. The project comes with some strong &lt;a href="https://github.com/ruby/power_assert#label-Known+Limitations"&gt;caveats&lt;/a&gt; that make me hesitant to try, though.&lt;/p&gt;

&lt;p&gt;Programming language &amp;amp; library designers should really be taking heed. All these &lt;a href="https://www.google.com/search?q=RSpec%20vs%20Minitest"&gt;RSpec vs Minitest&lt;/a&gt; debates are tired—they've &lt;a href="http://www.rubyinside.com/dhh-offended-by-rspec-debate-4610.html"&gt;been raging&lt;/a&gt; &lt;em&gt;years&lt;/em&gt; longer than I've even been programming Ruby. Power Assert &amp;amp; pytest have to go through great lengths to get something resembling good output without burdening the programmer. A good language design can make this much easier.&lt;/p&gt;

&lt;p&gt;One example is &lt;a href="https://elixir-lang.org/"&gt;Elixir&lt;/a&gt;. The &lt;a href="https://hexdocs.pm/ex_unit/ExUnit.html"&gt;ExUnit&lt;/a&gt; framework is simple but powerful, and its implementation is relatively straightforward due to Elixir's &lt;a href="https://elixir-lang.org/getting-started/meta/macros.html"&gt;macros&lt;/a&gt;. &lt;a href="https://hexdocs.pm/ex_unit/ExUnit.Case.html"&gt;&lt;code&gt;ExUnit.Case&lt;/code&gt;&lt;/a&gt; gives you the &lt;code&gt;describe&lt;/code&gt;/&lt;code&gt;test&lt;/code&gt; pair—a light dusting of DSL, like &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; but with more functionality. Then &lt;a href="https://hexdocs.pm/ex_unit/ExUnit.Assertions.html"&gt;&lt;code&gt;ExUnit.Assertions&lt;/code&gt;&lt;/a&gt; defines the &lt;code&gt;assert&lt;/code&gt; macro, which introspects its arguments at compile-time so you get better failure reporting. The fact that it can even do this is a testament to the language's design.&lt;/p&gt;

&lt;p&gt;It's still arguably complex. After all, someone had to implement everything that goes into supporting macros. Maybe programming is just &lt;a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down"&gt;magic all the way down&lt;/a&gt;. I only ask that &lt;em&gt;your&lt;/em&gt; magic doesn't shift the burden onto &lt;em&gt;me&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>testing</category>
      <category>design</category>
    </item>
    <item>
      <title>Classes &amp; Inheritance: Unweaving the Rainbow</title>
      <dc:creator>Alex Vondrak</dc:creator>
      <pubDate>Sat, 02 May 2020 22:44:03 +0000</pubDate>
      <link>https://dev.to/ajvondrak/classes-inheritance-unweaving-the-rainbow-2h0</link>
      <guid>https://dev.to/ajvondrak/classes-inheritance-unweaving-the-rainbow-2h0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Philosophy will clip an Angel's wings,&lt;br&gt;
Conquer all mysteries by rule and line,&lt;br&gt;
Empty the haunted air, and gnomed mine–&lt;br&gt;
Unweave a rainbow, as it erewhile made&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— &lt;a href="https://en.wikipedia.org/wiki/Lamia_%28poem%29" rel="noopener noreferrer"&gt;Lamia (John Keats)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm an academic at heart. I don't think that's a bad thing for a software engineer, but it gets kind of lonely.&lt;/p&gt;

&lt;p&gt;I notice it when phone screening. As an interviewer, I might ask something about object-oriented design. "Can you tell me how inheritance works?" Typically, I'll hear answers about code reuse, subclasses delegating to superclasses, quirks of specific programming languages, &lt;a href="https://en.wikipedia.org/wiki/Composition_over_inheritance" rel="noopener noreferrer"&gt;composition over inheritance&lt;/a&gt;, etc.&lt;/p&gt;

&lt;p&gt;These answers aren't wrong. But I die a little bit inside when it's all I hear. The academic part of me yearns for more depth. In my experience, most programmers can describe what inheritance &lt;em&gt;does&lt;/em&gt;, but not what it really &lt;em&gt;is&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Nineteenth-century poet John Keats purportedly &lt;a href="https://www.scientificamerican.com/article/unweaving-the-heart/" rel="noopener noreferrer"&gt;quipped&lt;/a&gt; that scientist Issac Newton "destroyed the poetry of the rainbow by reducing it to a prism". And sure, learning how a magic trick works might rob you of a certain joy. But I think the broader programming community would do well not to treat language constructs as magic tricks—as some syntactic incantation you just learn by rote. Not only can there be a joy in knowing, there are practical benefits to digging deeper. You'll take the lessons learned with you on all your future programming endeavors.&lt;/p&gt;

&lt;p&gt;So instead of waiting to hear it from an interviewee like some secret academic handshake, I want to educate. Let's destroy a rainbow together.&lt;/p&gt;

&lt;h1&gt;
  
  
  Classes
&lt;/h1&gt;

&lt;p&gt;You can't take two steps in Programming Town without &lt;a href="https://en.wikipedia.org/wiki/List_of_object-oriented_programming_languages" rel="noopener noreferrer"&gt;tripping over object orientation&lt;/a&gt;: C++, C#, Java, Kotlin, Perl, PHP, Python, Ruby, Scala, Swift, ... The syntax, nomenclature, and other gory details vary, but I'll assume you're familiar with the ideas in general. The gist being: you define your own classes of objects (the nouns of your program) that then contain constructors, variables, methods, etc. An individual object constructed by the class is called an instance, and the instance's methods are what do the main work of your program.&lt;/p&gt;

&lt;p&gt;While this post isn't really specific to any language, Ruby is a quintessential example of an object-oriented language, so I'll go ahead and use it for the code throughout. Let's define a simple class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rainbow&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:colors&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;Rainbow&lt;/code&gt; class has an initializer that takes in an array of colors and saves them in an instance variable. That variable is accessible by a corresponding getter method.&lt;/p&gt;

&lt;p&gt;To represent the individual colors in a rainbow, we define yet another class.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:alpha&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transparent?&lt;/span&gt;
    &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;translucent?&lt;/span&gt;
    &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;opaque?&lt;/span&gt;
    &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To make things interesting, we define the &lt;code&gt;Color&lt;/code&gt; class with an &lt;a href="https://en.wikipedia.org/wiki/Alpha_compositing" rel="noopener noreferrer"&gt;alpha channel&lt;/a&gt;—a percentage used to represent the opacity of a pixel when we render the color. Not only do we have a getter method for this alpha, we define several methods implementing logic involving its value.&lt;/p&gt;

&lt;p&gt;So far, so good. But we can't really construct a meaningful color yet. The problem is that there are several different ways to represent colors in a computer. That's where we'll use inheritance.&lt;/p&gt;
&lt;h1&gt;
  
  
  Inheritance
&lt;/h1&gt;

&lt;p&gt;The term "inheritance" is meant to evoke the concept of genetics. When children inherit traits from their parents, the parents don't lose those traits. Children may also have additional traits beyond the ones they've inherited.&lt;/p&gt;

&lt;p&gt;So, too, can we propagate traits between classes in a program. We define subclasses that inherit from superclasses. Inherit what, though? Generally, the methods and variables. Features like access modifiers (e.g., &lt;code&gt;private&lt;/code&gt; in Ruby) change which exact things get inherited, but the details aren't relevant to this post.&lt;/p&gt;

&lt;p&gt;Defining a subclass is largely the same as defining a standard class.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RGB&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Color&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:red&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:green&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:blue&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;
    &lt;span class="vi"&gt;@green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;
    &lt;span class="vi"&gt;@blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;
    &lt;span class="vi"&gt;@alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;RGB&lt;/code&gt; class is a subclass of &lt;code&gt;Color&lt;/code&gt; (and &lt;code&gt;Color&lt;/code&gt; is a superclass of &lt;code&gt;RGB&lt;/code&gt;). It represents a point in the &lt;a href="https://en.wikipedia.org/wiki/RGB_color_model" rel="noopener noreferrer"&gt;RGB color model&lt;/a&gt;: three values between 0 and 255 for the redness, greenness, and blueness of a pixel. Combined, these 3 primary colors add together to form a secondary color, ranging from black (0, 0, 0) to white (255, 255, 255).&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;RGB&lt;/code&gt;, we can finally instantiate meaningful colors.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;165&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;yellow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;indigo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;violet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;238&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;238&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Due to inheritance, every instance of &lt;code&gt;RGB&lt;/code&gt; responds to methods defined by &lt;code&gt;Color&lt;/code&gt;. This is how inheritance promotes code reuse: basically by "copying" code (if only virtually) so you don't have to.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;dimmed_red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dimmed_red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transparent?&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; false&lt;/span&gt;
&lt;span class="n"&gt;dimmed_red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translucent?&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; true&lt;/span&gt;
&lt;span class="n"&gt;dimmed_red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;opaque?&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can keep defining more subclasses, and even subclasses of those subclasses.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cylindrical&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Color&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:hue&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:saturation&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:component&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@hue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hue&lt;/span&gt;
    &lt;span class="vi"&gt;@saturation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;saturation&lt;/span&gt;
    &lt;span class="vi"&gt;@component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;
    &lt;span class="vi"&gt;@alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HSL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;lightness&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HSV&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;Cylindrical&lt;/code&gt; class is another subclass of &lt;code&gt;Color&lt;/code&gt;. It is a generalized representation of a point in a &lt;a href="https://en.wikipedia.org/wiki/HSL_and_HSV" rel="noopener noreferrer"&gt;cylindrical-coordinate color model&lt;/a&gt;. Such a color has three pieces: a hue represented as an angle between 0-360°, a saturation between 0 and 1, and a third component whose name &amp;amp; possible values vary by the specific model.&lt;/p&gt;

&lt;p&gt;Some examples of such models are given by the &lt;code&gt;Cylindrical&lt;/code&gt; subclasses &lt;code&gt;HSL&lt;/code&gt; (hue, saturation, lightness) and &lt;code&gt;HSV&lt;/code&gt; (hue, saturation, value). They inherit the getters for hue and saturation, as well as the getter for the generic third component. But &lt;code&gt;component&lt;/code&gt; isn't very descriptive, so each subclass &lt;a href="https://www.rubyguides.com/2018/11/ruby-alias-keyword/" rel="noopener noreferrer"&gt;aliases&lt;/a&gt; it, creating a getter with a more specific name.&lt;/p&gt;

&lt;p&gt;We can represent a color like &lt;a href="https://colorpicker.me/#228b22" rel="noopener noreferrer"&gt;forest green&lt;/a&gt; using equivalent instances of each class.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rgb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;139&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;hsl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.34&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;hsv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Even more &lt;a href="https://en.wikipedia.org/wiki/Color_model" rel="noopener noreferrer"&gt;color models&lt;/a&gt; exist, but digging into all of them isn't the sort of rainbow wrecking I had in mind. 😉&lt;/p&gt;
&lt;h1&gt;
  
  
  Classes unwoven
&lt;/h1&gt;

&lt;p&gt;My point is that, as programmers, we know how to define classes mechanistically in our language of choice. In fact, there's quite a lot we know about how to use them: writing initializers, instantiating objects, making getter methods over instance variables, defining methods that use those values. You probably know even more than what I'm summarizing here. The &lt;code&gt;Rainbow&lt;/code&gt; class and all the different &lt;code&gt;Color&lt;/code&gt; classes are pretty humdrum in most languages.&lt;/p&gt;

&lt;p&gt;But if we know classes merely by how we use them, then a class is whatever our language makes of it—all the syntax, semantics, patterns, and boilerplate. In Python, you use &lt;a href="https://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html" rel="noopener noreferrer"&gt;explicit &lt;code&gt;self&lt;/code&gt;s&lt;/a&gt;. In Java, you have to be mindful about the &lt;a href="https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html" rel="noopener noreferrer"&gt;difference between &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;Integer&lt;/code&gt;&lt;/a&gt;. In C++, &lt;a href="https://blog.tartanllama.xyz/initialization-is-bonkers/" rel="noopener noreferrer"&gt;initialization is bonkers&lt;/a&gt;. In Kotlin, you need to declare a class &lt;a href="https://kotlinlang.org/docs/reference/classes.html#inheritance" rel="noopener noreferrer"&gt;&lt;code&gt;open&lt;/code&gt;&lt;/a&gt; to allow subclasses. In Go, &lt;a href="https://golang.org/ref/spec#Method_declarations" rel="noopener noreferrer"&gt;methods&lt;/a&gt; are declared separately from &lt;a href="https://golang.org/ref/spec#Struct_types" rel="noopener noreferrer"&gt;structs&lt;/a&gt;, which only have a limited form of inheritance via &lt;a href="https://golang.org/doc/effective_go.html#embedding" rel="noopener noreferrer"&gt;embedding&lt;/a&gt;. And so on.&lt;/p&gt;

&lt;p&gt;Really, I want to highlight the difference between the &lt;a href="https://en.wikipedia.org/wiki/Imperative_programming" rel="noopener noreferrer"&gt;imperative&lt;/a&gt; and the &lt;a href="https://en.wikipedia.org/wiki/Declarative_programming" rel="noopener noreferrer"&gt;declarative&lt;/a&gt;—doing vs being. Programming languages implement classes as a feature that &lt;em&gt;does&lt;/em&gt; something for you in some specific way. But they only bother because of what a class &lt;em&gt;is&lt;/em&gt; in the abstract, which doesn't change between languages. So what is a class, really?&lt;/p&gt;

&lt;p&gt;My answer may seem underwhelming: a class can be understood as a &lt;a href="https://en.wikipedia.org/wiki/Set_%28mathematics%29" rel="noopener noreferrer"&gt;set&lt;/a&gt; of all its possible instances.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F600924%2F80325991-346fdf80-87ec-11ea-9897-9f366491aeda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F600924%2F80325991-346fdf80-87ec-11ea-9897-9f366491aeda.png" alt="A Venn diagram of the RGB set with elements corresponding to the red, yellow, green, blue, indigo, and violet instances we created previously."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For illustration, the &lt;code&gt;RGB&lt;/code&gt; class contains not just the &lt;a href="https://en.wikipedia.org/wiki/ROYGBIV" rel="noopener noreferrer"&gt;ROYGBIV&lt;/a&gt; instances we created before, but also every other valid instance. Using just the red/blue/green components, that's 256^3 = 16,777,216 possible combinations. Along with the alpha channel, which is a real number between 0 and 1, there is an &lt;a href="https://en.wikipedia.org/wiki/Uncountable_set" rel="noopener noreferrer"&gt;uncountably infinite&lt;/a&gt; number of elements in this set (although computers can only represent a finite range of floating point numbers in practice).&lt;/p&gt;

&lt;p&gt;The exciting part of pulling on this thread is that it leads us to the very &lt;a href="https://en.wikipedia.org/wiki/Foundations_of_mathematics" rel="noopener noreferrer"&gt;foundations of mathematics&lt;/a&gt;. Virtually all mathematical theorems can be formulated as theorems of &lt;a href="https://en.wikipedia.org/wiki/Set_theory" rel="noopener noreferrer"&gt;set theory&lt;/a&gt;. Thanks to over a century of work by mathematicians, we know a wide variety of abstract properties about sets. Things that are true not because of a specific implementation, but because they &lt;em&gt;must&lt;/em&gt; be true.&lt;/p&gt;
&lt;h1&gt;
  
  
  Inheritance unwoven
&lt;/h1&gt;

&lt;p&gt;One such truth: if classes are sets, then subclasses are &lt;a href="https://en.wikipedia.org/wiki/Subset" rel="noopener noreferrer"&gt;subsets&lt;/a&gt;. One set is a subset of another if every element of the first is also an element of the second. In mathematical notation, we write 

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x∈Sx \in S&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 if element 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;xx&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is a member of set 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SS&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and we write 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;S⊆TS \subseteq T&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 if set 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SS&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is a subset of set 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;TT&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. So 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;S⊆TS \subseteq T&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 if for every 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x∈Sx \in S&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 it's the case that 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x∈Tx \in T&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 as well.&lt;/p&gt;

&lt;p&gt;When an object-oriented language says "everything is an object", one thing they often mean is that every class inherits from some base &lt;code&gt;Object&lt;/code&gt; superclass. Considering all the classes we've defined so far, we can draw the Venn diagram showing how these sets nest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F600924%2F80559926-fad9d880-8993-11ea-83c3-047fb442d51a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F600924%2F80559926-fad9d880-8993-11ea-83c3-047fb442d51a.png" alt="A Venn diagram showing the nesting of the classes we've defined in this post."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Elements of the &lt;code&gt;Object&lt;/code&gt; set include the elements of the &lt;code&gt;Rainbow&lt;/code&gt; and &lt;code&gt;Color&lt;/code&gt; sets. Inside of &lt;code&gt;Color&lt;/code&gt; are all its possible instances; some of those instances are also in the &lt;code&gt;RGB&lt;/code&gt; and &lt;code&gt;Cylindrical&lt;/code&gt; sets. Inside &lt;code&gt;Cylindrical&lt;/code&gt; are &lt;code&gt;HSL&lt;/code&gt; and &lt;code&gt;HSV&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In mathematical notation:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Rainbow⊆ObjectColor⊆ObjectRGB⊆ColorCylindrical⊆ColorHSL⊆CylindricalHSV⊆Cylindrical
  \begin{aligned}
    \texttt{Rainbow} &amp;amp;\subseteq \texttt{Object} \\
    \texttt{Color} &amp;amp;\subseteq \texttt{Object} \\
    \texttt{RGB} &amp;amp;\subseteq \texttt{Color} \\
    \texttt{Cylindrical} &amp;amp;\subseteq \texttt{Color} \\
    \texttt{HSL} &amp;amp;\subseteq \texttt{Cylindrical} \\
    \texttt{HSV} &amp;amp;\subseteq \texttt{Cylindrical} \\
  \end{aligned}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mtable"&gt;&lt;span class="col-align-r"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Rainbow&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Color&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;RGB&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Cylindrical&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;HSL&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;HSV&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="col-align-l"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Object&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Object&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Color&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Color&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Cylindrical&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Cylindrical&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;Now consider an instance of the &lt;code&gt;HSL&lt;/code&gt; class for the color &lt;a href="https://colorpicker.me/#FFC0CB" rel="noopener noreferrer"&gt;pink&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;pink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can say that &lt;code&gt;pink&lt;/code&gt; is in the &lt;code&gt;HSL&lt;/code&gt; set. By the definition of subsets, this also means the following.&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;pink∈HSLpink∈Cylindricalpink∈Colorpink∈Objectpink∉Rainbowpink∉RGBpink∉HSV
  \begin{aligned}
    \texttt{pink} &amp;amp;\in \texttt{HSL} \\
    \texttt{pink} &amp;amp;\in \texttt{Cylindrical} \\
    \texttt{pink} &amp;amp;\in \texttt{Color} \\
    \texttt{pink} &amp;amp;\in \texttt{Object} \\
    \texttt{pink} &amp;amp;\notin \texttt{Rainbow} \\
    \texttt{pink} &amp;amp;\notin \texttt{RGB} \\
    \texttt{pink} &amp;amp;\notin \texttt{HSV} \\
  \end{aligned}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mtable"&gt;&lt;span class="col-align-r"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;pink&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="col-align-l"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;HSL&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Cylindrical&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Color&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Object&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&lt;span class="mord"&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;/span&gt;&lt;span class="mord vbox"&gt;&lt;span class="thinbox"&gt;&lt;span class="llap"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="inner"&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;/&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="fix"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Rainbow&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&lt;span class="mord"&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;/span&gt;&lt;span class="mord vbox"&gt;&lt;span class="thinbox"&gt;&lt;span class="llap"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="inner"&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;/&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="fix"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;RGB&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&lt;span class="mord"&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;/span&gt;&lt;span class="mord vbox"&gt;&lt;span class="thinbox"&gt;&lt;span class="llap"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="inner"&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;/&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="fix"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;HSV&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;Given these set memberships, and the fact that classes are sets of instances, this means &lt;code&gt;pink&lt;/code&gt; is &lt;em&gt;an instance&lt;/em&gt; of &lt;code&gt;HSL&lt;/code&gt;, &lt;code&gt;Cylindrical&lt;/code&gt;, and &lt;code&gt;Color&lt;/code&gt; simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methods
&lt;/h2&gt;

&lt;p&gt;These interactions become important when we consider what methods mean through the lens of subsets. Say we add a method to the &lt;code&gt;RGB&lt;/code&gt; class that returns the corresponding &lt;a href="https://en.wikipedia.org/wiki/Complementary_colors" rel="noopener noreferrer"&gt;complementary color&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RGB&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Color&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complement&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We think of this method as taking no arguments. After all, we pass the message to an instance of &lt;code&gt;RGB&lt;/code&gt; with no parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cyan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complement&lt;/span&gt; &lt;span class="c1"&gt;# RGB 0, 255, 255&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we try to call this method on some other class that does not define it, we get an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complement&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; NoMethodError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine for a moment that the language didn't have this concept of methods, though: just top-level functions. The spelling might be different, but you could achieve the same effect. In our example, if an instance is in the &lt;code&gt;RGB&lt;/code&gt; class, we can use the logic as defined; otherwise raise an exception.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A method is then a function whose first argument is tacitly an instance of the class on which it's defined. This is made more evident in languages like Python, where methods are always defined with an explicit first argument named &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But conceptually, using just one function gets more complicated. Methods with the same name might be defined for several different classes. So if we only have this one function, it needs to have a branch for each class on which it's defined.&lt;/p&gt;

&lt;p&gt;Say we added an equivalent method for &lt;code&gt;Cylindrical&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cylindrical&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Color&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complement&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;hue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top-level function would need an extra clause for the &lt;code&gt;Cylindrical&lt;/code&gt; case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this implementation still &lt;strong&gt;incorrect&lt;/strong&gt;, thanks to inheritance. In the above code, we're checking set &lt;em&gt;equality&lt;/em&gt;. As was pointed out in the previous section, an instance can be a member of multiple sets at once, even if the sets aren't equal. For example, even though an instance of &lt;code&gt;HSL&lt;/code&gt; is also an instance of &lt;code&gt;Cylindrical&lt;/code&gt;, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;HSL≠Cylindrical\texttt{HSL} \ne \texttt{Cylindrical}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;HSL&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&lt;span class="mrel"&gt;&lt;span class="mord vbox"&gt;&lt;span class="thinbox"&gt;&lt;span class="rlap"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="inner"&gt;&lt;span class="mord"&gt;&lt;span class="mrel"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="fix"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Cylindrical&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. Yet the actual method is equally valid to call on subclasses of &lt;code&gt;Cylindrical&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cyan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complement&lt;/span&gt; &lt;span class="c1"&gt;# HSL 180, 1.0, 0.5&lt;/span&gt;

&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;purple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complement&lt;/span&gt; &lt;span class="c1"&gt;# HSV 300, 1.0, 0.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We know classes are sets. So instead of checking if the object's class is &lt;em&gt;equal&lt;/em&gt; to the definer's class, what if we perform a subset check, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;⊆\subseteq&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
? In Ruby, you can actually compare two classes this way using the &lt;code&gt;&amp;lt;=&lt;/code&gt; operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;RGB&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
    &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saturation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, this is a correct definition for our complementary color method. Languages don't necessarily implement methods this way, but that doesn't invalidate the conceptual understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method overriding
&lt;/h2&gt;

&lt;p&gt;Things get more complicated when we throw &lt;a href="https://en.wikipedia.org/wiki/Method_overriding" rel="noopener noreferrer"&gt;method overriding&lt;/a&gt; into the mix. This allows a subclass to redefine a method of their parent class. The subclass can usually delegate to the original superclass method with some form of &lt;code&gt;super&lt;/code&gt; keyword, but this feature complicates our discussion too much for the scope of this post. I encourage you to destroy that rainbow for yourself. 😉&lt;/p&gt;

&lt;p&gt;One common (though admittedly not particularly interesting) pattern in Ruby, which lacks explicit &lt;a href="https://en.wikipedia.org/wiki/Class_%28computer_programming%29#Abstract_and_concrete" rel="noopener noreferrer"&gt;abstract classes&lt;/a&gt;, is to define a method that raises &lt;code&gt;NotImplementedError&lt;/code&gt; on the superclass. Subclasses are expected to override the method, because otherwise they'll inherit the error-throwing version. This is desirable simply for the fact that &lt;code&gt;NotImplementedError&lt;/code&gt; is more informative than &lt;code&gt;NoMethodError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, we might expect to be able to convert cylindrical-coordinate colors into RGB points. However, the implementation details vary based on the specific cylindrical model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cylindrical&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HSL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSL to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HSV&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSV to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we subclass &lt;code&gt;Cylindrical&lt;/code&gt; without overriding the method, we're tipped off to the fact that we didn't implement something we should have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HSI&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
  &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;intensity&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;magenta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.67&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;magenta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_rgb&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; NotImplementedError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again, for the sake of illustration, let's think about how we would define this method as a function that compares subsets. This isn't how languages necessarily implement it, but it helps us understand how we might get the logic wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSL to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;HSV&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSV to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue is that our big conditional statement is sensitive to the order of the branches. In the &lt;strong&gt;incorrect&lt;/strong&gt; implementation above, the &lt;code&gt;Cylindrical&lt;/code&gt; subset check is done first. So say we pass an &lt;code&gt;HSL&lt;/code&gt; instance into this top-level function. Since 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;HSL⊆Cylindrical\texttt{HSL} \subseteq \texttt{Cylindrical}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;HSL&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Cylindrical&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, we'll immediately execute the branch that raises the &lt;code&gt;NotImplementedError&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;coral&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;to_rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coral&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; NotImplementedError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we need to order the branches correctly. How do we know we can even find such an ordering? Because of set theory!&lt;/p&gt;

&lt;p&gt;Firstly, a &lt;a href="https://en.wikipedia.org/wiki/Partially_ordered_set" rel="noopener noreferrer"&gt;partial order&lt;/a&gt; of a given set is a relationship between any two of its elements such that the following properties hold.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reflexivity&lt;/strong&gt;: every element is related to itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antisymmetry&lt;/strong&gt;: any two distinct elements related to each other in one direction cannot be related in the other direction. Said another way, the only way two elements can be related in both directions is if they're actually the same element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transitivity&lt;/strong&gt;: if one element is related to a second element and the second is related to a third, then the first is related to the third as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the set of integers is partially ordered by the "less than or equal to" relationship.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every integer 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;xx&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is less than or equal to itself: 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x≤xx \le x&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/li&gt;
&lt;li&gt;If 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x≤yx \le y&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;y≤xy \le x&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, then 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x=yx = y&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/li&gt;
&lt;li&gt;If 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x≤yx \le y&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;y≤zy \le z&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, then 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x≤zx \le z&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, too, is the set of &lt;em&gt;classes&lt;/em&gt; (in Ruby, this would be the &lt;code&gt;Class&lt;/code&gt; class) partially ordered by the "is a subset of" relationship, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;⊆\subseteq&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every set is a subset of itself: 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;A⊆AA \subseteq A&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/li&gt;
&lt;li&gt;If 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;A⊆BA \subseteq B&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;B&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;B⊆AB \subseteq A&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;B&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, then 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;A=BA = B&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;B&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/li&gt;
&lt;li&gt;If 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;A⊆BA \subseteq B&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;B&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;B⊆CB \subseteq C&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;B&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, then 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;A⊆CA \subseteq C&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But a key difference between 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;≤\le&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;⊆\subseteq&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is that "less than or equal to" is a &lt;a href="https://en.wikipedia.org/wiki/Total_order" rel="noopener noreferrer"&gt;total order&lt;/a&gt; on its corresponding set (i.e., integers). This means the relationship satisfies one additional property.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connexity&lt;/strong&gt;: any two elements must be related in some direction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given any two integers 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;xx&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;yy&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, either 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x≤yx \le y&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 or 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;y≤xy \le x&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;y&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. That is, integers are always comparable.&lt;/p&gt;

&lt;p&gt;However, the subset relationship is &lt;strong&gt;not&lt;/strong&gt; a total order. Although some sets are subsets of others, there are &lt;a href="https://en.wikipedia.org/wiki/Disjoint_sets" rel="noopener noreferrer"&gt;disjoint sets&lt;/a&gt; with nothing in common. We can't say &lt;code&gt;HSL&lt;/code&gt; is a subset of &lt;code&gt;HSV&lt;/code&gt; or vice versa; &lt;code&gt;RGB&lt;/code&gt; is not a subclass of &lt;code&gt;Cylindrical&lt;/code&gt;, nor is &lt;code&gt;Cylindrical&lt;/code&gt; a subclass of &lt;code&gt;RGB&lt;/code&gt;. So classes aren't always comparable.&lt;/p&gt;

&lt;p&gt;This matters because we'd like to order our conditional branches correctly. We must be able to sort classes such that the "smallest", most specific subset is first—so that &lt;code&gt;HSL&lt;/code&gt; &amp;amp; &lt;code&gt;HSV&lt;/code&gt; come before &lt;code&gt;Cylindrical&lt;/code&gt;. But if 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;⊆\subseteq&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is not a total order, how can we sort classes relative to each other? What order do &lt;code&gt;HSL&lt;/code&gt; &amp;amp; &lt;code&gt;HSV&lt;/code&gt; go in, since they're not comparable?&lt;/p&gt;

&lt;p&gt;The answer is not to use a traditional &lt;a href="https://en.wikipedia.org/wiki/Comparison_sort" rel="noopener noreferrer"&gt;comparison sorting&lt;/a&gt; algorithm that CS majors learn so much about in school. Rather, we can use &lt;a href="https://en.wikipedia.org/wiki/Topological_sorting#Relation_to_partial_orders" rel="noopener noreferrer"&gt;topological sorting&lt;/a&gt;. Theory tells us that for any partial order there exists at least one topological ordering. Incomparable elements may appear in any order relative to each other, as long as they come before/after comparable elements as appropriate.&lt;/p&gt;

&lt;p&gt;Any such ordering is sufficient for our current example. One possible topological ordering puts &lt;code&gt;HSL&lt;/code&gt; before &lt;code&gt;HSV&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSL to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;HSV&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSV to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just as valid would be to put &lt;code&gt;HSV&lt;/code&gt; before &lt;code&gt;HSL&lt;/code&gt;. As long as they both come before their superset &lt;code&gt;Cylindrical&lt;/code&gt;, the topological ordering keeps us from executing the wrong logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;HSV&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSV to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;HSL&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(HSL to RGB algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Cylindrical&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might seem trivial. Our classes all have one immediate superset, so we can readily see this "straight line" topological order from &lt;code&gt;HSV&lt;/code&gt; to &lt;code&gt;Cylindrical&lt;/code&gt; to &lt;code&gt;Color&lt;/code&gt; to &lt;code&gt;Object&lt;/code&gt;. Never fear: there's still more rainbow left to untangle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple inheritance
&lt;/h2&gt;

&lt;p&gt;Where the topological order matters more is in when a subclass can have several superclasses, as is the case with &lt;a href="https://en.wikipedia.org/wiki/Multiple_inheritance" rel="noopener noreferrer"&gt;multiple inheritance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Languages don't seem to advertise their multiple inheritance loudly. If you ask me, it's a case of &lt;a href="https://en.wikipedia.org/wiki/No_true_Scotsman" rel="noopener noreferrer"&gt;no true Scotsman&lt;/a&gt;. Many languages have some &lt;em&gt;form&lt;/em&gt; of multiple inheritance, they just won't call it that. They implement different limitations &amp;amp; algorithms to handle some of the ambiguities that can arise, making their flavor of multiple inheritance different from "pure" multiple inheritance—whatever that means. As a result, trying to understand the concept in terms of any one language will be difficult: Ruby's approach will lead you one way while Python's will lead you another. Once again, it serves us to think about the topic in the abstract (and not too pedantically).&lt;/p&gt;

&lt;p&gt;In terms of sets, multiple inheritance declares that a class is a subset of the &lt;a href="https://en.wikipedia.org/wiki/Union_%28set_theory%29" rel="noopener noreferrer"&gt;union&lt;/a&gt; of multiple superclasses. The union of sets 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SS&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;TT&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, denoted 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;S∪TS \cup T&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∪&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, is a set such that if 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x∈Sx \in S&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 or 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x∈Tx \in T&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, then 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;x∈S∪Tx \in S \cup T&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;x&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;∈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∪&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;

&lt;p&gt;In Ruby terms, you can't inherit from multiple classes. But you &lt;em&gt;can&lt;/em&gt; &lt;a href="https://en.wikipedia.org/wiki/Mixin" rel="noopener noreferrer"&gt;mix-in&lt;/a&gt; multiple modules. In the language, modules differ from classes in that they don't have instances. That is, you can't call &lt;code&gt;M.new&lt;/code&gt; for a module &lt;code&gt;M&lt;/code&gt;. However, the two are still very much related. In fact, &lt;code&gt;Class&lt;/code&gt; is a subclass of &lt;code&gt;Module&lt;/code&gt; in Ruby. Realizing this, it's not a stretch to think that modules are still sets. If you can conceptualize classes as sets of instances, a module might be a set of objects that respond to certain methods. Despite the technical differences, the conceptual effect is the same: the child acquires traits from multiple parents.&lt;/p&gt;

&lt;p&gt;So you want a convoluted example? You've got it! We'll first define modules how we normally would in Ruby, then think again about how methods could theoretically be implemented as top-level functions.&lt;/p&gt;

&lt;p&gt;Let's say we have some shared functionality for objects that are suitable subjects of poetry. According to Keats, this might include an angel's wings, gnomed mines, and of course rainbows. For us to write a poem about any given object, we might need to compute properties about it, like words that rhyme with the object's name or the source of its artistic beauty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Poetic&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rhymes&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Poetic rhymes algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;beauty&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Poetic beauty algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While angels, gnomes, and rainbows aren't subclasses of each other, we can factor out their shared poetic functionality.&lt;/p&gt;

&lt;p&gt;Compare this to cold, calculating science. In science, we care about objects that we can measure. As Keats laments, all the object's mysteries will be conquered by rule and line. But to a scientist, the beauty is in knowing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Scientific&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;measurements&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Scientific measurements algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;beauty&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Scientific beauty algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any number of objects might have scientific functionality, but we care primarily about rainbows. Moreover, a rainbow is both a poetic &lt;em&gt;and&lt;/em&gt; a scientific phenomenon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rainbow&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Poetic&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Scientific&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The spelling might be different from subclassing, but this declares that &lt;code&gt;Rainbow&lt;/code&gt; is a subset of the union of &lt;code&gt;Poetic&lt;/code&gt; and &lt;code&gt;Scientific&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F600924%2F80845191-66eb5500-8bbd-11ea-908c-dbc4dde1f835.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F600924%2F80845191-66eb5500-8bbd-11ea-908c-dbc4dde1f835.png" alt="A Venn diagram showing conceptual multiple inheritance, where the Rainbow set overlaps the union of the Poetic &amp;amp; Scientific sets."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In mathematical notation, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Rainbow⊆Poetic∪Scientific\texttt{Rainbow} \subseteq \texttt{Poetic} \cup \texttt{Scientific}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Rainbow&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;⊆&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Poetic&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∪&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord texttt"&gt;Scientific&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. In Ruby, you can even compare the sets the same way as before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rainbow&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Poetic&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; true&lt;/span&gt;
&lt;span class="no"&gt;Rainbow&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Scientific&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defining the functional logic of the unique methods is simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rhymes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Poetic&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Poetic rhymes algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;measurements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Scientific&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Scientific measurements algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, what do we do about the &lt;code&gt;beauty&lt;/code&gt; function, whose method is defined for both &lt;code&gt;Scientific&lt;/code&gt; and &lt;code&gt;Poetic&lt;/code&gt;? Even though a topological sort would put &lt;code&gt;Rainbow&lt;/code&gt; before either of these modules, &lt;code&gt;Scientific&lt;/code&gt; and &lt;code&gt;Poetic&lt;/code&gt; themselves are incomparable. So the behavior is dependent on the order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;beauty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Poetic&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Poetic beauty algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Scientific&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Scientific beauty algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;rainbow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rainbow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;violet&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;beauty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rainbow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; (Poetic beauty algorithm)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;Poetic&lt;/code&gt; before &lt;code&gt;Scientific&lt;/code&gt;, the poetic algorithm is used on the &lt;code&gt;Rainbow&lt;/code&gt; instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;beauty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Scientific&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Scientific beauty algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="no"&gt;Poetic&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'(Poetic beauty algorithm)'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NoMethodError&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;rainbow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rainbow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;violet&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;beauty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rainbow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; (Scientific beauty algorithm)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;Scientific&lt;/code&gt; before &lt;code&gt;Poetic&lt;/code&gt;, the scientific algorithm is used on the &lt;code&gt;Rainbow&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;In a way, our topological sort has failed us: either of the above could be valid. This is where language implementations resolve the ambiguity in different ways—sometimes even as full-fledged features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Ruby, classes may mix in multiple modules. &lt;a href="https://ruby-doc.org/core-2.7.1/Module.html#method-i-include" rel="noopener noreferrer"&gt;&lt;code&gt;include&lt;/code&gt;&lt;/a&gt; inserts the module directly after the current class in the topological order, so a more recent &lt;code&gt;include&lt;/code&gt; has higher precedence than a less recent one. &lt;a href="https://ruby-doc.org/core-2.7.1/Module.html#method-i-prepend" rel="noopener noreferrer"&gt;&lt;code&gt;prepend&lt;/code&gt;&lt;/a&gt; inserts a module directly before the current class in the topological order, which allows for some &lt;a href="http://gshutler.com/2013/04/ruby-2-module-prepend/" rel="noopener noreferrer"&gt;interesting patterns&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In Common Lisp (&lt;a href="https://en.wikipedia.org/wiki/Common_Lisp_Object_System" rel="noopener noreferrer"&gt;CLOS&lt;/a&gt;), &lt;a href="http://www.gigamonkeys.com/book/object-reorientation-classes.html" rel="noopener noreferrer"&gt;classes&lt;/a&gt; may have multiple parents. &lt;a href="http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html" rel="noopener noreferrer"&gt;Generic functions&lt;/a&gt; sort applicable methods with tie-breakers based on the declaration order. This list can be applied in &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/07_ffac.htm" rel="noopener noreferrer"&gt;standard&lt;/a&gt; or &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_defi_4.htm#define-method-combination" rel="noopener noreferrer"&gt;customized&lt;/a&gt; ways.&lt;/li&gt;
&lt;li&gt;In Scala, classes may mix in multiple &lt;a href="https://docs.scala-lang.org/tour/traits.html" rel="noopener noreferrer"&gt;traits&lt;/a&gt;. The spec defines a standard &lt;a href="https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#class-linearization" rel="noopener noreferrer"&gt;class linearization&lt;/a&gt; that winds up working similarly to Ruby's, where the order of declarations matters.&lt;/li&gt;
&lt;li&gt;In Python, classes may inherit from any number of parent classes. The method resolution order (MRO) is given by the &lt;a href="https://www.python.org/download/releases/2.3/mro/" rel="noopener noreferrer"&gt;C3 linearization algorithm&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;a href="https://factorcode.org/" rel="noopener noreferrer"&gt;Factor&lt;/a&gt;, ambiguities in the &lt;a href="https://docs.factorcode.org/content/article-class-linearization.html" rel="noopener noreferrer"&gt;class linearization&lt;/a&gt; are resolved through a couple different tie-breaking rules based on &lt;a href="https://en.wikipedia.org/wiki/Metaclass" rel="noopener noreferrer"&gt;metaclasses&lt;/a&gt; &amp;amp; lexicographic order.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Multiple_inheritance#Mitigation" rel="noopener noreferrer"&gt;And so on.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no right answer. But knowing the abstract problem helps you to better understand the algorithms used by your favorite language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types
&lt;/h2&gt;

&lt;p&gt;I'd be remiss not to mention &lt;a href="https://en.wikipedia.org/wiki/Type_theory" rel="noopener noreferrer"&gt;type theory&lt;/a&gt;, since we're used to dealing with various notions of types in programming languages.&lt;/p&gt;

&lt;p&gt;If you dig into set theory and the foundations of mathematics, you'll find various equivalent formalisms. While there is ostensibly a &lt;a href="https://softwareengineering.stackexchange.com/questions/362316/whats-the-difference-between-a-subclass-and-a-subtype" rel="noopener noreferrer"&gt;difference between a subclass and a subtype&lt;/a&gt;, in practice I think it's largely pedantic. At any rate, the &lt;em&gt;theories&lt;/em&gt; of sets and types are &lt;a href="https://plato.stanford.edu/entries/type-theory/#TypeTheoTheo" rel="noopener noreferrer"&gt;equivalent&lt;/a&gt;. That being the case, it's still useful for programmers to think of a classes as sets, sets as types, and thus classes as types.&lt;/p&gt;

&lt;p&gt;For example, a practical concept that falls out of this kind of thinking is the difference between &lt;a href="https://en.wikipedia.org/wiki/Has-a" rel="noopener noreferrer"&gt;has-a&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Is-a" rel="noopener noreferrer"&gt;is-a&lt;/a&gt; relationships. Intuitively, a rainbow is not a type of color, but RGB is. However, a rainbow contains many colors. Thus, &lt;code&gt;Rainbow&lt;/code&gt; should use a has-a (or really, has-many) relationship to &lt;code&gt;Color&lt;/code&gt; by keeping objects in instance variables, whereas &lt;code&gt;RGB&lt;/code&gt; should use an is-a relationship by subclassing &lt;code&gt;Color&lt;/code&gt;. But you don't even need to know the buzzwords if you can think critically about the types (and therefore classes) involved in your program. Doing so means you're actively thinking about how to write good code. And that's a skill transferable to any language, whatever the subclassing (or subtyping) mechanisms might be.&lt;/p&gt;

&lt;p&gt;This also means that despite whatever misconceptions people may have, even a dynamic language like Ruby has types. After all, it has classes. Just because one language doesn't use the same type-checking algorithms as another, it doesn't mean that types cease to exist. They're there abstractly even if your language doesn't call them out by name. As such, it still behooves you to think about types in a general sense, whether you're writing dynamically-typed PHP or statically-typed Java.&lt;/p&gt;

&lt;p&gt;Conversely, even &lt;a href="https://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;functional&lt;/a&gt; languages that we don't normally think of as object-oriented have the same theoretical underpinnings. Instead of full-blown classes you might see a construct like &lt;a href="https://elixir-lang.org/getting-started/structs.html" rel="noopener noreferrer"&gt;Elixir's &lt;code&gt;defstruct&lt;/code&gt;&lt;/a&gt;, which defines a type with specific fields but no methods. Or there may be very sophisticated ways of defining custom types, as in &lt;a href="http://learnyouahaskell.com/making-our-own-types-and-typeclasses" rel="noopener noreferrer"&gt;Haskell's algebraic data types&lt;/a&gt;. Such types might include something like &lt;a href="https://docs.racket-lang.org/ts-guide/types.html#%28part._.Union_.Types%29" rel="noopener noreferrer"&gt;Racket's anonymous unions&lt;/a&gt;, allowing us to formulate multiple inheritance in terms of type constraints. And as we've seen, rather than attach methods to specific classes, you could define functions that dispatch based on the type of their first argument. But why stop there? A system like &lt;a href="https://en.wikipedia.org/wiki/Common_Lisp_Object_System" rel="noopener noreferrer"&gt;CLOS&lt;/a&gt; can use &lt;a href="https://en.wikipedia.org/wiki/Multiple_dispatch" rel="noopener noreferrer"&gt;multiple dispatch&lt;/a&gt; to select the most specific function to apply to the &lt;em&gt;combination&lt;/em&gt; of parameters. Of course, you could also be &lt;a href="https://ocaml.org/releases/4.10/htmlman/index.html" rel="noopener noreferrer"&gt;OCaml&lt;/a&gt; and fuse object orientation with the type system &amp;amp; syntax of the functional language &lt;a href="https://en.wikipedia.org/wiki/Standard_ML" rel="noopener noreferrer"&gt;ML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Programming languages vary widely, but the concepts can still be understood in general terms. If you understand the general, then learning any specific language—and writing good code in it—becomes that much easier.&lt;/p&gt;

&lt;h1&gt;
  
  
  Rainbows unwoven
&lt;/h1&gt;

&lt;p&gt;So there it is. Classes &amp;amp; inheritance analyzed to death; the underlying theory exposed. Armed with this knowledge, what have you lost? If you ask me, not the beauty, but the preconceptions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Object orientation isn't at odds with functional programming. Either can be understood (at least at a high level) in terms of the other.&lt;/li&gt;
&lt;li&gt;Mixins, multiple inheritance, and union types are more similar than they are different.&lt;/li&gt;
&lt;li&gt;Even among object-oriented languages, one system isn't fundamentally different from another. They're all different flavors of set theory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A major benefit of abstraction is unifying ideas into an elegant whole that can be kept in your head. If you understand that classes are sets, and you already know how sets behave, you're capable of grokking a wide variety of object-oriented systems. Language becomes an implementation detail.&lt;/p&gt;

&lt;p&gt;But despite what you might automatically think that means, implementation &lt;em&gt;does&lt;/em&gt; matter. Each object system is beautiful in some way. Taking advantage of your language's strengths makes your job easier as a programmer. Playing to these strengths lets you organize your code in beautiful ways. What could be more important?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[W]e want to establish the idea that a computer language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;— &lt;a href="https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-7.html" rel="noopener noreferrer"&gt;Structure and Interpretation of Computer Programs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So go forth and destroy some rainbows. You never know what you might learn. 🌈&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>oop</category>
      <category>plt</category>
    </item>
  </channel>
</rss>
