Defining new types in Julia

In [1]:
mutable struct MyDiscreteWalker   # capital letters for each word in type name -- camel case
    x::Int64
end
In [2]:
mutable struct SomethingElse   
    x::Int64   # several fields
    y::Float64
    name::String
end
In [4]:
MyDiscreteWalker  # refers to the *type*
Out[4]:
MyDiscreteWalker
In [6]:
methods(MyDiscreteWalker)  # which methods / versions of a function exist
Out[6]:
# 2 methods for type constructor:
  • MyDiscreteWalker(x::Int64) in Main at In[1]:2
  • MyDiscreteWalker(x) in Main at In[1]:2

Julia automatically created 2 constructors -- functions to create objects of that type

In [7]:
w = MyDiscreteWalker(3)
Out[7]:
MyDiscreteWalker(3)
In [8]:
typeof(w)
Out[8]:
MyDiscreteWalker
In [9]:
Complex(3, 4)
Out[9]:
3 + 4im
In [10]:
3 + 4im  # im is i (sqrt of -1)
Out[10]:
3 + 4im
In [11]:
@which Complex(3, 4)
Out[11]:
(::Type{Complex})(re::T, im::T) where T<:Real in Base at complex.jl:12
In [12]:
w
Out[12]:
MyDiscreteWalker(3)
In [14]:
w isa MyDiscreteWalker   # w is an **instance** of the type MyDiscreteWalker
Out[14]:
true
In [15]:
methods(MyDiscreteWalker)
Out[15]:
# 2 methods for type constructor:
  • MyDiscreteWalker(x::Int64) in Main at In[1]:2
  • MyDiscreteWalker(x) in Main at In[1]:2

MyDiscreteWalker(x::Int64) is a method that works only when it receives an argument x of type Int64

MyDiscreteWalker(x) will accept an x of any type

In [16]:
Any
Out[16]:
Any
In [17]:
3.0 isa Int64
Out[17]:
false
In [19]:
typeof(3.0)
Out[19]:
Float64
In [20]:
MyDiscreteWalker(3.0)
Out[20]:
MyDiscreteWalker(3)
In [21]:
MyDiscreteWalker(3.1)
InexactError: Int64(3.1)

Stacktrace:
 [1] Int64 at ./float.jl:710 [inlined]
 [2] convert at ./number.jl:7 [inlined]
 [3] MyDiscreteWalker(::Float64) at ./In[1]:2
 [4] top-level scope at In[21]:1
In [22]:
Int64(3.0)
Out[22]:
3
In [23]:
Int64(3.1)
InexactError: Int64(3.1)

Stacktrace:
 [1] Int64(::Float64) at ./float.jl:710
 [2] top-level scope at In[23]:1
In [24]:
trunc(Int64, 3.1)
Out[24]:
3
In [25]:
floor(Int64, 3.1)
Out[25]:
3
In [26]:
floor(3.1)
Out[26]:
3.0
In [27]:
Int64(floor(3.1))
Out[27]:
3
In [28]:
w = MyDiscreteWalker(3)
Out[28]:
MyDiscreteWalker(3)
In [29]:
w
Out[29]:
MyDiscreteWalker(3)

Problem set 3:

Collecting information into one packet that belongs together: encapsulation

In [ ]:
mutable struct Agent
    infection_status::Int
    num_infected::Int
end
In [30]:
w = MyDiscreteWalker(3)
Out[30]:
MyDiscreteWalker(3)

More common to use immutable structs:

In [31]:
struct ExampleImmutable
    x::Int
    y::Int
end
In [ ]:
z = ExampleImmutable()
In [32]:
convert(Int64, 3.0)
Out[32]:
3
In [33]:
convert(Int64, 3.1)
InexactError: Int64(3.1)

Stacktrace:
 [1] Int64 at ./float.jl:710 [inlined]
 [2] convert(::Type{Int64}, ::Float64) at ./number.jl:7
 [3] top-level scope at In[33]:1
In [34]:
Int64(3.0)
Out[34]:
3
In [36]:
@which Int64(3.0)
Out[36]:
Int64(x::Float64) in Base at float.jl:707
In [37]:
methods(Int64)
Out[37]:
# 14 methods for type constructor:
In [39]:
@which convert(Int64, 3.1)
Out[39]:
convert(::Type{T}, x::Number) where T<:Number in Base at number.jl:7
In [41]:
z = ExampleImmutable(1, 2)
Out[41]:
ExampleImmutable(1, 2)
In [42]:
z.x
Out[42]:
1
In [44]:
z.y
Out[44]:
2
In [45]:
z.x = 10
setfield! immutable struct of type ExampleImmutable cannot be changed

