Introduction
Contents
Introduction#
Julia’s typing system is core to the language, even though it might not always be explicitly visible.
Julia’s type system is designed to be powerful and expressive, yet clear, intuitive and unobtrusive. Many Julia programmers may never feel the need to write code that explicitly uses types. Some kinds of programming, however, become clearer, simpler, faster and more robust with declared types.
Abstract vs Concrete Types#
Concrete types:
Types of values (“objects”)
Specify data structure
Abstract types:
Cannot be instantiated
Define sets of concrete types (their descendants) by their shared “behaviors” (duck typing)
3 + 2.0
5.0
typeof(3)
Int64
typeof(2.0)
Float64
isconcretetype(Float64)
true
3 isa Int64
true
3 isa Float64
false
3 isa Number
true
isabstracttype(Number)
true
isabstracttype(Real)
true
Duck typing#
“If it walks like a duck and it quacks like a duck, then it must be a duck”
The abstract type Number
indicates that one can do number-like things, e.g. +
,-
,*
, and /
, with corresponding values. In this category we have (concrete) things like Float64
and Int32
numbers.
An AbstractArray
is a type that, e.g., allows indexing A[i]
. Examples include regular arrays (Array
), as well as ranges (UnitRange
).
Inspecting the Type Tree#
supertype(Float64)
AbstractFloat
supertype(AbstractFloat)
Real
supertype(Real)
Number
supertype(Number)
Any
subtypes(AbstractFloat)
4-element Vector{Any}:
BigFloat
Float16
Float32
Float64
Everything is a subtype of Any
:
Number <: Any
true
Float64 <: Any
true
Int32 <: Any
true
Int32 <: String
false
Let’s extract a branch of the type tree and visualize it
using AbstractTrees
AbstractTrees.children(x) = subtypes(x)
print_tree(Number)
Number
├─ Complex
└─ Real
├─ AbstractFloat
│ ├─ BigFloat
│ ├─ Float16
│ ├─ Float32
│ └─ Float64
├─ AbstractIrrational
│ └─ Irrational
├─ Integer
│ ├─ Bool
│ ├─ Signed
│ │
├─ BigInt
│ │ ├─ Int128
│ │ ├─ Int16
│ │ ├─ Int32
│ │ ├─ Int64
│ │ └─ Int8
│ └─ Unsigned
│ ├─ UInt128
│ ├─ UInt16
│ ├─ UInt32
│ ├─ UInt64
│ └─ UInt8
└─ Rational
Note that concrete types are the leaves of the type tree whereas abstract types are nodes in the type graph.
Functions, Methods, and Dispatch#
Let’s define a function that calculates the absolute value of a number (like the built-in abs
already does).
How would we practically calculate the absolute values of the numbers \(-4.32\) and \(1.0 + 1.0i\)?
Broadly you have two cases:
Real number
Drop the sign.
myabs(-4.32) = 4.32
Complex number:
Square root of z times the complex conjugate of z.
myabs(1.0 + 1.0im) = sqrt(2) ≈ 1.414
We see that the methods that we use depend on the type of the number.
While the single function represents the what (“calculate the absolute value”), there might be different methods describing the how.
We can use the ::
operator to annotate function arguments with types and define different methods.
myabs(x::Float64) = sign(x) * x
myabs (generic function with 1 method)
myabs(-4.32)
4.32
myabs(1.0 + 1.0im)
MethodError: no method matching myabs(::ComplexF64)
Closest candidates are:
myabs(::Float64) at In[21]:1
Stacktrace:
[1] top-level scope
@ In[23]:1
myabsthatdoesntexist(1.0 + 1.0im)
UndefVarError: myabsthatdoesntexist not defined
Stacktrace:
[1] top-level scope
@ In[24]:1
myabs(z::ComplexF64) = sqrt(real(z)^2 + imag(z)^2)
myabs (generic function with 2 methods)
myabs(1.0 + 1.0im)
1.4142135623730951
myabs(1.0 + 1.0im) ≈ sqrt(2.0)
true
methods(myabs)
- myabs(x::Float64) in Main at In[21]:1
- myabs(z::ComplexF64) in Main at In[25]:1
One can check which particular method is being used through the @which
macro.
@which myabs(-4.32)
@which myabs(1.0 + 1.0im)
Note that we should better loosen our type restrictions:
myabs(-3)
MethodError: no method matching myabs(::Int64)
Closest candidates are:
myabs(::Float64) at In[21]:1
myabs(::ComplexF64) at In[25]:1
Stacktrace:
[1] top-level scope
@ In[31]:1
myabs(1 + 1im)
MethodError: no method matching myabs(::Complex{Int64})
Closest candidates are:
myabs(::Float64) at In[21]:1
myabs(::ComplexF64) at In[25]:1
Stacktrace:
[1] top-level scope
@ In[32]:1
myabs(x::Real) = sign(x) * x
myabs(z::Complex) = sqrt(real(z * conj(z)))
myabs (generic function with 4 methods)
myabs(-3)
3
As we will understand later, type annotations in function signatures virtually never affect performance!
One should therefore generally make them as generic as possible.
Multiple Dispatch#
Which method gets executed when you call a generic function f
for a given set of input arguments?
Answer: Julia always chooses the most specific method by considering all input argument types.
(Since methods belong to generic functions rather than objects no function argument is special.)
f(a, b::Any) = "fallback"
f(a::Number, b::Number) = "a and b are both numbers"
f(a::Number, b) = "a is a number"
f(a, b::Number) = "b is a number"
f(a::Integer, b::Integer) = "a and b are both integers"
f (generic function with 5 methods)
methods(f)
- f(a::Integer, b::Integer) in Main at In[35]:5
- f(a::Number, b::Number) in Main at In[35]:2
- f(a::Number, b) in Main at In[35]:3
- f(a, b::Number) in Main at In[35]:4
- f(a, b) in Main at In[35]:1
f(1.5, 2)
"a and b are both numbers"
f(1, "Hamburg!")
"a is a number"
f(1, 2)
"a and b are both integers"
f("Hello", "World!")
"fallback"
@which f(1, 2)
@which f(1, "Hamburg!")
It happens rarely, but it can happen that there is no unique most specific method:
f(x::Int, y::Any) = println("int")
f(x::Any, y::String) = println("string")
f(3, "test")
MethodError: f(::Int64, ::String) is ambiguous. Candidates:
f(x::Int64, y) in Main at In[43]:1
f(a::Number, b) in Main at In[35]:3
f(x, y::String) in Main at In[43]:2
Possible fix, define
f(::Int64, ::String)
Stacktrace:
[1] top-level scope
@ In[43]:3
Spoiler! This is an awesome feature:
See more about ‘The Unreasonable Effectiveness of Multiple Dispatch’ here: https://www.youtube.com/watch?v=kc9HwsxE1OY
Built-in Julia Functions#
(Most of) Julia’s built-in functions are not special by any means.
methods(+)
- +(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:87
- +(x::T, y::T) where T<:Union{Float16, Float32, Float64} in Base at float.jl:383
- +(c::Union{UInt16, UInt32, UInt64, UInt8}, x::BigInt) in Base.GMP at gmp.jl:531
- +(c::Union{Int16, Int32, Int64, Int8}, x::BigInt) in Base.GMP at gmp.jl:537
- +(c::Union{UInt16, UInt32, UInt64, UInt8}, x::BigFloat) in Base.MPFR at mpfr.jl:398
- +(c::Union{Int16, Int32, Int64, Int8}, x::BigFloat) in Base.MPFR at mpfr.jl:406
- +(c::Union{Float16, Float32, Float64}, x::BigFloat) in Base.MPFR at mpfr.jl:414
- +(x::Union{Dates.CompoundPeriod, Dates.Period}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:378
- +(r::AbstractRange{<:Dates.TimeType}, x::Dates.Period) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/ranges.jl:64
- +(r1::OrdinalRange, r2::OrdinalRange) in Base at range.jl:1423
- +(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where N in Base.IteratorsMD at multidimensional.jl:114
- +(x::AbstractArray{<:Dates.TimeType}, y::Union{Dates.CompoundPeriod, Dates.Period}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/deprecated.jl:6
- +(x::P, y::P) where P<:Dates.Period in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:77
- +(x::Dates.Period, y::Dates.Period) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:367
- +(y::Dates.Period, x::Dates.CompoundPeriod) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:369
- +(y::Dates.Period, x::Dates.TimeType) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:85
- +(x::Dates.Period, r::AbstractRange{<:Dates.TimeType}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/ranges.jl:63
- +(y::Union{Dates.CompoundPeriod, Dates.Period}, x::AbstractArray{<:Dates.TimeType}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/deprecated.jl:14
- +(J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:149
- +(J::LinearAlgebra.UniformScaling, x::Number) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:144
- +(J1::LinearAlgebra.UniformScaling, J2::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:150
- +(J::LinearAlgebra.UniformScaling, B::BitMatrix) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:152
- +(x::LinearAlgebra.UniformScaling, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(J::LinearAlgebra.UniformScaling, F::LinearAlgebra.Hessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:642
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.Tridiagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:257
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.SymTridiagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:262
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.Bidiagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:267
- +(A::LinearAlgebra.UniformScaling, B::LinearAlgebra.Diagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:272
- +(J::LinearAlgebra.UniformScaling, A::AbstractMatrix) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:153
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/tridiag.jl:207
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:485
- +(A::LinearAlgebra.SymTridiagonal{var"#s884", V} where {var"#s884"<:Real, V<:AbstractVector{var"#s884"}}, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:487
- +(x::LinearAlgebra.SymTridiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:157
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:171
- +(A::LinearAlgebra.SymTridiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:225
- +(A::LinearAlgebra.SymTridiagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:243
- +(A::LinearAlgebra.UpperHessenberg, B::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:101
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(H::LinearAlgebra.UpperHessenberg, x::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:108
- +(Da::LinearAlgebra.Diagonal, Db::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/diagonal.jl:184
- +(D::LinearAlgebra.Diagonal, S::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/diagonal.jl:188
- +(D::LinearAlgebra.Diagonal{var"#s886", V} where {var"#s886"<:Real, V<:AbstractVector{var"#s886"}}, H::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/diagonal.jl:194
- +(x::LinearAlgebra.Diagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:137
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:147
- +(A::LinearAlgebra.Diagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:175
- +(A::LinearAlgebra.Diagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:253
- +(A::BitArray, B::BitArray) in Base at bitarray.jl:1184
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:643
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:645
- +(UL::LinearAlgebra.UnitLowerTriangular, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:181
- +(A::LinearAlgebra.UnitLowerTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:98
- +(z::Complex, w::Complex) in Base at complex.jl:288
- +(x::Dates.Instant) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:4
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/bidiag.jl:365
- +(x::LinearAlgebra.Bidiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:106
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:106
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:106
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:106
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:127
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:195
- +(A::LinearAlgebra.Bidiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:215
- +(A::LinearAlgebra.Bidiagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:248
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Tridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/tridiag.jl:741
- +(x::LinearAlgebra.Tridiagonal, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:169
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:185
- +(A::LinearAlgebra.Tridiagonal, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:205
- +(A::LinearAlgebra.Tridiagonal{var"#s886", V} where {var"#s886"<:Number, V<:AbstractVector{var"#s886"}}, B::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:238
- +(y::AbstractFloat, x::Bool) in Base at bool.jl:172
- +(F::LinearAlgebra.Hessenberg, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:641
- +(x::Rational, y::Integer) in Base at rational.jl:313
- +(x::Base.TwicePrecision, y::Number) in Base at twiceprecision.jl:290
- +(x::Base.TwicePrecision{T}, y::Base.TwicePrecision{T}) where T in Base at twiceprecision.jl:296
- +(x::Base.TwicePrecision, y::Base.TwicePrecision) in Base at twiceprecision.jl:301
- +(x::T, y::Integer) where T<:AbstractChar in Base at char.jl:237
- +(A::LinearAlgebra.Symmetric, B::LinearAlgebra.Symmetric) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:469
- +(A::LinearAlgebra.Symmetric, B::LinearAlgebra.SymTridiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:486
- +(S::LinearAlgebra.Symmetric, D::LinearAlgebra.Diagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/diagonal.jl:191
- +(A::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:15
- +(A::LinearAlgebra.Symmetric, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:18
- +(A::LinearAlgebra.Symmetric{<:Real, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:25
- +(A::LinearAlgebra.Symmetric{var"#s885", S} where {var"#s885"<:Real, S<:(AbstractMatrix{<:var"#s885"})}, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:484
- +(A::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:23
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.Hermitian) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:469
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.SymTridiagonal{var"#s883", V} where {var"#s883"<:Real, V<:AbstractVector{var"#s883"}}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:488
- +(H::LinearAlgebra.Hermitian, D::LinearAlgebra.Diagonal{var"#s886", V} where {var"#s886"<:Real, V<:AbstractVector{var"#s886"}}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/diagonal.jl:197
- +(A::LinearAlgebra.Hermitian, J::LinearAlgebra.UniformScaling{<:Complex}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:195
- +(A::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:15
- +(A::LinearAlgebra.Hermitian, B::SparseArrays.AbstractSparseMatrix) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:18
- +(A::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Symmetric{<:Real, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:26
- +(A::LinearAlgebra.Hermitian, B::LinearAlgebra.Symmetric{var"#s886", S} where {var"#s886"<:Real, S<:(AbstractMatrix{<:var"#s886"})}) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/symmetric.jl:483
- +(A::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}, B::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:24
- +(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:158
- +(r1::LinRange{T}, r2::LinRange{T}) where T in Base at range.jl:1430
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:642
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:644
- +(UL::LinearAlgebra.UnitUpperTriangular, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:181
- +(x::LinearAlgebra.UnitUpperTriangular, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(A::LinearAlgebra.UnitUpperTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:98
- +(x::Ptr, y::Integer) in Base at pointer.jl:159
- +(y::Dates.TimeType, x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/deprecated.jl:18
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.LowerTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:639
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.UnitLowerTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:641
- +(A::LinearAlgebra.LowerTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:98
- +(r1::StepRangeLen{T, R}, r2::StepRangeLen{T, R}) where {R<:Base.TwicePrecision, T} in Base at twiceprecision.jl:644
- +(r1::StepRangeLen{T, S}, r2::StepRangeLen{T, S}) where {T, S} in Base at range.jl:1446
- +(r1::Union{LinRange, OrdinalRange, StepRangeLen}, r2::Union{LinRange, OrdinalRange, StepRangeLen}) in Base at range.jl:1439
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.UpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:638
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.UnitUpperTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:640
- +(A::LinearAlgebra.AbstractTriangular, B::LinearAlgebra.AbstractTriangular) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/triangular.jl:646
- +(x::LinearAlgebra.UpperTriangular, H::LinearAlgebra.UpperHessenberg) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/hessenberg.jl:109
- +(A::LinearAlgebra.UpperTriangular, B::LinearAlgebra.Bidiagonal) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/special.jl:98
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Hermitian{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:14
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Hermitian) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:17
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Symmetric{<:Any, <:SparseArrays.AbstractSparseMatrix}) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:14
- +(A::SparseArrays.AbstractSparseMatrix, B::LinearAlgebra.Symmetric) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/linalg.jl:17
- +(A::SparseArrays.AbstractSparseMatrixCSC, B::SparseArrays.AbstractSparseMatrixCSC) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/sparsematrix.jl:1835
- +(x::SparseArrays.AbstractSparseVector, y::SparseArrays.AbstractSparseVector) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/sparsevector.jl:1394
- +(A::SparseArrays.AbstractSparseMatrixCSC, B::Array) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/sparsematrix.jl:1838
- +(A::SparseArrays.AbstractSparseMatrixCSC, J::LinearAlgebra.UniformScaling) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/sparsematrix.jl:3827
- +(A::Array, Bs::Array...) in Base at arraymath.jl:12
- +(X::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}, Y::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/deprecated.jl:62
- +(A::Array, B::SparseArrays.AbstractSparseMatrixCSC) in SparseArrays at /usr/local/julia/share/julia/stdlib/v1.8/SparseArrays/src/sparsematrix.jl:1839
- +(x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/deprecated.jl:55
- +(x::StridedArray{<:Union{Dates.CompoundPeriod, Dates.Period}}, y::Dates.TimeType) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/deprecated.jl:10
- +(A::AbstractArray, B::AbstractArray) in Base at arraymath.jl:6
- +(x::BigFloat, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.MPFR at mpfr.jl:393
- +(x::BigFloat, c::Union{Int16, Int32, Int64, Int8}) in Base.MPFR at mpfr.jl:401
- +(x::BigFloat, c::Union{Float16, Float32, Float64}) in Base.MPFR at mpfr.jl:409
- +(x::BigFloat, y::BigFloat) in Base.MPFR at mpfr.jl:386
- +(x::BigFloat, c::BigInt) in Base.MPFR at mpfr.jl:417
- +(a::BigFloat, b::BigFloat, c::BigFloat) in Base.MPFR at mpfr.jl:558
- +(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat) in Base.MPFR at mpfr.jl:564
- +(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat, e::BigFloat) in Base.MPFR at mpfr.jl:571
- +(dt::Dates.Date, t::Dates.Time) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:19
- +(dt::Dates.Date, y::Dates.Year) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:27
- +(dt::Dates.Date, z::Dates.Month) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:54
- +(x::Dates.Date, y::Dates.Quarter) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:73
- +(x::Dates.Date, y::Dates.Week) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:77
- +(x::Dates.Date, y::Dates.Day) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:79
- +(x::Dates.CompoundPeriod, y::Dates.Period) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:368
- +(x::Dates.CompoundPeriod, y::Dates.TimeType) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:398
- +(x::Dates.CompoundPeriod, y::Dates.CompoundPeriod) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:370
- +(x::BigInt, c::Union{UInt16, UInt32, UInt64, UInt8}) in Base.GMP at gmp.jl:530
- +(x::BigInt, c::Union{Int16, Int32, Int64, Int8}) in Base.GMP at gmp.jl:536
- +(x::BigInt, y::BigInt) in Base.GMP at gmp.jl:482
- +(a::BigInt, b::BigInt, c::BigInt) in Base.GMP at gmp.jl:522
- +(a::BigInt, b::BigInt, c::BigInt, d::BigInt) in Base.GMP at gmp.jl:523
- +(a::BigInt, b::BigInt, c::BigInt, d::BigInt, e::BigInt) in Base.GMP at gmp.jl:524
- +(x::BigInt, y::BigInt, rest::BigInt...) in Base.GMP at gmp.jl:657
- +(c::BigInt, x::BigFloat) in Base.MPFR at mpfr.jl:422
- +(x::Bool) in Base at bool.jl:159
- +(x::Integer, y::Ptr) in Base at pointer.jl:161
- +(y::Integer, x::Rational) in Base at rational.jl:320
- +(x::Integer, y::AbstractChar) in Base at char.jl:247
- +(x::Bool, y::T) where T<:AbstractFloat in Base at bool.jl:169
- +(x::Bool, y::Bool) in Base at bool.jl:162
- +(a::Integer, b::Integer) in Base at int.jl:987
- +(x::Bool, z::Complex{Bool}) in Base at complex.jl:299
- +(x::Real, z::Complex{Bool}) in Base at complex.jl:313
- +(x::Bool, z::Complex) in Base at complex.jl:306
- +(x::Real, z::Complex) in Base at complex.jl:325
- +(::Missing) in Base at missing.jl:101
- +(::Missing, ::Number) in Base at missing.jl:123
- +(x::Missing, y::Dates.AbstractTime) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:89
- +(::Missing, ::Missing) in Base at missing.jl:122
- +(B::BitMatrix, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:151
- +(A::AbstractMatrix, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:214
- +(x::AbstractArray{<:Number}) in Base at abstractarraymath.jl:220
- +(a::Pkg.Resolve.VersionWeight, b::Pkg.Resolve.VersionWeight) in Pkg.Resolve at /usr/local/julia/share/julia/stdlib/v1.8/Pkg/src/Resolve/versionweights.jl:22
- +(a::Pkg.Resolve.FieldValue, b::Pkg.Resolve.FieldValue) in Pkg.Resolve at /usr/local/julia/share/julia/stdlib/v1.8/Pkg/src/Resolve/fieldvalues.jl:43
- +(dt::Dates.DateTime, y::Dates.Year) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:23
- +(dt::Dates.DateTime, z::Dates.Month) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:47
- +(x::Dates.DateTime, y::Dates.Quarter) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:75
- +(x::Dates.DateTime, y::Dates.Period) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:81
- +(x::Rational{BigInt}, y::Rational{BigInt}) in Base.GMP.MPQ at gmp.jl:922
- +(x::Rational) in Base at rational.jl:270
- +(x::Number, y::Base.TwicePrecision) in Base at twiceprecision.jl:294
- +(::Number, ::Missing) in Base at missing.jl:124
- +(x::Number, J::LinearAlgebra.UniformScaling) in LinearAlgebra at /usr/local/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/uniformscaling.jl:145
- +(x::Rational, y::Rational) in Base at rational.jl:284
- +(level::Base.CoreLogging.LogLevel, inc::Integer) in Base.CoreLogging at logging.jl:131
- +(x::Dates.Time, y::Dates.TimePeriod) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:83
- +(t::Dates.Time, dt::Dates.Date) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:20
- +(x::Dates.TimeType) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:8
- +(x::Dates.AbstractTime, y::Missing) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/arithmetic.jl:88
- +(a::Dates.TimeType, b::Dates.Period, c::Dates.Period) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:389
- +(a::Dates.TimeType, b::Dates.Period, c::Dates.Period, d::Dates.Period...) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:390
- +(x::Dates.TimeType, y::Dates.CompoundPeriod) in Dates at /usr/local/julia/share/julia/stdlib/v1.8/Dates/src/periods.jl:392
- +(z::Complex{Bool}, x::Bool) in Base at complex.jl:300
- +(z::Complex, x::Bool) in Base at complex.jl:307
- +(z::Complex{Bool}, x::Real) in Base at complex.jl:314
- +(z::Complex) in Base at complex.jl:286
- +(z::Complex, x::Real) in Base at complex.jl:326
- +(x::Number) in Base at operators.jl:528
- +(x::T, y::T) where T<:Number in Base at promotion.jl:463
- +(x::Number, y::Number) in Base at promotion.jl:388
- +(a, b, c, xs...) in Base at operators.jl:591
@which true + false
@which "Hello" * "World!"
We can easily modify or add methods to them as well.
import Base: + # we have to import functions to override/add methods
+(x::String, y::String) = x * "_" * y
# alternative
Base.:+(x::String, y::String) = x * "_" * y
"Hello" + "Hamburg!"
"Hello_Hamburg!"
(Side note: as we neither own the +
function nor the String
type the above is type piracy and should generally be avoided! 😉)
Type Parameters#
Types can have type parameters. They are crucial for achieving high performance while being generic at the same time (more on this later).
The most prominent example is Julia’s regular array type.
M = rand(2, 2)
2×2 Matrix{Float64}:
0.143278 0.420948
0.624822 0.817864
typeof(M)
Matrix{Float64} (alias for Array{Float64, 2})
Here, Array
is a parametric datatype. The type parameters are
Float64
(element type)2
(dimensionality)
Hence Array{Float64, 2}
means that we have a matrix than can hold 64-bit floating point numbers.
This generalizes as expected. Here, a vector of String
s:
M = fill("Hamburg", 2)
2-element Vector{String}:
"Hamburg"
"Hamburg"
typeof(M)
Vector{String} (alias for Array{String, 1})
eltype(M)
String
We can also nest parametric types. This is a vector of matrices of Float64
s.
v = [rand(2, 2) for i in 1:3]
3-element Vector{Matrix{Float64}}:
[0.4729470610240727 0.2454842781254145; 0.9946494078553325 0.7321556009235937]
[0.3537497611269118 0.6223284386618653; 0.5429099854763516 0.09985402528424125]
[0.34467500788821326 0.8186559161726774; 0.8342360583024825 0.19253273388995396]
typeof(v)
Vector{Matrix{Float64}} (alias for Array{Array{Float64, 2}, 1})
eltype(v)
Matrix{Float64} (alias for Array{Float64, 2})
Another example of a parametric type is the Tuple
.
(1, 2.0, "3")
(1, 2.0, "3")
typeof((1, 2.0, "3"))
Tuple{Int64, Float64, String}
Type parameters in function signatures#
Naive approach:
myfunc(v::Vector{Real}) = "I'm a real vector!"
myfunc (generic function with 1 method)
myfunc([1.0, 2.0, 3.0])
MethodError: no method matching myfunc(::Vector{Float64})
Closest candidates are:
myfunc(::Vector{Real}) at In[59]:1
Stacktrace:
[1] top-level scope
@ In[60]:1
Huh? What’s going on?
Note that although we have
Float64 <: Real
true
Parametric types have the following (perhaps somewhat counterintuitive) property
Vector{Float64} <: Vector{Real}
false
[1.0, 2.0, 3.0] isa Vector{Real}
false
How can we understand the behavior above?
The crucial point is that Vector{Real}
is a concrete container type despite the fact that Real
is an abstract type. Specifically, it describes a heterogeneous vector of values that individually can be of any type T <: Real
.
This behaviour is explained in more detailed in the Julia documentation here however I’ll skip over the details as they are not too important.
isconcretetype(Vector{Real})
true
Real[1, 2.2, 13.0f0]
3-element Vector{Real}:
1
2.2
13.0f0
As we have learned above, concrete types are the leafes of the type tree and cannot have any subtypes. Hence it is only consistent to have…
Vector{Float64} <: Vector{Real}
false
What we often actually mean when writing myfunc(v::Vector{Real}) = ...
is
myfunc(v::Vector{T}) where T <: Real = "I'm a real vector!"
# Equivalent to:
# myfunc(v::Vector{<:Real}) = "I'm a real vector!"
myfunc (generic function with 2 methods)
myfunc([1.0, 2.0, 3.0])
"I'm a real vector!"
It works! But what does it mean exactly? First of all, we see that
Vector{Float64} <: Vector{T} where {T <: Real}
true
Here, Vector{T} where T <: Real
describes the set of concrete Vector
types whose elements are of any specific single type T
that is a subtype of Real
.
Think of it as representing
set(Vector{Float64}, Vector{Int64}, Vector{Int32}, Vector{AbstractFloat}, ...)
Vector{Int64} <: Vector{T} where T <: Real
true
Vector{AbstractFloat} <: Vector{T} where T <: Real
true
[1.0, 2.0, 3.0] isa Vector{T} where T <: Real
true
We can also use the where
notation to write out our naive Vector{Real}
from above in a more explicit way:
Vector{Real} === Vector{T where T <: Real}
true
Note that the crucial difference is the position of the where T<:Real
piece, i.e. whether it is inside or outside of the curly braces.
Vector{T} where T <: Real
Vector{T} where T<:Real (alias for Array{T, 1} where T<:Real)
Vector{T where T <: Real}
Vector{Real} (alias for Array{Real, 1})
(More mathematically put: Whether where T
is inside or outside of the curly braces indicates whether there is or is not a “degree of freedom” that spans the “one-dimensional” set above.)
Summary#
Concrete types describe data structures, i.e. concrete implementations.
Abstract types define the kind of a thing (What is it? What can I do with it?), i.e. an informal interface. This is also known as duck-typing.
A function (the what) can have multiple methods (the how).
Types in function signatures serve as filters. Avoid writing overly-specific types.
Multiple dispatch: Julia selects the method to run based on the types of all input arguments and chooses the most specialized one.
Types can have parameters, i.e.
Vector{Float64}
. We can use the notationT where T<:SomeSuperType
to address sets of types.