1. 15

  2. 22

    You can write any article you want about FP and OOP because everyone seems to pick their own definition. I tend towards Alan Kay’s definition of OO, because he coined the term and gets to define it. I tend towards a very Haskell-flavoured definition of FP because of various personal biases that are not really defensible as objective interpretations.

    There’s a huge difference between Kay’s definition of OOP and Haskell’s definition of FP. OOP is about stateful objects that communicate via message passing. Each message may mutate the state of an object and may trigger more messages. Whether the messages are synchronous (function-call-like) or asynchronous (actor-like) are localisation decisions made when mapping the abstract idea to a specific language instantiation. In contrast, Haskell-style FP is built around the idea that there is no such thing as mutable state. If you want something like mutation, you use a Monad.

    A more Lisp-like definition of FP is far closer to OOP. The COLA model shows very cleanly that objects and closures are equivalent and if you have one then you can trivially have the other. Common Lisp’s CLOS demonstrated that you can implement any definition of OOP in Lisp, including some that were never implemented anywhere else.

    Like the author of the article, I don’t really find the distinction particularly useful, except perhaps when thinking about API design. An OO style makes it easy to add new nouns but hard to add new verbs, a functional style makes it easy to add new verbs but hard to add new nouns. Things like class extensions and generics blur this line a lot.

    1. 3

      Smalltalk OO does not require mutation but only allows it. The essence of the paradigm is still sending messages to objects you receive without source dependency on their type/existance (aka duck typing aka polymorphism)

      1. 4

        Smalltalk OO does not require mutation but only allows it.

        But that distinction doesn’t matter. The fact that it’s allowed and may be hidden from you is the point- you don’t get to make assumptions about the object beyond what it declares as its “contract” (method signatures in most “typical” languages). So you “can’t” assume that calling the same method twice in a row will even give you the same result.

        And that’s fine. When I write code in one of the many mainstream “multi-paradigm” languages, I often model (mentally, as well as in code) network-request-makers as “objects” in the Alan Kay-ish sense. I (again, mentally) separate my class/structs into “data” and “objects”.

        Not sure what my point is. Just commenting because I like to type, I guess.

        1. 4

          I think what @singpolyma meant is that a Smalltalk without mutable state would still work and would still be OO. It’s just that, instead of mutating its state in response to a message, an object would return a new instance with different state. Therefore mutability is not an intrinsic feature of OOP.

      2. 1

        /u/singpolyma makes a good point.

        If I were to point at the biggest mistake in the development of OOP, it is the name “constructor”.

        Call them and treat them as “name binders”…. to bind the instance variables to names and to create a class that enforces an invariant you can rely on, wherever you use that type.

        Yes you can mutate them if you want.

        You should prefer not to. Make it const or frozen or whatever facility your language provides and only make it mutable if there is a strong good reason to do so.

        Spoiler Alert: It’s very seldom (but not never) that you need to make them mutable.

      3. 17

        I have yet to hide any tag on Lobsters, but if there were a tag for these stupid OOP vs FP hot takes I would hide them in an instant.

        1. 16

          I’d generalize that to every article “Uncle Bob” writes.

          1. 3

            But you would miss the discussions on Lobsters, which are often better than the OP ;)

            1. 2

              Same; I think I’d hide even an OOP tag by itself, never mind FP.

            2. 13

              This article is absolute garbage full of both factually incorrect statements (there is absolutely nothing about functional programs that prevent you from having ad-hoc polymorphism, and in fact ad-hoc polymorphism is heavily used in both haskell (typeclasses) and rust (traits), neither of which are traditional OO languages). Although he acknowledges that his arguments are overly reductive, I see absolutely no reason to forgive it given that the dimensions he chooses to reduce the argument to are boring and uniformative, and his conclusion bland. This guy is a misogynist with nothing useful to contribute to the field of software engineering and is best left alone to howl in obscurity into the silence of history.

              1. 4

                there is absolutely nothing about functional programs that prevent you from having ad-hoc polymorphism

                He says exactly that.

              2. 4

                This is a very poor definition of OO. By this definition Julia’s multiple dispatch functions are some of the most polymorphic things in programming languages; And I think you would be very hard-pressed to describe Julia as OO.

                1. 1

                  Julia is a very lisp-inspired, smalltalk-inspired system with a ruby-inspired syntax. In what way is it not OO?

                  1. 1

                    Where did you get the idea that Julia is smalltalk inspired? I am pretty sure there is no smalltalk-style message-passing dispatch under the hood.

                    The Syntax is also not ruby-inspired (except for maybe using : for symbols and using ! as a postfix sigil for mutating functions).

                    1. 1

                      Julia homepage does mention ruby as one of the languages from which it borrows.

                      1. 1

                        I’m very aware, Have used julia since 0.4, and ruby before that, and its borrowing some ideas from ruby is part of why I got excited by it. But borrowing one or two things does not mean your language is inspired by another.

                        But you are also really pushing hard against the limits of credible transitivity if you are saying julia borrows features from ruby, which is inspired by smalltalk, that means that julia is inspired by smalltalk - when the core features and architecture are not in common, and the borrowed features from ruby are not themselves a major part of smalltalk.

                        At what point does it become silly? Is Go javascript-inspired because it, too, uses curly braces?

                        1. 1

                          I am not that familiar with Julia (or with the influence diagrams and inspiration transitivity in general), but wasn’t CLOS which inspires Julia MOP originally inspired by the Smalltalk MOP?

                2. 3

                  > Laughs in CLOS

                  1. 3

                    The OO concept this article is trying to get across is late binding: that the function invoked by a call is looked up at runtime, not compile time. So the “function” named at the call site is really just that, a name, and it’s the combination of that name (called a message) and the receiver’s runtime type that determines what function (method) to call.

                    The syntax doesn’t matter. There are languages like Nim where f(x) and x.f() are semantically identical and interchangeable. In Nim a call may be early-binding or late-binding, depending on the declaration of f in x’s compile-time base class.

                    1. 1

                      Also, in some languages (I think Python is like this), if f has class C, then x.f() and C::f(x) are equivalent. Granted, there’s no reason to call it like this most of the time, but it shows that it’s not really polymorphism, it’s just that resolution of f in the ‘object-oriented’ case has an extra step, as you say.

                    2. 2

                      My very first FP language was Scala, which has first class objects. Granted this is one of the rare ones, but OO FP does exist.

                      1. 3

                        The article is all about how FP and OO co-exist without issue and there is no “vs”. I wouod go one step further and say there is no “OO language” or “FP language” – every language with a feature that can simulate a closure can describe both paradigms at will.

                        1. 2

                          Scala had the explicit goal to combine FP and OO nicely.

                        2. 2

                          They lost me immediately at “this syntax means it’s a polymorphic function”. Glad to see I’m not the only one.

                          1. 1

                            I find the starting point of the article hard to accept if not plainly wrong.

                            How is f(o) and o.f() the same semantically? It might result in the same under the hood in some languages, but it is absolutely not the same semantically. o.f() clearly communicates that f() is to be looked up on that object or scoped to it in some way while f(o) is usually perceived as a function accessible in your current scope. And that is the case for most languages. This is not a small difference, and if the author is thinking in languages like java, where the parameter type is used to disambiguate two functions with the same name otherwise, it is still not true. f(o) and f(o2) have to be the same method for objects declared with the same type, while o.f() and o2.f() might not be.

                            And how would we go about functions with more parameters? o.f() assumes state and assumes that a function definition is in someway attached to an instance of a data type. This is arguably a very bad idea and the reason why we have all these discussions. OOP is successful in its popularity, but horror stories are their strongest tradition. What would happen if you take languages like java or C# and force all methods to be static and live in separate Classes (for lack of a better word) than data classes, aka records? If we maintain most of the same syntax and still call it OOP, will people still be happy. As it is, most codebases immediately degrade to an entangled mess.

                            Edit: typescript compiler is written in typescript and does exactly this.

                            1. 1

                              For me the fundamental difference between the two is around how logic relates to data. OO approach couples data with the logic that operates on it. An object is effectively a state machine, and its methods are the API to manipulate and inspect its state. An OO program is structured as a graph of such interdependent state machines.

                              On the other hand, FP approach keeps logic and data decoupled. Functions accept data as their input and produce data as their output. The state is expressed via chaining of these functions.

                              I think there are numerous advantages to the latter approach. It makes code reuse much easier since the functions do not care about where the data comes from, and can be directly applied in different context. Data can be easily serialized and transformed in a general fashion while objects require custom approach for each type of object. Objects are both opaque and stateful, making large object graphs impossible to reason about. Data is transparent and inert, and each pure function can be safely reasoned about in isolation.