About inheritance and abstract types

Ah thanks - somehow I misread that this was in Base. I use a similar macro and also find it very useful and clear

It was in base but went to the Graphics package. Actually it does not really belong there but the discussion on interfaces in Base have kind of stopped.

If you want to also “inherit” the behaviour, you can define a fallback method for the abstract type (rather than an error), and override only when required for concrete sub-types.

abstract type Curve end
get_pos(c::Curve) = c.pos

type Line <: Curve
    pos::Float64
end

type Arc <: Curve
    pos::Float64
end
get_pos(arc::Arc) = arc.pos + 1

type Circle <: Curve
    centre::Float64
end

get_pos(Line(2.5))      # use default fallback
get_pos(Arc(2.5))       # use Arc-specific method
get_pos(Circle(2.5))    # ERROR: type Circle has no field pos

One advantage to the Julia approach is dealing with degeneracy. For example, the point (1.0,1.0) is a 0-dimensional curve that could be represented as:

type One <: Curve end
get_pos(one::One) = (1.0, 1.0)

In this example, the savings are trivial, but in general it allows for degenerate subtypes of an abstract supertype to be as light as they can be. If a family of types were to contain a matrix, then it’s reasonable for a degenerate subtype to instead have the identity, I, that doesn’t need to be stored in every concrete instance of the degenerate subtype.

1 Like

On that note, how is it possible to get something common to all types extending the abstract type. For example, if I have a field called type::String. Do I need to implement get_type in every subtype? Is it possible to have a common get_type defined at the base (abstract or not abstract) level.

get_type(curve::BaseType) -> "curve", "straight-line", "circle" depending on how it was constructed?

If all subtypes have the field, then

get_type(curve::BaseType) = curve.type

works. And if some subtype then goes and puts that info elsewhere, then you can special-case it:

get_type(curve::SomeSubType) = curve.some_other_field
3 Likes

if all subtypes have the field

I think the above is key. Thanks for the answer, that’s exactly how I’m addressing that, but code duplication grows pretty quickly without inheritance.

It might be possible to make a macro that makes Julia act as if it had inheritance. The rough idea would be to store a dict keyed on abstract types who’s values were vectors of fields to be inherited.

There are quite a few of those out there, many are listed in this thread:

1 Like

If the duplication comes from repeating the fields, consider using composition.

4 Likes

I think that this guide is useful for users coming to Julia with OO mindset:

Also you may want to use this package for your specific question:

1 Like

There is also https://github.com/mauro3/OO.jl :wink:
(please don’t use it!)

3 Likes

if the duplication comes from repeating the fields, consider using composition.

Thanks for the input. My 2 cents is that Julia can benefit from OO if we want to use it in large projects. My understanding is that composition, while nice, still doesn’t save from code duplication in all derived types.

More than the mindset, it is the practical aspect of maintaining large code bases. Many projects are switching from Fortran to C++ in my field due to the multiparadigm and modern features availability. While I like Julia and what it offers, it is still hard to sell the alternatives to OO as it is very similar to Fortran. type dispatch is great, but I think it’s orthogonal/complementary to inheritance. I’d still use Julia for small projects, though.

Thanks you guys for the responses. My personal opinion of Julia is that it is great and the main benefit over Python for the kind of work I do is “static types”, the wonderful language built-in capabilities and the unified package distribution, Project.toml is a great idea. My curiosity about OO is that I’ve seen large code bases in C and Fortran essentially creating their own OO dialects as they grow in size and complexity. My 2 cents is that it would be nice to have it in the language.

Sorry, I don’t see the connection between OO and programming in the large. Maybe you can elaborate?

IMO what matters for large codebases is compartmentalization of functionality into smaller units that one can reason about. It is true that some languages use OO (in combination with other things, like namespaces) to achieve this. Julia doesn’t, which is an intentional design choice. Multiple dispatch is more expressive than single-dispatch OO.

A concrete example could be helpful to focus this discussion.

3 Likes

My impression is that many people with an OO background seeing Julia for the first time think that the missing OO features are a show-stopper. However, I don’t think we see any such complaints from long term users. That could either mean that the OO-background-people left again or that the programming paradigm of Julia is good after all and there is nothing to complain. I’m pretty sure it is the latter.

Edit: So, my recommendation is to embrace Julia’s model (e.g. look at good packages) and see what you think instead of trying to port your OO habits to Julia.

6 Likes

Sorry, I don’t see the connection between OO and programming in the large. Maybe you can elaborate?

No problem. I was referring to the legacy of large extensible code bases scientific data interacts with (which Julia targets). Think of VTK, OpenFoam, MFEM, MOOSE, ParaView, Slate, the next generation of Scalapack, I can go on and on with this list. Their choice is to organize using OO architecture. Julia essentially implements the Fortran model that frameworks are moving away for better organization and due to the demands for new capabilities in a timely manner.

Multiple dispatch is more expressive than single-dispatch OO.

Yes, this is why I consider them complimentary/orthogonal concepts wrt inheritance. Inheritance allows reusability that multiple dispatch doesn’t. These are just different hammers for different nails.

A concrete example could be helpful to focus this discussion.

I provided an example in my first entry for get_type(curve::BaseType) , the mentioned alternatives mean that get_type must be implemented in all derived types, while with inheritance a single function is sufficient as every derived class is-a (inheritance) rather than has-a (composition).

Personally, I’m not a OO advocate myself just curious about the Julia roadmap on the topic (programming languages are tools at the end of the day). I’ve just seen over the years how many scientific frameworks chose OO as an organizational model (list above). If this is coming to Julia, it would be an easier sell, that’s all.

Thanks, good conversation.

@mauro3 It’s not my “OO” habits or my “OO”-background (people can have multiple paradigm backgrounds). I’d say I’m comfortable with Julia’s paradigm (reminds me of Fortran), but I also see the value of OO which many framework do see as well. Just providing my feedback based on what I see. You’re welcome to take it or not. Julia is a relatively young language, I think feedback from community users and good relations is key to its success in the long-run.

As much as I like Fortran (95), I think Julia is pretty far from it. I like the Julian way of writing code, much more so than OO, but I certainly took a bit of time to get used to it. Also, Julia is at v1 now, so large changes to the language are probably not going to happen.

Last, note that the examples you state above, except Slate, are all “old” (10+ years), when OO was still en-vouge. I think this has now changed quite bit. And it seems that in Julia it is much easier to write composable software than in many other languages, thanks to multiple dispatch: https://www.youtube.com/watch?v=kc9HwsxE1OY

Anyway, I think you just need to develop a few 1k locs of code to see if you like it.

2 Likes