Inheritance in Julia

Hello, I´m new in Julia language. I´m thinking to write a framework that allows me to work with evolutionary algorithms. I come from Java language and the first idea that I had was to create a type hierarchy. I´m using Julia 0.6.

Just for illustrating what is my problem. I have the following type that represents an individual of the population.

struct Individual{T}
  chromosome::Vector{T}

  #Inner constructor
  Individual{T}(ngenes::UInt) where T<:Number =  new{T}(Vector{T}(ngenes))
end

Suppose that I want to create a composite type that inherits of Individual, for instance, an individual having a binary or real representation, in such a way that in the new composite type I would not need to declare the chromosome field. For instance

struct BinaryIndividual <: Individual

  #Inner constructor
  BinaryIndividual(ngenes::UInt) = new{Bool}(ngenes)

end

I have tried several manners to do this, but I always got the following error:

invalid subtyping in definition of BinaryIndividual.

The only way that I have found is declaring Individual as an abstract type, but in this case, Julia does not allow me to put any field into the abstract type, therefore I must to put the chromosome field into each composite type that inherits of Individual, and this is not actually a good OOP practice.

My question is: Is there a way to do this in Julia?,

Thanks.

–Oscar

3 Likes

You cannot inherit from concrete types.

Julia is not really a OO language. Perhaps you can use composition, or alternatively look at some macro that do the “field inheritance” for you like GitHub - tbreloff/ConcreteAbstractions.jl: Concrete type inheritance for Julia.

2 Likes

No, this was a specific design decision in Julia not to implement that kind of inheritance. From https://docs.julialang.org/en/stable/manual/types/ :

One particularly distinctive feature of Julia’s type system is that concrete types may not subtype each other: all concrete types are final and may only have abstract types as their supertypes. While this might at first seem unduly restrictive, it has many beneficial consequences with surprisingly few drawbacks. It turns out that being able to inherit behavior is much more important than being able to inherit structure, and inheriting both causes significant difficulties in traditional object-oriented languages.

I moved from Python to Julia and really missed this feature…for a while. It turns out that composition works better than inheritance for my use cases, and as I’ve gotten more used to the Julia way of doing things I find that I don’t want concrete type hierarchies any more.

@ChrisRackauckas has a nice blog post about one way of structuring code in a more Julian way: Type-Dispatch Design: Post Object-Oriented Programming for Julia - Stochastic Lifestyle

3 Likes

[citation needed]

6 Likes

11 posts were split to a new topic: Comments on tone of [citation needed]

Hi @ogreyesp,

I hope the reply by @rdeits and the link to @ChrisRackauckas’s blog post were helpful. When I came to Julia, this was one of the first things I bumped into, but (just like everyone else) I quickly found that changing around the way I thought about the problem (dropping OOP) and instead working with multiple dispatch and some abstract types made my problems far easier, and I haven’t missed OOP at all since. In fact, I went to the Julia convention last June, and your question was the first thing a guy sitting next to me asked. He was fluent in a ton of languages and had some deep OOP setups, but after playing with different representations of his problems in Julia, he said that he was done with OOP, as the Julian way was far easier and more powerful. I don’t know if that would be the case for your problem, but maybe it’s worth a shot.

Perhaps since this is such a common question, we could put some additional examples in the documentation, or even link to Chris’s post? I know I always need more example, and as I get better at Julia, I’ll start contributing some.

(Also, I’m super sorry that your thread has turned into a discussion about whether or not someone was being rude rather than answering your question. This community works impressively hard to remain positive, with the result that if a comment from an old-timer might not be positive, he gets jumped on by everyone else as they defend the newcomer. I hope you’ll read through more posts and find that this Discourse is an amazingly useful resource full of people that want to help; I know that it is for me.)

11 Likes

Hello,

Certainly, it was not my intention to begin a debate about what is better, composition or inheritance, therefore I encountered pointless to put a citation about this. Yes, I come from Java language (almost 15 years in this language), that is a different paradigm, and I still learning the ways Julia does the things. I just wanted to know if exist a Julia way to resolve the problem that I have. And yes…, I believe that Julia is great!

As for Stefan response, maybe I’m used to this type of response due to the publication process in journals. Althought, I think that this type of response is more appropriated for specific threads, for instance, threads about the language design, and not a proper comment for a thread named “First Steps” in Julia.

Best regards

1 Like

Yeah, wow, I really wasn’t expecting this to go so far off the rails. Sorry this had to be one of your early julia-community experiences.

Anyway, I am happy to talk about the ways I’ve made Julia work for the actual problems I want to solve (and I’m sure others here are too), and, in particular, the ways that multiple dispatch have let me do things that actually weren’t possible in an OO framework. Just let us know if you have any more questions.

1 Like

This is in fact what makes me most happy about Julia - I worked in C++ for some years earlier in my life, and the answer to every problem always started with developing a type hierarchy. I always found that surprisingly complex :slight_smile: (of course it might not be for you, as someone very used to it).

BTW you may want to check out BioJulia · GitHub

@ogreyesp

I actually find the composition as a solution to a similar problem is that you describe quite attractive in my case.
I write methods for the abstract type with the assumption that the concrete types will have a certain field
to hold the data. The composition can be done quite nicely with macros. For instance:

I have a macro to define the fields of a concrete type:

macro add_FESet_fields()
  return esc(:(
  nodesperelem::FInt;
  conn::FIntMat;
  label::FIntVec; )
  )
end

I have a couple of abstract types:

abstract type FESet end
abstract type FESet1Manifold <: FESet end

Then I can create a concrete type:

mutable struct FESetL2 <: FESet1Manifold
  @add_FESet_fields
end

The methods are defined for the abstract types, for instance:

function subset(self::T, L::FIntVec) where {T<:FESet}
  result = deepcopy(self)
  result.conn = deepcopy(self.conn[L, :])
  result.label = deepcopy(self.label[L])
  return  result
end

Note that it refers to the fields of the struct for the concrete type.

4 Likes

@ogreyesp quite separate from your original question and echoing @mkborregaard, you should chat with @Ward9250 about this - he’s done a ton of work in Bio.jl precisely on the topic of genetic variation in an evolutionary framework. Your work might overlap, or fit nicely into the gaps of what he’s doing.

And come join us in the Bio.jl gitter channel - always happy to see new biologists using Julia!

A few thoughts that might help you.

Multi-inheritance in Julia can be achieved sort of…

abstract type Indicators end
struct TypeA <: Indicators end
struct TypeB <: Indicators end

abstract type Basic end
mutable struct myobj <: Basic
    First::Float64
    Second::Indicator
    function myobj()
        new()
    end
end
function makeobj(obj::Dict{Symbol,Any})
    output = myobj()
    for key in keys(obj)
        setfield!(output, key, obj[key])
    end
    return output
end
function operation(obj::Basic)
    operation(obj, getfield(obj, :Second))
end
function operation(obj::Basic, indicator::TypeA)
    #dosomething
end
function operation(obj::Basic, indicator::TypeB)
    #dosomethingdifferent
end

This allows you to have one struct and supports undefined attributes. It also allows you to have multi-path inheritance using an attribute indicator. Hope this helps.