Real-world problems aren’t well-suited for inheritance-based OOP either, which is why you’re generally told/taught to not use inheritance in OOP, except in your first undergrad class where you learn about OOP and program design. Here’s three links discussing why considered using these features is considered bad practice (even in OOP languages).
So that begs the question, if every style guide is against inheritance and for composition, why do people push so hard to have inheritance implemented when composition comes very natural to Julia through multiple dispatch?
Let me elaborate on that last part a bit. Say you want
type B <: HoldsAnA
a::A
end
to act like an A
in many places. Then you can f(b::B) = f(b.a)
and dispatch handles the rest (and implicit returns makes that function that easy). You can even make an entire “class” of types “inherit” this ability via f(b::HoldsAnA) = f(b.a)
.
You can do multiple inheritance of behavior via traits. For more information on that, check out SimpleTraits.jl. Essentially, you can make some type have a trait, and just dispatch functions on that trait. You can use that to make functions give a value:
@traitfn islinear(x::::IsLinear) = true
@traitfn islinear(x::::(!IsLinear)) = false
so instead of looking for “an inherited field” x.islinear
, this is “an inherited function call” islinear(x)
… but guess what? Unlike a field of a mutable object, this is known at compile time and thus results in faster code!
Now lets say you got to here and are like, well I still want inheritance. Well, you can take the 10 minutes to make it yourself. A poor man’s way is just via @def
:
macro def(name, definition)
return quote
macro $(esc(name))()
esc($(Expr(:quote, definition)))
end
end
end
You can use this macro to do compile-time copy/paste, which is essentially what inheritance of fields is. So you can do:
@def the_fields begin
x::Float64
y::Float64
end
type A
@the_fields
z::Float64
end
type B
@the_fields
end
and there you go. What if you want to go all of the way? Here’s a good introduction to meta-programming project: implement this design:
@inheritable X{T} begin
x::{T}
y::Float64
end
@inherits type A
X
z::Float64
end
@inherits type B
X
end
The tricky part is making it produce A{T}
(but make it general enough so you can have X{T}
with z::T
and get A{T,T2}
. Hint: use gensym
), but all of the information is there. So if you still really Really REALLY need OOP-style inheritance, there you go: it’s a fun weekend project.
This question gets asked enough that this response might need to be a blog post so I can just paste it around.