I am a C++ and Python user and quite accustomed to OOP for some tasks. The following is a small example of using OOP style in Julia. Please give comments if there are better ways to do that. Thank! Hope it could help the users have the similar demand.
# animal.jl
abstract type AbstractAnimal end
mutable struct BaseAnimal
name::String
weight::Float64
BaseAnimal() = new("", 1.)
end
function get_weight( animal::AbstractAnimal )
return animal.base.weight #no member variable in AbstractAnimal
end
#file2, dog.jl
mutable struct Dog <: AbstractAnimal
base::BaseAnimal
owner_name::String
Dog( name::String ) = new( BaseAnimal(), name)
end
function get_owner_name( dog::Dog )
return dog.owner_name
end
#file3 cat.jl
mutable struct Cat <: AbstractAnimal
base::BaseAnimal
climbing_speed::Float64
Cat( speed::Float64 ) = new( BaseAnimal(), speed )
end
function get_climbing_speed( cat::Cat )
return cat.climbing_speed
end
#file4 OOPTest.jl
module OOPTest
export Dog, Cat,
get_climbing_speed, get_owner_name, get_weight
include("animal.jl") #it must be put before cat.jl and dog.jl, or there is error
include("cat.jl")
include("dog.jl")
end # module
#file5 runtest.jl
using OOPTest
dog = Dog("Tom")
cat = Cat(10.)
@show get_owner_name( dog )
@show get_climbing_speed( cat )
@show get_weight( dog )
@show get_weight( cat )
This is not so convenient to access the member variable of the base class. The user should remember which variable belongs to the parent class and use base variable to access the parent’s variable. It made extra workload for the user. For the up mentioned example, the user should access the member variable of dog as follows,
dog.owner_name # very straightforward
dog.base.weight # user should remember that this variable belong's to its parent, or there will be error
dog.base.name
@StefanKarpinski Is it possible to solve this on the Language-level?
Then the user can access the parent’s variable directly using
There are some macros to make composition easier, eg Lazy.@forward and similar.
You can also define
Base.parent(dog::Dog) = dog.base
etc and then the user would not need to remember slot names.
I would advise to just let go of OO as a general style in Julia (which does not mean that you can’t use building blocks from it, just that you should not design your code like you would in C++), multiple dispatch meshes better with other idioms.
The problem with simplistic examples of animal classes etc is that they rarely reflect the problems of real life code. You can study code from others (eg read Base, it is very instructive).
(Also, I am not sure why you are pinging Stefan Karpinski on this; generally it is considered unnecessary unless you have a good reason).
Thank you so much! Very helpful information. I am currently rewrite some simulation system written in Python with OOP design. I try to translate into Julia and found the coding style is a quite different and and is not convenient to translate directly.
If there are some books or manuals, such as Thinking in Julia or From C++/Python to Julia, it would be helpful. I will try to learn how to drop the C++ OOP style in Julia.
As variable.something translates into getproperty(variable, :something), you can overload getproperty() for the derived types, like:
function Base.getproperty(animal::T, p::Symbol) where {T<:AbstractAnimal}
if p in fieldnames(T)
getfield(animal, p)
elseif :base in fieldnames(T)
Base.getproperty(animal.base, p)
else
error("type $T has no property $p")
end
end
However, the dot syntax is intended mainly for the development purposes (like private fields in more OOP languages, I guess). The users are supposed to have accessor functions.
While I don’t personally find the ideas of OOP helpful when thinking about programming, @ninjaaron wrote a guide to his view of the topic a few months ago that didn’t seem to drag too many of the objectionable bits of OOP along when I skimmed it. It at least frames some of the patterns of the language in ways that may be familiar if you’re more comfortable with OOP. The section in the polymorphism notebook titled “Composition and method forwarding: an alternative to inheritance.” specifically addresses what you’re trying to do here.
My own advice for grokking the “Julia” way of structuring code is to not overuse types. Think of them as simple containers for data rather than “objects” that interact. And as for how I would structure your example (although I also think animal examples aren’t super helpful):
This provides fallback name and weight implementations for Animals that happen to have fields with those names while still allowing overriding if the data comes from elsewhere:
Thanks for the sharing! The suggested document is concise and useful, which saves me a lot of time to find solution from large amount of discussions on this website. Object Orientation and Polymorphism in Julia