Parametric Types
Prerequisites: Constructors
A parametric type has a type parameter in curly braces, so one
definition works for many element types. You've already used them:
Vector{Int}, Dict{String,Int}.
struct Point{T}
x::T
y::T
end
p1 = Point(1, 2)
p2 = Point(1.5, 2.5)
p3 = Point{Float32}(1, 2)
println(typeof(p1), " ", typeof(p2), " ", typeof(p3))
println(Point{Int} <: Point, " ", Point{Int} <: Point{Real})
try
Point(1, 2.5)
catch err
println(typeof(err))
end
Point{Int64} Point{Float64} Point{Float32}
true false
MethodError
- Julia infers
Tfrom the arguments;Point(1, 2.5)fails becausexandymust have the sameT. Point{Int}is a subtype ofPoint, but not ofPoint{Real}even thoughInt <: Real(type parameters are invariant).
Constraining parameters
struct Interval{T<:Real}
lo::T
hi::T
end
width(i::Interval) = i.hi - i.lo
Base.in(x::Real, i::Interval) = i.lo <= x <= i.hi
i = Interval(2, 10)
println(width(i), " ", 5 in i, " ", 11 in i)
try
Interval("a", "b")
catch err
println(typeof(err))
end
8 true false
MethodError
Parametric methods and where
where introduces a type variable in a method signature:
same_type(a::T, b::T) where {T} = "both are $T"
same_type(a, b) = "different types"
println(same_type(1, 2))
println(same_type(1, 2.0))
first_or_zero(v::Vector{T}) where {T<:Number} = isempty(v) ? zero(T) : v[1]
println(first_or_zero(Float64[]), " ", first_or_zero([7, 8]))
sum_all(xs::AbstractVector{<:Real}) = sum(xs)
println(sum_all([1, 2, 3]), " ", sum_all(1:10))
both are Int64
different types
0.0 7
6 55
where {T}names the type so you can use it (likezero(T)).AbstractVector{<:Real}is shorthand for "a vector of any real subtype" — it acceptsVector{Int},Vector{Float64}, and ranges.
Why parameters matter for speed
struct Slow
value # field type Any
end
struct Fast{T}
value::T
end
println(fieldtype(Slow, :value), " ", fieldtype(Fast{Int}, :value))
println(isconcretetype(fieldtype(Fast{Int}, :value)))
Any Int64
true
A field without a type (or with an abstract type) can hold anything, so the compiler can't know what's in it and generates slower code. Parameterizing the field keeps the struct generic and fast — a pattern you'll see throughout Julia code.