There’s something about Julia style that I’m just not getting. Let’s say I want a Curve type for a curve in the complex plane. It’s meant to be abstract, since I will have different subtypes of Curve (Line, Circle, etc). But all objects of Curve type should have a pos field that gives the position z as a function of a parameter. This is all that’s needed for a lot of Curve functionality.
In a purer OOP language I’d have Curve as an abstract class that has a pos property and methods that work on all Curves. All those methods know that the object has a pos property, even though the class is abstract.
In Julia, if I have an abstract Curve type and methods that operate on all Curves, those methods will need to use the pos property. Every concrete subtype of Curve then has to be responsible for declaring a pos property, rather than having it be guaranteed as part of Curve. That’s a recipe for mayhem.
Essentially I’m looking for the concept of an interface. What is the Julia equivalent? Or does Julia have another way to look at this?
With the ‘interface’ style, you might be looking for something like
abstract type Curve end
get_pos(c::Curve) = error("No get_pos method defined for curve type $(typeof(c))")
type Line <: Curve
pos::...
end
get_pos(line::Line) = line.pos
do_something(c::Curve) = get_pos(c) + ...
and you’ll get an appropriate run-time error if a given Curve subtype doesn’t conform to the implicit get_pos interface. There is discussion around adding language-level support for a formal interface concept in a future release of Julia.
Its also not just shorter but IMHO it reads really nice and makes the code self documenting. I use this a lot to program against defined interfaces and it works pretty well.
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.
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?
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.
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.