<story><title>The use of `class` for things that should be simple free functions</title><url>https://quuxplusone.github.io/blog/2020/05/28/oo-antipattern/</url></story><parent_chain><item><author>kqr</author><text>There&amp;#x27;s one big difference in terms of convenience in many common implementations of classes and methods. There are essentially three different pieces involved:&lt;p&gt;In OO notation, we have A.B(C)&lt;p&gt;In almost all languages, you can store C in a variable and supply it &amp;quot;later&amp;quot;:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    z = C
    A.B(z)
&lt;/code&gt;&lt;/pre&gt;
You can also store A in a variable and supply it &amp;quot;later&amp;quot;:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    x = A
    x.B(C)
&lt;/code&gt;&lt;/pre&gt;
However, it is much more rare to be able to store B in a variable and supply it &amp;quot;later&amp;quot;. Often, you must store A.B:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    xy = A.B
    xy(C)
&lt;/code&gt;&lt;/pre&gt;
This means that at the time you select which method to call, you must also have at your disposal the instance on which you want to call it!&lt;p&gt;With functions, things get easier. If you have B(A, C), you can generally just store B in a variable on its own:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    y = B
    y(A, C)
&lt;/code&gt;&lt;/pre&gt;
Some OO languages allow you to store plain methods that aren&amp;#x27;t yet bound to a particular instance (JavaScript, modern Java) but far from all do, without resorting to inconveniences like&lt;p&gt;&lt;pre&gt;&lt;code&gt;    y = lambda o: o.B
&lt;/code&gt;&lt;/pre&gt;
Please note that what this workaround accomplishes is effectively to turn the method into a function!&lt;p&gt;I suspect the main argument here is that it just doesn&amp;#x27;t make sense notationally to treat the first argument to a function specially. It only introduces wierd edge cases that have to be worked around. Plain old function notation has stood the test of time for good reason: it&amp;#x27;s flexible and handles most of the common cases we want.</text></item><item><author>cataphract</author><text>But if you&amp;#x27;re passing a state object around, you might as well use a class, no? Admittedly simplifying a bit, an instance method is a function that implicitly takes &amp;quot;this&amp;quot; as the first argument.</text></item><item><author>amw-zero</author><text>None of these situations require an object to do. You can always have a function that takes in an extra ‘state’ argument. You can then do everything you said by passing in the corresponding state values.</text></item><item><author>dataflow</author><text>Counter-point: while in many situations this isn&amp;#x27;t the right approach, it&amp;#x27;s worth recognizing when this &lt;i&gt;is&lt;/i&gt; the right approach, because they can look awfully similar. An example of this is graph searching, e.g. BFS or Dijkstra&amp;#x27;s algorithm. The typical implementation is a function. But if you make Dijkstra a class, with (say) a function to iterate through nodes, it lets you do several things that would be difficult with a function: (a) you can now re-use the same object for computing distances to multiple vertices, which allows incremental search, (b) you can now fork&amp;#x2F;copy the object and continue searching on &lt;i&gt;different&lt;/i&gt; extensions of the original graph, (c) you can now save &amp;amp; restore the searcher state to pause the search &amp;amp; continue it later, (d) you can do multiple searches across one or more graphs &lt;i&gt;in lockstep&lt;/i&gt;. IMHO these are very much &lt;i&gt;not&lt;/i&gt; obvious, and to someone who doesn&amp;#x27;t recognize what&amp;#x27;s going on, a class for something like BFS will look &lt;i&gt;exactly&lt;/i&gt; like an &amp;quot;OO antipattern&amp;quot;, when in reality it&amp;#x27;s actually providing significant additional functionality for more complicated situations.</text></item></parent_chain><comment><author>Huperniketes</author><text>&amp;gt;There&amp;#x27;s one big difference in terms of convenience in many common implementations of classes and methods.&lt;p&gt;Which ones?&lt;p&gt;&amp;gt;However, it is much more rare to be able to store B in a variable and supply it &amp;quot;later&amp;quot;. Often, you must store A.B:&lt;p&gt;How is it rare? Smalltalk doesn&amp;#x27;t require you to specify the receiver when storing a symbol. Neither does Objective-C (selector). Neither does Ruby. Nor Java.&lt;p&gt;Which commonly used languages require one to have the receiver tied to the method when memoized? C++ is the only one I&amp;#x27;m aware of. So it might be rare to do it in C++, but not in OOP generally.</text></comment>
<story><title>The use of `class` for things that should be simple free functions</title><url>https://quuxplusone.github.io/blog/2020/05/28/oo-antipattern/</url></story><parent_chain><item><author>kqr</author><text>There&amp;#x27;s one big difference in terms of convenience in many common implementations of classes and methods. There are essentially three different pieces involved:&lt;p&gt;In OO notation, we have A.B(C)&lt;p&gt;In almost all languages, you can store C in a variable and supply it &amp;quot;later&amp;quot;:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    z = C
    A.B(z)
