Dialogue with OOP guy

O — FP adepts are so arrogant. Why are they constantly blaming OOP? Don't they have other stuff to do?

F — because OOP is wrong ;)

O — Yeah, like that...

F — I can prove it to you if you want.

O — Go ahead :)

F — You can't compare objects in OOP...

O — I see where you're leaning here! You can compare objects if your language allows to overload operators. JS is not a true OOP language but in Ruby you can easily do it!

F — So you think if you can hook into comparison operation, you can compare objects?

O — Sure.

F — How would you compare methods?

O — Eh, methods are equal if they are equal aren't they?

F — Is (x) => 2 * x equal to (x) => x + x?

O — Kinda. Why, yes!

F — ...

O — Ok. Methods are equal if they return the same data for the same input!

F — How do you know that at the comparison time?

O — Ehh, there should be some way.

F — What about side effects?

O — ... and contain equal side effects.

F — Are these two snippets equal:


O — Yes

F — What about buffering?

O — Auch!

F — Memoizing?

O — I give up, you can't compare functions.

F — ...

O — Exclude methods from comparison. Problem's solved.

F — So if datas are equal – two objects are equal?

O — Yep.

F — What about methods returning data?

O — What do you mean?

F — You want to serialize new User("John", "Doe") to {"fullname": "John Doe"} where fullname is a method over name and surname.

O — It's probably a wrong serialization...

F — Let's say the remote API requires it. Where do you put the derived data? In method – and your serialization is broken. In data – and you're open to unsync issues.

O — I see. Well, I can hook into both the comparison and serialization procedures. Problem's fixed.

F — What if I told you OOP is about fixing problems while FP is about preventing them?

O — Can you describe other problems?

F — How do you hook into serialization?

OtoString, toNumber... whatever name your language uses.

F — What about hooks for non-builtins?


F — How do you force it to autocall on custom coercion?

O — Well... I think my language does not provide such feature. But in the perfect OOP language it could be done.

F — But you can always override ;) More code is just more code.

O — We are programmers. We are not afraid of code.

F — We can always autogenerate it!

O — Java is not a true OOP language. Really, what else?

FNo true Scotsman. See – just by switching to OOP from Procedural Programming, you already lost half of the basics: comparison, serialization, ordering, logging...

F — And you spend time recovering it instead of programming.

O — Still, OOP provides better structuring. You can't argue that.

F — Circle-Ellipse problem...

O — Never heard of it.

FCircle is a subclass of Ellipse, isn't it?

O — Sure. It's a specification of it.

F — And Ellipse has a method stretchX – but you can't stretch a Circle...

O — Hmmm. Then this method should probably return an Ellipse instance.

F — So it should be immutable and instance substitution is not a problem.

O — Yep. In good OOP language you can do that.

F — But substitution is a problem. After the stretchX line you have to encounter for both classes. You've created an implicit union case.

O — Still fixable.

F — What about constructor? Ellipse should accept more params than Circle.

O — Again, in good OOP language you can do that.

F — But this makes constructors not replaceable.

O — Snap. Inheritance is an antipattern – OOP community accepts that! We are not afraid to admit our weak parts, unlike the others ;)

F — So good OOP is immutable, without inheritance and without methods?!

O — Yes. Oh, wait – no! Methods are fine.

F — Except the cases when they are not, like in Circle-Ellipse? ;)

O — Can you generalize the problematic cases?

F — All the cases where subclass adds restrictions. And this is possible with any new behavior (read – method). Inheritance really works only for data.

O — How often will you add restrictions?

F — Actually, quite a lot. You see, classes are poor man's types. And in classic theory of static typing you mostly use types to introduce restrictions. Like Month type introduces restrictions to Integer or replaces it, depending on a type system.

O — Supposably. I'm not sure I totally get it.

F — ... also not many OOP languages allow to define union types / classes. And coding without unions is like coding without one hand.

O — I'm not so sure. Those examples were artificial.

F — Every OOP tutorial mentions geometry figures or animals. "Broken to the core" – I would say.

O — Animals are fine.

F — Really? What about:

