パラメトリック型の階層構造
型の階層構造は <: 演算子を使って確認できる。
たとえば次のように、Signed や Number は Int の先祖なので true になるけど、Float64 とは親子関係がないので false になる。
julia> Int <: Signed
true
julia> Int <: Number
true
julia> Int <: Float64
false
>: という演算子もあって、<: とは左右のオペランドが入れ替わったもの。
さて、パラメトリック型について同じようなことをすると次のようになる。
julia> struct Point{T}
x::T
y::T
end
julia> Point{Int} <: Point
true
julia> Point{Float64} <: Point
true
型パラメータを指定しない Point は、Point{Int} や Point{Float64} の親としてふるまう。
一方、Int と Number には親子関係があるのに、Point{Int} と Point{Number} の間に親子関係はない。
julia> Point{Int} <: Point{Number}
false
当然ながら、Point{Int} と Point{Float64} の間にも親子関係はない。
julia> Point{Int} <: Point{Float64}
false
プログラミング言語において、型の共変(covariant)、不変(invariant)という概念があって、上の例のように(つまり Julia のように)親子関係がないことを不変という。 逆に親子関係があることを共変という。
次のような関数を考えよう。パラメトリック型は不変なので、この関数は期待するようには動作しない。つまり、Point{Int} にも Point{Float64} にもエラーになる。
function distance(p::Point{Number})
# 何かの処理
end
代わりに、型パラメータに制約をつけることができる。<:Number に注目。
function distance(p::Point{<:Number})
# 何かの処理
end
または、次のように書いて同じ。
function distance(p::Point{T}) where T <:Number
# 何かの処理
end
