How do we do classes in Julia?
The closest one can get to classes with methods in Julia is the module:
module DogClass
export Dog, bark
struct Dog
name::String
end
function bark(d::Dog)
println(d.name, " says woof!")
end
end #MODULE
using .DogClass # note the . here, means look locally for module, not library
mydog = Dog("Fido")
bark(mydog)
Julia does not have classes. Instead we define new types and then define methods on those types. Methods are not "owned" by the types they operate on. Instead, a method can be said to belong to a generic function of the same name as the method. For instance, there are many versions ("methods") of the length
function; together they form the generic function length
.
Here's an extended example of the Julian approach to programming with types and methods. New types are declared using the struct
keyword:
struct Person
name::String
age::Int64
end
Now we can define methods on the Person
type:
name(p::Person) = p.name
age(p::Person) = p.age
bio(p::Person) = println("My name is ", name(p)," and I am ", age(p), " years old.")
Methods can be defined for different combinations of argument types. To illustrate this, let's first define some new types:
abstract type Pet end
struct Cat <: Pet
name::String
color::String
end
name(c::Cat) = c.name
color(c::Cat) = c.color
species(::Cat) = "cat"
struct Dog <: Pet
name::String
color::String
end
name(d::Dog) = d.name
color(d::Dog) = d.color
species(::Dog) = "dog"
bio(p::Pet) = println("I have a ", color(p), " ", species(p), " named ", name(p), ".")
struct Plant
type::String
end
type(p::Plant) = p.type
bio(p::Plant) = println("I have a ", type(p), " house plant.")
At this point we can see that we've defined three different one-argument methods for bio
:
julia> methods(bio)
3 methods for generic function "bio":
[1] bio(p::Plant) in Main at REPL[17]:1
[2] bio(p::Person) in Main at REPL[4]:1
[3] bio(p::Pet) in Main at REPL[14]:1
Note the comment in the output of methods(bio)
: "3 methods for generic function 'bio'". We see that bio
is a generic function that currently has 3 methods defined for different function signatures. Now let's add a two-argument method for bio
:
function bio(person::Person, possession)
bio(person)
bio(possession)
end
Notice that this function is generic in the possession
argument, since the internal call to bio(possession)
will work whether the possession
is a plant, cat, or dog! So we now have four total methods for bio
:
julia> methods(bio)
4 methods for generic function "bio":
[1] bio(p::Plant) in Main at REPL[17]:1
[2] bio(p::Person) in Main at REPL[4]:1
[3] bio(p::Pet) in Main at REPL[14]:1
[4] bio(person::Person, possession) in Main at REPL[18]:1
Now let's create a few instances of our types:
alice = Person("Alice", 37)
cat = Cat("Socks", "black")
dog = Dog("Roger", "brown")
plant = Plant("Boston Fern")
So finally we can test our bio
methods:
julia> bio(alice, cat)
My name is Alice and I am 37 years old.
I have a black cat named Socks.
julia> bio(alice, dog)
My name is Alice and I am 37 years old.
I have a brown dog named Roger.
julia> bio(alice, plant)
My name is Alice and I am 37 years old.
I have a Boston Fern house plant.
Side note: Modules are used primarily for namespace management. A single module can contain the definitions for multiple types and multiple methods.