&lt;/code&gt;&lt;/pre&gt;
You can also store A in a variable and supply it &amp;quot;later&amp;quot;:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    x = A
    x.B(C)
&lt;/code&gt;&lt;/pre&gt;
However, it is much more rare to be able to store B in a variable and supply it &amp;quot;later&amp;quot;. Often, you must store A.B:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    xy = A.B
    xy(C)
&lt;/code&gt;&lt;/pre&gt;
This means that at the time you select which method to call, you must also have at your disposal the instance on which you want to call it!&lt;p&gt;With functions, things get easier. If you have B(A, C), you can generally just store B in a variable on its own:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    y = B
    y(A, C)
&lt;/code&gt;&lt;/pre&gt;
Some OO languages allow you to store plain methods that aren&amp;#x27;t yet bound to a particular instance (JavaScript, modern Java) but far from all do, without resorting to inconveniences like&lt;p&gt;&lt;pre&gt;&lt;code&gt;    y = lambda o: o.B
&lt;/code&gt;&lt;/pre&gt;
Please note that what this workaround accomplishes is effectively to turn the method into a function!&lt;p&gt;I suspect the main argument here is that it just doesn&amp;#x27;t make sense notationally to treat the first argument to a function specially. It only introduces wierd edge cases that have to be worked around. Plain old function notation has stood the test of time for good reason: it&amp;#x27;s flexible and handles most of the common cases we want.</text></item><item><author>cataphract</author><text>But if you&amp;#x27;re passing a state object around, you might as well use a class, no? Admittedly simplifying a bit, an instance method is a function that implicitly takes &amp;quot;this&amp;quot; as the first argument.</text></item><item><author>amw-zero</author><text>None of these situations require an object to do. You can always have a function that takes in an extra ‘state’ argument. You can then do everything you said by passing in the corresponding state values.</text></item><item><author>dataflow</author><text>Counter-point: while in many situations this isn&amp;#x27;t the right approach, it&amp;#x27;s worth recognizing when this &lt;i&gt;is&lt;/i&gt; the right approach, because they can look awfully similar. An example of this is graph searching, e.g. BFS or Dijkstra&amp;#x27;s algorithm. The typical implementation is a function. But if you make Dijkstra a class, with (say) a function to iterate through nodes, it lets you do several things that would be difficult with a function: (a) you can now re-use the same object for computing distances to multiple vertices, which allows incremental search, (b) you can now fork&amp;#x2F;copy the object and continue searching on &lt;i&gt;different&lt;/i&gt; extensions of the original graph, (c) you can now save &amp;amp; restore the searcher state to pause the search &amp;amp; continue it later, (d) you can do multiple searches across one or more graphs &lt;i&gt;in lockstep&lt;/i&gt;. IMHO these are very much &lt;i&gt;not&lt;/i&gt; obvious, and to someone who doesn&amp;#x27;t recognize what&amp;#x27;s going on, a class for something like BFS will look &lt;i&gt;exactly&lt;/i&gt; like an &amp;quot;OO antipattern&amp;quot;, when in reality it&amp;#x27;s actually providing significant additional functionality for more complicated situations.</text></item></parent_chain><comment><author>vanderZwan</author><text>I like the approach Wouter van Oortmerssen uses in Lobster:&lt;p&gt;&lt;pre&gt;&lt;code&gt;    class Animal:
        alive = true

    class Cat : Animal
        def hello(): print &amp;quot;meow&amp;quot;

    class Dog : Animal
        barked = 0

    def hello(d::Dog):
        print &amp;quot;bark!&amp;quot;
        barked++

    let d = Dog {}
    d.hello()

    let a:Animal = d
    a.hello()
&lt;/code&gt;&lt;/pre&gt;
In other words, A.B(C) is just syntactic sugar for B(A, C), and class definitions are just syntactic on top of that. The relation between OO and regular functions and stateful objects is completely transparent (the only real gotcha I can see is that &lt;i&gt;a.hello()&lt;/i&gt; still works here because it was assigned a subclass where this method is defined). This makes it easy to store B for later usage, like you wanted to do in your example, and reason about its behavior.&lt;p&gt;[0] &lt;a href=&quot;http:&amp;#x2F;&amp;#x2F;strlen.com&amp;#x2F;lobster&amp;#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&amp;#x2F;&amp;#x2F;strlen.com&amp;#x2F;lobster&amp;#x2F;&lt;/a&gt;</text></comment>