# Is Julia's way of OOP superior to C++/Python? Why Julia doesn't use class-based OOP?

**URL:** <https://discourse.julialang.org/t/is-julias-way-of-oop-superior-to-c-python-why-julia-doesnt-use-class-based-oop/52058>\
**Category:** Internals & Design\
**Created:** [December 18, 2020, 4:32pm UTC](https://discourse.julialang.org/t/is-julias-way-of-oop-superior-to-c-python-why-julia-doesnt-use-class-based-oop/52058 "2020-12-18T16:32:10Z")\
**Posts on this page:** 1\
**Showing post:** 24

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [December 21, 2020, 4:41pm UTC](https://discourse.julialang.org/t/is-julias-way-of-oop-superior-to-c-python-why-julia-doesnt-use-class-based-oop/52058/24 "2020-12-21T16:41:13Z")

</div>

Despite the fancy name, the expression problem is _definitely_ a stumbling block you’ve encountered many, many times in object-oriented programming, wether you knew what it was or not. From the OOP side (as opposed to the functional language perspective, where it is also a problem but looks quite different), it is simply the problem of trying to add a new operation to existing types. For what it’s worth, I go through this in depth in the video I linked above, but some people prefer written explanations. The core problem with class-based OOP is that methods live _inside_ of classes and everyone has to agree on what methods make sense to add to each class. You may think, “What is the problem? I have added methods to classes all the time?” In your own code that you fully control, sure, if you want a new method, just open up the class definition and add the method. The problem occurs when, as happens in the real world, there are many people sharing and reusing code. Which, you will note, is one of the main problems that OOP is intended to solve.

Suppose, for example, that I maintain a package like [ColorTypes](https://github.com/JuliaGraphics/ColorTypes.jl) but in a class-based language like Python or Java. Now further suppose that you want to treat each color object like a 3-vector (or however many channels it has) and do vector operations like addition, subtraction, and taking norms. You don’t even insist on using operators like `+` and `-`, you’ll happily write `color1.add(color2)`, `color1.sub(color2)` and `color.norm()`. You try to convince me to add these methods to the `Colorant` abstract base class. However, I don’t want to add these methods: I don’t want to take on more features to maintain and it’s not entirely clear to me if/how colors should behave like vectors because perceptual color space isn’t linear in terms of various representations like RGB, CMYK, HSV, Lab, XYZ, etc.

Given my refusal to add methods to ColorTypes, what can you do? Traditional OOP would tell you to define your own subclasses. Problem solved! Right? Well, let’s think it through. The implementation is not as simple as it sounds. You want to add methods to `Colorant` so that all the color types inherit the new methods you add and any generic operations you define work across all color types. So you subclass `VectorColorant <: Colorant`. But now how do you get all the corresponding vector versions of specific concrete subtypes like `RGB`, `HSV`, `CMYK`, etc.? You have to define your own parallel type hierarchy that mirrors the one in `ColorTypes`. If your language supports multiple inheritance (Java does not, Python does), you can define `VectorRGB <: (VectorColorant, RGB)`, but you have to do that for every possible subclass — even ones defined outside of `ColorTypes`. This isn’t great: the number of definitions you have to write scales with the number of concrete subtypes, but at least each definition is trivial.

If your language does not support multiple inheritance, then you’re pretty much stuck: you have to copy the definition of each subtype but inheriting from the `Vector`-prefixed variant of what the original type inherited from. At this point, you’ve copied the entire class hierarchy aside from the topmost abstract base class, which probably doesn’t have much in it anyway, so you might as well just make your own copy of the whole thing into your own separate `VectorColorTypes` package. You also fix a few annoyances with the original code while you’re at it. After a while the upstream `ColorTypes` package makes a few changes that you haven’t kept up with, and the `ColorTypes` and `VectorColorTypes` packages start to diverge. They’re still similar, but unless you both try really hard to keep them in sync, they gradually become more and more incompatible. OOPs! 😬

That’s not even the extent of the problems. Suppose you’re lucky enough to be in a language that supports multiple inheritance and you created a mirror hierarchy of trivial `Vector`-prefixed types without completely parting ways with the original `ColorTypes` package. Now you encounter some really neat `palette` method that someone wrote using `ColorTypes` that takes a single `RGB` value and produces an entire palette of harmonious colors based on it. But you want to use it with your `VectorRGB` type. You can’t use it directly, however, since the methods are defined specifically for the `RGB` type. Even though `VectorRGB` has the exact same structure as `RGB` and even supports all the same methods that do all the same things (remember, I only wanted to _add_ methods, not change the existing stuff), they are different types and not interchangeable: if some method returns `RGB` values, you cannot apply methods for `VectorRGB` objects to them. You have three options:

1. You can petition the author of the code to change it to handle `VectorRGB` as well as `RGB`; they many not want to do this, since it’s an unnecessary complication.

2. You can create a shim method for `VectorRGB` that converts the `VectorRGB` value to `RGB`, calls `palette` on it and then converts the returned colors back to `VectorRGB`. This works, but you have to do it for every function like `palette`.

3. You can copy the code and duplicated it for the `VectorRGB` type and part ways with the original author.

None of these is that bad, but you have to do this for every bit of `ColorTypes` functionality that you might want to use. It’s also funny how OOP, which is supposed to be this great enabler of code reuse and sharing, keeps pushing us towards just making our own copy of someone else’s code and modifying it 🤔.

The `palette` method is actually quite simple, so adding support for `VectorColorant` subtypes isn’t that complicated. In more complex situations where color types are consumed or constructed, adding support for different subtypes leads to a proliferation of complex OOP patterns in real world code bases, like the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern), the [abstract factory pattern](https://en.wikipedia.org/wiki/Abstract_factory_pattern), the [factory method pattern](https://en.wikipedia.org/wiki/Factory_method_pattern), the [prototype pattern](https://en.wikipedia.org/wiki/Prototype_pattern), etc.

How does all of this work out in Julia with multiple dispatch? All you have to do is define methods like this in your own code:

```julia
add(c1::RGB, c2::RGB) = RGB(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b)
norm(c::RGB) = norm((c.r, c.g, c.b))

```

That’s it. You don’t have to convince anyone of anything. You don’t have to copy their code. You can add methods at whatever level of the abstraction hierarchy is appropriate. It doesn’t even matter if other people want to use the same names for methods because if you define an `add` function in your code and they define a different `add` function in their code, these are separate function objects in different namespaces and there’s no issue at all. In OOP, the namespace of methods is global and shared, so method name collisions are a problem. The key reason all this is better with multiple dispatch is that, unlike OOP where methods are defined inside of classes, methods belong to generic functions and are defined outside of the types they apply to — potentially not even in the same code base. Each user of a type can add methods as they please, which other users can reuse or ignore as they prefer.

So even if you never realized you encountered the expression problem because you didn’t have a name for it, you almost certainly have. That time you wanted to just add one teensy tiny method to someone else’s class but couldn’t. That was the expression problem. That time you discovered that your favorite OOP language’s ecosystem has two or more non-interoperable packages for some subject and you need some features from both of them but can’t use them together. If not for the expression problem, they could share a core set of simple types while extending them with different methods on top of that shared core. That time you were tearing your hair out trying to understand what the [`SimpleBeanFactoryAwareAspectInstanceFactory`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/config/SimpleBeanFactoryAwareAspectInstanceFactory.html) class is and how the heck you’re supposed to use it. That was also the expression problem: if people weren’t forced to subclass just to add a few methods, they could just share concrete types instead of needing so many layers of abstraction. Despite seeming so different, these are all manifestations of the same core problem in OOP languages, a problem which multiple dispatch solves simply and elegantly, thereby eliminating all these issues at once. The effectiveness of this solution is borne out by the unusually high degree of code reuse and sharing we actually see in the Julia ecosystem.

---

_[View the full topic](https://discourse.julialang.org/t/is-julias-way-of-oop-superior-to-c-python-why-julia-doesnt-use-class-based-oop/52058)._