Stacktrace:
 [1] setproperty!(::ExampleImmutable, ::Symbol, ::Int64) at ./Base.jl:34
 [2] top-level scope at In[45]:1
In [46]:
z.y = 10
setfield! immutable struct of type ExampleImmutable cannot be changed

Stacktrace:
 [1] setproperty!(::ExampleImmutable, ::Symbol, ::Int64) at ./Base.jl:34
 [2] top-level scope at In[46]:1

Make walker move

In [47]:
w = MyDiscreteWalker(3)
Out[47]:
MyDiscreteWalker(3)
In [48]:
MyDiscreteWalker()
MethodError: no method matching MyDiscreteWalker()
Closest candidates are:
  MyDiscreteWalker(!Matched::Int64) at In[1]:2
  MyDiscreteWalker(!Matched::Any) at In[1]:2

Stacktrace:
 [1] top-level scope at In[48]:1
In [49]:
methods(MyDiscreteWalker)
Out[49]:
# 2 methods for type constructor:
  • MyDiscreteWalker(x::Int64) in Main at In[1]:2
  • MyDiscreteWalker(x) in Main at In[1]:2
In [50]:
MyDiscreteWalker() = MyDiscreteWalker(0)  # outer constructor (lives outside definition of type)
Out[50]:
MyDiscreteWalker
In [51]:
methods(MyDiscreteWalker)
Out[51]:
# 3 methods for type constructor:
  • MyDiscreteWalker() in Main at In[50]:1
  • MyDiscreteWalker(x::Int64) in Main at In[1]:2
  • MyDiscreteWalker(x) in Main at In[1]:2
In [52]:
w = MyDiscreteWalker()
Out[52]:
MyDiscreteWalker(0)

Now make it move

In [53]:
function jump!(w::MyDiscreteWalker)
    w.x += rand( (-1, +1) )
end
Out[53]:
jump! (generic function with 1 method)
In [54]:
pos(w::MyDiscreteWalker) = w.x   # getter function
Out[54]:
pos (generic function with 1 method)
In [ ]:
w.x   # w.<TAB>
In [55]:
propertynames(w)
Out[55]:
(:x,)
In [59]:
propertynames(z)
Out[59]:
(:x, :y)
In [60]:
pos(w)   # interface to my object; removes me from the internal details
Out[60]:
0
In [61]:
function set_pos!(w, x)   # setter function
    w.x = x
end
Out[61]:
set_pos! (generic function with 1 method)
In [62]:
jump(MyDiscreteWalker) = rand( (-1, +1) )
Out[62]:
jump (generic function with 1 method)
In [ ]:
function jump!(w::MyDiscreteWalker)
    old_pos = pos(w)
    set_pos!(old_pos + jump(w))
end

In this version of jump!, I never refer to internal details of the object.

In [64]:
function walk!(w::MyDiscreteWalker, N)
    for i in 1:N
        jump!(w)
    end
    
    return w
end
Out[64]:
walk! (generic function with 1 method)
In [65]:
w
Out[65]:
MyDiscreteWalker(0)
In [66]:
walk!(10)
MethodError: no method matching walk!(::Int64)
Closest candidates are:
  walk!(!Matched::MyDiscreteWalker, !Matched::Any) at In[64]:2

Stacktrace:
 [1] top-level scope at In[66]:1
In [67]:
walk!
Out[67]:
walk! (generic function with 1 method)
In [68]:
methods(walk!)
Out[68]:
# 1 method for generic function walk!:
  • walk!(w::MyDiscreteWalker, N) in Main at In[64]:2
In [69]:
aweirdfunction(10)
UndefVarError: aweirdfunction not defined

Stacktrace:
 [1] top-level scope at In[69]:1
In [70]:
walk!(w, 10)
Out[70]:
MyDiscreteWalker(4)
In [71]:
w  # has been mutated / modified
Out[71]:
MyDiscreteWalker(4)

Continuous walkers

In [72]:
mutable struct MyContinuousWalker
    y::Float64
end
In [73]:
w = MyContinuousWalker(3)
Out[73]:
MyContinuousWalker(3.0)
In [74]:
w isa MyContinuousWalker
Out[74]:
true
In [75]:
jump!(w)
MethodError: no method matching jump!(::MyContinuousWalker)
Closest candidates are:
  jump!(!Matched::MyDiscreteWalker) at In[53]:2

Stacktrace:
 [1] top-level scope at In[75]:1
In [76]:
methods(jump!)
Out[76]:
# 1 method for generic function jump!:
  • jump!(w::MyDiscreteWalker) in Main at In[53]:2
