# Composition and inheritance: the Julian way

**URL:** <https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231>\
**Category:** General Usage\
**Tags:** inheritance, structtypes\
**Created:** [May 29, 2018, 1:06pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231 "2018-05-29T13:06:00Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 30, 2018, 11:50am UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/21 "2018-05-30T11:50:47Z")

</div>

> [@DNF](#):
>
> “Something _exactly_ like (a concrete) `Vector` , with a twist.” Right now, that means reimplementing or at least forwarding a _lot_ of methods. Can this be automated?

I understand what you are trying to do, I am just arguing that it is not an approach that meshes well with Julia, or leads to good interface design.

First, I think that if you want an _interface_, you should define an _abstract type_ to go with it. This is costless in terms of performance, and allows you do document the interface in the docstring of the abstract type.

Second, you should _minimize_ the functions that need to be implemented for this interface, ideally by choosing a core and then some extra methods that use this core but can be overridden for performance/implementation reasons. This is what `AbstractArray` does, and Julia’s compilation model makes this costless (in terms of runtime) in most cases, because of specialization and inlining (and lately, constant propagation).

IMO an interface which violates these is not good design. Interfaces should not be associated primarily with concrete types, nor should they be very rich. Either will cause problems independently of forwarding methods. That said, designing good interfaces is an difficult iterative process; frequently they emerge from functions of concrete types, and similarly, get streamlined by refactoring.

---

<div class="post-metadata">

**Author:** ![traktofon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/traktofon/32/591_2.png) [@traktofon](https://discourse.julialang.org/u/traktofon)\
**Post date:** [May 30, 2018, 12:11pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/22 "2018-05-30T12:11:57Z")

</div>

> [@Tamas\_Papp](#):
>
> They are [documented](https://docs.julialang.org/en/latest/manual/interfaces.html#man-interface-array-1).

This link seems broken now, [this one](https://docs.julialang.org/en/latest/manual/interfaces/#man-interface-array-1) works for me.

---

<div class="post-metadata">

**Author:** ![ScottPJones](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/scottpjones/32/146_2.png) [@ScottPJones](https://discourse.julialang.org/u/ScottPJones)\
**Post date:** [May 30, 2018, 12:25pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/24 "2018-05-30T12:25:33Z")

</div>

> [@Tamas\_Papp](#):
>
> abstract types are good for designating interfaces, you can also make a hierarchy of them, keeping [substitutability](https://en.wikipedia.org/wiki/Liskov_substitution_principle) in mind,

I’ve been using abstract types for broad classification (i.e. `AbstractString` & `AbstractChar`), but for the rest, I don’t make hierarchies of abstract types, I’ve found traits and parameterized concrete types much more useful for writing generic code and having a consistent API. Most of the things about strings and characters are orthogonal to one another (Mutable or not, validated or not, single- or multi- codeunit encoding, ASCII compatible or not, ISO compatible or not, Unicode subset, full Unicode, Unicode + invalids (i.e. `Char`), or not Unicode compatible, etc.)  
Trying to make that into a hierarchical structure explodes the number of types to deal with.

Also, instead of forwarding everything, you can use a method to access the Person from whatever type you have that includes person, and then call the person specific methods directly on it, which to me is clearer.

```julia
struct Person
    name::String
    gender::String
    birthday::String
end
person(p::Person) = p

struct Citizen
    person::person 
    nationality::String
end
person(c::Citizen) = c.person

```

Then you don’t have to add methods for every field, just one, instead of `name(c)` you’d have `person(c).name` or `name(person(c))`

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [May 31, 2018, 2:04pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/25 "2018-05-31T14:04:17Z")

</div>

Sorry for the absence… I will comment on individual posts below:

> [@Tamas\_Papp](#):
>
> If this is still about `Lazy.@forward` , I suggest you look at the macro and its documentation before further discussion. It is precisely for the composition example we are talking about.

Sorry but I don’t understand why it should be related to be composition example. The `Lazy.@forward` **requires** encapsulation (i.e. approach 1 and 2 in the first post), not composition (approach 3). You will forgive me if _encapsulation_ and _composition_ are not the right words (again, I’m not a CS…), I use them just to refer to the approach discussed in the first post.

> [@DNF](#):
>
> It just seems like the “composition over inheritance” argument would be easier to make if there were a way to efficiently cut all the boilerplate.

Completely agreed. Besides, the boilerplate code may add lots of entries in the dispatch table, without actually adding any _new_ functionality. Moreover, all the boilerplate code, even if automatically generated, must be compiled resulting in further waste of time.

Thank you @Raf for pointing out Mixers.jl, it quickly allows to add further fields to structures without listing again the previous ones. This goes exactly in the direction of aproach 3 in the first post.

> [@Tamas\_Papp](#):
>
> 1. consider simplifying the interface (too many verbs),

You can’t. If you want to add a _twist_ to (e.g.) `Vector`, you simply can’t because you didn’t wrote the interface.

Thank @marius311 for providing a list of rules. Following your example I will provide my proposal, which is actually very similar to yours (except for the `AbstractCitizen` type):

1. define an abstract type as supertype for each struct, e.g. define, `Person <: AbstractPerson`, `Citizen <: AbstractCitizen`, etc. In OOP terminology, the structures will be the data members of a **class** , and the abstract types will be the **interfaces** ;
2. define the hierarchy among structures using abstract types (not concrete types), e.g. `AbstractPerson <: AbstractCitizen`. This will allow us to define hierarchy in the same way as we do it in OOP;
3. define sub structures by repeating the fields of the fields of the parent structure, in the same order and with the same types. This step can be automatized with Mixers.jl by @Raf;
4. all methods working on a structure must accept the corresponding abstract type, not the concrete one. In OOP terminology these are the **methods** associated to a class;
5. the only exception to the rule 4 above are the constructors, and those methods returning a specific structure (either `Person` or `Citizen` in the example above). These methods must accept concrete types, not abstract ones;
6. define a _super_ method like `super(p::Citizen) = Person(p.name, p.age)` (the name is not mandatory…) to be used in all methods of the derived structures to access the corresponding method acting on the parent structure.

I would like to emphasize that the goal here is not to reproduce an OOP practice in Julia, I know it is not possible and will likely lead to troubles. Here we only want to **simply and efficiently** extend/customize/specialize (use the word you prefer…) the behaviour of a Julia object whose methods have been written by someone else.

In my opinion this simple list of rules allows to easily extend the functionality of any object, and use it (quoting @DNF) **exactly like the concrete object, with a twist**. Moreover, even if the object is not supposed to be extended it will not harm its development or performances, and I can’t think of any practical reason to avoid following them. Any counter example here is more than welcome.

> [@DNF](#):
>
> But it’s difficult to apply when the object you want to wrap is pre-existing

Well, if we agree that the above rules are to be followed, the only feasible way is to ask the object developer to adapt its implementation according to the rules… 😉

Two final comments:

- the very fact that we are discussing what is the best way to _wrap_ an object into another to slightly customize its behavour, without adding lot of boilerplate code (either written or generated with macros), implies there is not yet a standardized and commonly accepted way to do it. Or, at least, I am not aware of it (again, any advice here is very welcome);

- many of us, certainly myself, are struggling to understand whether we can use Julia as the main tool for our daily work. Hence, we **need** to know whether Julia has some intrinsic limitation. The impossibility to customize the behaviour of an object is (in my opinion) a strong restriction.

---

<div class="post-metadata">

**Author:** ![ScottPJones](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/scottpjones/32/146_2.png) [@ScottPJones](https://discourse.julialang.org/u/ScottPJones)\
**Post date:** [May 31, 2018, 2:27pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/26 "2018-05-31T14:27:34Z")

</div>

> [@gcalderone](#):
>
> many of us, certainly myself, are struggling to understand whether we can use Julia as the main tool for our daily work. Hence, we **need** to know whether Julia has some intrinsic limitation. The impossibility to customize the behaviour of an object is (in my opinion) a strong restriction.

I’m struggling to understand that it’s somehow impossible to customize the behavior of an objects.  
I’ve been use only Julia (even for my low-level work) for over 3 years now, and haven’t had any problems customizing the behavior of anything. I’m partial to using traits these days, because they end up very efficient, and allow me to deal efficiently with many orthogonal traits, and be able to add new traits later on, and new types using those traits, instead of having to change some hierarchy of abstract types.

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [May 31, 2018, 2:39pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/27 "2018-05-31T14:39:36Z")

</div>

> [@ScottPJones](#):
>
> I’m struggling to understand that it’s somehow impossible to customize the behavior of an objects.

well, you know where all this discussion [comes from](https://discourse.julialang.org/t/how-to-add-metadata-info-to-a-dataframe/11168/) 😉. Extending the behavior of, say, a `DataFrame` object is not trivial at all…

It would be nice if you could provide an example which solves the Person/Citizen problem in the first post using traits. Please 🙏 avoid encapsulation since on a real example (e.g. a `DataFrame`) this means re-defining hundreds of methods…

---

<div class="post-metadata">

**Author:** ![piever](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/piever/32/1815_2.png) [@piever](https://discourse.julialang.org/u/piever)\
**Post date:** [May 31, 2018, 2:57pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/28 "2018-05-31T14:57:32Z")

</div>

To be honest, if you are trying to solve the problem linked above (define a `DataFrame` with a twist), it seems that one issue is the lack of a “tabular data interface”. It’s hard to believe that the hundreds of methods defined for DataFrames are all necessary: I imagine it’d be possible to write most of them as a function of a much reduced “table interface” (see what e.g. Query does, getting everything to work for every iterable of named tuples).

A very good solution in my view would be finalizing this tabular interface and rewriting things in function of it (see for example [here](https://github.com/JuliaStats/StatsModels.jl/pull/57) for an attempt at porting StatsModels to this design).

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 31, 2018, 2:58pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/29 "2018-05-31T14:58:15Z")

</div>

> [@gcalderone](#):
>
> Please 🙏 avoid encapsulation

This should be fairly easy, as Julia does not support [encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) as it is usually understood (= restricting access to some slots) 😉

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [May 31, 2018, 3:02pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/30 "2018-05-31T15:02:54Z")

</div>

> [@Tamas\_Papp](#):
>
> as Julia does not support [encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) as it is usually understood

OK, I have to apologize for my ignorance on CS terminology, I am an astronomer struggling to face a CS problem…

Could you please provide a name for this:

```julia
struct Person
    name::String
    gender::String
    birthday::String
end
person(p::Person) = p

struct Citizen
    person::person 
    nationality::String
end

```

Whatever the name is, I kindly asked to avoid exactly **that**. Thanks!

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 31, 2018, 3:07pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/31 "2018-05-31T15:07:06Z")

</div>

> [@gcalderone](#):
>
> If you want to add a _twist_ to (e.g.) `Vector` , you simply can’t because you didn’t wrote the interface.

I don’t know what you mean by a “twist”. But if you want composition and automatic forwarding of _all_ functions that would emulate inheritance, that is indeed not possible. Many people in OO argue that composition is preferable to inheritance, but if you are unwilling to accept that, then you may find Julia difficult.

As @ScottPJones said, traits buy you almost all the benefits of inheritance, without the usally recognized problems. But you still have to implement some functions for the interface, and extend/change your implementation if the interface changes. There is still no automatic forwarding.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [May 31, 2018, 3:09pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/32 "2018-05-31T15:09:31Z")

</div>

So that I have this clear, the goal would be to have few methods act on the concrete type `Person`.

Rather, the writers of the “Person” package should have `Person` be an abstract type and define a set of methods that _only operate on that abstract type_. This list of methods should be both short and well-documented. That way, when you define a concrete type, it should be very easy to get your new concrete type “up to speed” so that it fully implements the abstract type.

DataFrames does this with the `AbstractDataFrame` abstract type, but as you noted, many methods in `DataFrames` operate on the concrete type `DataFrame`. To the maintainers of `DataFrames`, would a good goal for PRs be to track down those methods and see if they could be better implemented on `AbstractDataFrames` instead of `DataFrames`?

---

<div class="post-metadata">

**Author:** ![ScottPJones](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/scottpjones/32/146_2.png) [@ScottPJones](https://discourse.julialang.org/u/ScottPJones)\
**Post date:** [May 31, 2018, 3:14pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/33 "2018-05-31T15:14:26Z")

</div>

> [@Tamas\_Papp](#):
>
> This should be fairly easy, as Julia does not support [encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) as it is usually understood (= restricting access to some slots) 😉

Well, as that article notes later on, languages like Julia, with lexical closures, are generally more concerned with the other part to encapsulation (i.e. data abstraction) which is really orthogonal to the restricted access issue.

I would myself prefer to have the capability in Julia of declaring some things (functions, types, or fields), as being public (part of an API), I’ve found when dealing with code over long periods of time (decades!), with lots of people accessing it, modifying it, basing things on top of it (hundreds of thousands), it is very important way of helping to produce robust code.

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [May 31, 2018, 3:33pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/34 "2018-05-31T15:33:32Z")

</div>

> [@piever](#):
>
> A very good solution in my view would be finalizing this tabular interface and rewriting things in function of it

Yes, it is a possible solution, but it would requires some effort from the `DataFrames` manitainers.  
A simpler and quicker approach (in my very humble opinion…) would be to ask the maintainer to follow some rules like the ones we outlined above…

Still, the simplified tabular data interface appears very interesting. Of course the best would be to follow the rules for easy inheritance, and to implement the simplified interface. 😉

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [May 31, 2018, 3:41pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/35 "2018-05-31T15:41:19Z")

</div>

> [@Tamas\_Papp](#):
>
> Many people in OO argue that composition is preferable to inheritance, but if you are unwilling to accept that, then you may find Julia difficult.

I do not have enough knowledge to disagree, and you see I’m trusting Julia (and its developers) a lot.

I simply would like to:

- extend `DataFrame` to obtain a new object called `Foo`;
- use `Foo` in exactly the same way I would use ` DataFrame` object;
- re-defining **ONLY** those methods for which the `Foo` behaviour differs from the `DataFrame` one, which in the actual case are 3 or 4 (while the `DataaFrame` object is accepted by hundred of method);
- do it by myself without asking the `DataFrames` maintainers to change something in their package.

I actually don’t care what is the name of this operation (inheritance, composition, enacpsulation, specialization…), I only would like to know if/how it is possible.

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [May 31, 2018, 3:43pm UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/36 "2018-05-31T15:43:34Z")

</div>

> [@pdeffebach](#):
>
> To the maintainers of `DataFrames` , would a good goal for PRs be to track down those methods and see if they could be better implemented on `AbstractDataFrames` instead of `DataFrames` ?

That would really be a good start. Is there any `DataFrames` maintainer here?

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [June 1, 2018, 2:42am UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/37 "2018-06-01T02:42:11Z")

</div>

This is a rule I’m going to take on from now on: methods in shared packages should _always_ dispatch on abstract types. Never on concrete types.

It really wasn’t entirely clear to me before this. It should be in the docs in bold all caps somewhere. Dispatching on concrete types is locking in implementation details and making lots of useless replication for anyone trying to extend types.

Wouldn’t a quick (but manual) search and replace to swap out DataFrame for an abstract type resolve this in DataFrames?

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [June 1, 2018, 7:56am UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/38 "2018-06-01T07:56:43Z")

</div>

> [@Tamas\_Papp](#):
>
> Many people in OO argue that composition is preferable to inheritance…

I think this isn’t a hard and fast rule. Both inheritance and composition are available in OO languages; the trick is to apply the right solution to the domain model.

There are some good insights from [Composition vs. Inheritance: How to Choose? | Thoughtworks](https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose)

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [June 1, 2018, 8:04am UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/39 "2018-06-01T08:04:18Z")

</div>

> [@Raf](#):
>
> This is a rule I’m going to take on from now on: methods in shared packages should _always_ dispatch on abstract types. Never on concrete types.
> 
> It really wasn’t entirely clear to me before this. It should be in the docs in bold all caps somewhere. Dispatching on concrete types is locking in implementation details and making lots of useless replication for anyone trying to extend types.

Yup, or even just let anything go.

> **[Type-Dispatch Design: Post Object-Oriented Programming for Julia - Stochastic...](http://www.stochasticlifestyle.com/type-dispatch-design-post-object-oriented-programming-julia/)**
>
> In this post I am going to try to explain in detail the type-dispatch design which is used in Julian software architectures. It’s modeled after the design of many different packages and Julia Base, and has been discussed in parts elsewhere. This is...

---

<div class="post-metadata">

**Author:** ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)\
**Post date:** [June 1, 2018, 8:08am UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/40 "2018-06-01T08:08:23Z")

</div>

> [@Raf](#):
>
> Wouldn’t a quick (but manual) search and replace to swap out DataFrame for an abstract type resolve this in DataFrames?

Maybe. But I am not sure if it can have any consequence, or hide subtle errors. I (want to) believe there is a reason to define `getindex(df::DataFrame, col_ind::ColumnIndex)` (as they did), in place of `getindex(df::AbstractDataFrame, col_ind::ColumnIndex)`.

Moreover, `SubDataFrame` is defined as follows:

```julia
struct SubDataFrame{...} <: AbstractDataFrame
    parent::DataFrame
    rows::T 
end

```

i.e. it doesn’t follow what we called [_rule 3_](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/25).

Maybe there is a specific design motivation that I don’t understand…

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [June 1, 2018, 8:10am UTC](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/41 "2018-06-01T08:10:51Z")

</div>

> [@gcalderone](#):
>
> it is a possible solution, but it would requires some effort from the `DataFrames` manitainers

Certainly. But the great thing about the this is that _you_ could become a contributor just by making a PR, helping develop an API you care about. Pull requests that clean up interfaces are usually well-received and you get lots of feedback.

[Previous page](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231.md?page=1)

[Next page](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231.md?page=3)