class Animal {
  eat() { console.log("I can eat!") }

class Bird extends Animal {
  fly() { console.log("I can fly!") }

class Fish extends Animal {
  swim() { console.log("I can swim!") }

class FlyingFish extends Fish /* or Bird?! */ {
  // I can swim and I can fly :(

O — Multiple inheritance! Arguable subject...

F — OOP guys can't agree on a single point aren't they? ;)

O — Damn animals. You can use mixins.

F — So you can mix an anymal part to any other object? Is it sane?

O — I dunno. Maybe.

F — So good OOP is immutable, without inheritance and without methods?!

O — Good OOP language is yet to be created. I'm not sure how they will solve those problems but I'm sure they will.

F — What's the benefits of combining data and behaviors?

O — It's more convenient.

F — Never guessed about passing data in params vs accessing this?

O — Well, honestly, it's a constant struggle. But it takes time to come with a good hierarchy. Once you settled – it's fine.

F — Until the next refactoring?

O — We are programmers – we are not afraid of refactoring.

F — What about clients? They don't like tickets like that...

O — They have no clue about programming. It's a cultural problem.

F — Ok. What about constructors? Why do you use them?

O — It's convenient.

F — Than why do you need factories?

O — Because sometimes you need to create object differently, lol.

F — Don't you find it awkward to use different approaches to do the same stuff?

O — I can imagine a language with multiple overloadable constructors.

F — But when you need to apply it to library code...

O — Then you use inheritance... oh, wait. Then you have to use a standalone function. By the way, JavaScript allows standalone functions and creating objects without constructors!

F — But Ruby does not. And you said before JS is a not a "true OOP".

O — Whatever.

F — What about ORMs?

O — I love them. And sometimes I hate them.

F — ORM couples types with database.

O — And?

F — And if you need multiple databases – how many ORM models will you declare?

O — Ehh, I dunno. Never had such a case.

F — What about frontend and backend models?

O — ...

F — What about validation rules? The perfect typesystem will allow you to generate such rules from the type.

O — Now you're talking about perfections...

F — Constant improving is in human nature. The question is how fast we advance. So what about ORM?

O — ORM is a decent choice. You can always fallback to raw SQL.

F — Loosing all the validation and typing properties of the model...

O — Fixable.

F — ... forcing you into ad-hoc serialization and parsing ...

O — Still fixable.

F — ORM were intended to fix impedance mismatch. Now you need to fix after them. And I guess fix after those fixes as well. A pattern?

O — Could proper typing solve this problem?

F — Yes.

O — Still if OOP is that bad why everyone uses it?

F — Actually not everyone. If you make a search – you'll find that quite a lot of big systems are already written in non-OO languages.

O — And still?

F — I can express only my own opinion. I think it was a coincidence of facts. Java was "better than C". Java was cross-platform. Java was quite good for GUI programming. And GUI programming was a dominant theme back then.

O — So you accept that OOP is good for GUI. Why?

F — Expression problem. In GUI programming you tend have a lot of "things" and not so many "behaviors". While in other areas (and I would say in most areas) there tend to be more "behaviors" than "things.

O — Gotcha. Expression problem basically says FP and OOP are equally good (or equally bad for your taste).

F — Not exactly. First – all modern F languages provide their own solutions to Expression problem. Abstractions over types.

F — While OO languages kinda stuck with it. Even JS, which has a unique way to overcome this problem due to dynamic this.

O — And second?

F — And second – OO language suffer from both parts of Expression problem as long as they contain primitive types. Methods are functions, you know... Wiki don't tell you the whole awful truth.

O — The quest for a perfect OO language continues.

F — It will never succeed.

O — Why?

F — Because OOP is fundamentally wrong.

O — That argument again.

F — It's not an argument. It's a statement of fact. What OOP abstracts over?

O — Objects, Methods, States...?

F — Bingo. OOP abstracts over shared mutable state.

O — And?

F — And this abstraction has a name...

O — Which one?

F — Race condition.

F — OOP is not suitable for modern programming. Multiple cores, parallelism – all that stuff.

O — Async queues for the win!

F — Immutable programming, without inheritance, with pure functions and few isolated states... Legend says – it was called "Functional Programming".

O — Ok, enough. I think I've heard something about it. Where can I read more?

F — There's quite lot of information actually.

F — The thing is – most functional programmers of today inevitably learned and inevitably had to use a lot of OOP during their careers. The attitude to OOP comes from experience, not from ignorance.

O — "Just give me the link". I see some logic in your criticism but I want to make my own judgement.

F — Sure. I recommend to start with criticism anyway. You need to remove (or reduce at least) the tolerance to bad parts. As we were taught to blame ourselves for crappy design we always get with OOP.

F — Every time it's "another language" or "another pattern" at best. Time for "another paradigm"? Ok, I'm probably too conversational. Grab the links to begin your journey:





O — Thanks!

F — No problem.

Author: @ivankleshnin