In [77]:
jump(w::MyContinuousWalker) = randn()
Out[77]:
jump (generic function with 2 methods)
In [78]:
jump!(w::MyContinuousWalker)
MethodError: no method matching jump!(::MyContinuousWalker)
Closest candidates are:
  jump!(!Matched::MyDiscreteWalker) at In[53]:2

Stacktrace:
 [1] top-level scope at In[78]:1
In [79]:
methods(jump!)
Out[79]:
# 1 method for generic function jump!:
  • jump!(w::MyDiscreteWalker) in Main at In[53]:2
In [89]:
function jump!(w)  # takes argument w of *any* type
    old_pos = pos(w)
    set_pos!(w, old_pos + jump(w))
end
Out[89]:
jump! (generic function with 2 methods)
In [81]:
jump!(1)
MethodError: no method matching pos(::Int64)
Closest candidates are:
  pos(!Matched::MyDiscreteWalker) at In[54]:1

Stacktrace:
 [1] jump!(::Int64) at ./In[80]:2
 [2] top-level scope at In[81]:1
In [83]:
w
Out[83]:
MyContinuousWalker(3.0)
In [84]:
jump!(w)
MethodError: no method matching pos(::MyContinuousWalker)
Closest candidates are:
  pos(!Matched::MyDiscreteWalker) at In[54]:1

Stacktrace:
 [1] jump!(::MyContinuousWalker) at ./In[82]:2
 [2] top-level scope at In[84]:1
In [85]:
methods(pos)
Out[85]:
# 1 method for generic function pos:
  • pos(w::MyDiscreteWalker) in Main at In[54]:1
In [86]:
pos(w::MyContinuousWalker) = w.y
Out[86]:
pos (generic function with 2 methods)
In [87]:
function set_pos!(w::MyContinuousWalker, pos)
    w.y = pos
end
Out[87]:
set_pos! (generic function with 2 methods)
In [90]:
jump!(w)
Out[90]:
2.1822730537401607
In [91]:
walk!(w, 10)
MethodError: no method matching walk!(::MyContinuousWalker, ::Int64)
Closest candidates are:
  walk!(!Matched::MyDiscreteWalker, ::Any) at In[64]:2

Stacktrace:
 [1] top-level scope at In[91]:1
In [92]:
methods(walk!)
Out[92]:
# 1 method for generic function walk!:
  • walk!(w::MyDiscreteWalker, N) in Main at In[64]:2
In [93]:
function walk!(w, N)
    for i in 1:N
        jump!(w)
    end
    
    return w
end
Out[93]:
walk! (generic function with 2 methods)
In [94]:
walk!(1, 2)
MethodError: no method matching pos(::Int64)
Closest candidates are:
  pos(!Matched::MyContinuousWalker) at In[86]:1
  pos(!Matched::MyDiscreteWalker) at In[54]:1

Stacktrace:
 [1] jump!(::Int64) at ./In[89]:2
 [2] walk!(::Int64, ::Int64) at ./In[93]:3
 [3] top-level scope at In[94]:1
In [95]:
walk!(w, 10)
Out[95]:
MyContinuousWalker(-3.3119507350317736)
In [96]:
w
Out[96]:
MyContinuousWalker(-3.3119507350317736)

Abstract types

"A discrete walker is a kind of walker"

In [98]:
abstract type RandomWalker end
In [99]:
RandomWalker
Out[99]:
RandomWalker
In [100]:
methods(RandomWalker)
Out[100]:
# 0 methods for type constructor:
    In [101]:
    mutable struct DiscreteWalker <: RandomWalker
        x::Int64
    end
    
    In [102]:
    mutable struct ContinuousWalker <: RandomWalker
        y::Int64
    end
    
    In [103]:
    pos(w::DiscreteWalker) = w.x
    pos(w::ContinuousWalker) = w.y
    
    Out[103]:
    pos (generic function with 4 methods)
    In [104]:
    function walk!(w::RandomWalker, N)
        for i in 1:N
            jump!(w)
        end
        
        return w
    end
    
    Out[104]:
    walk! (generic function with 3 methods)
    In [105]:
    w = DiscreteWalker(10)
    
    Out[105]:
    DiscreteWalker(10)
    In [106]:
    w isa DiscreteWalker
    
    Out[106]:
    true
    In [107]:
    w isa RandomWalker
    
    Out[107]:
    true
    In [108]:
    [DiscreteWalker(1), DiscreteWalker(2)]
    
    Out[108]:
    2-element Array{DiscreteWalker,1}:
     DiscreteWalker(1)
     DiscreteWalker(2)
    In [ ]: