Skip to content
AgileLogics
6 min read

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 T from the arguments; Point(1, 2.5) fails because x and y must have the same T.
  • Point{Int} is a subtype of Point, but not of Point{Real} even though Int <: 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 (like zero(T)).
  • AbstractVector{<:Real} is shorthand for "a vector of any real subtype" — it accepts Vector{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.