パラメトリック型の階層構造

2026年3月21日
1 分

型の階層構造は <: 演算子を使って確認できる。

たとえば次のように、SignedNumberInt の先祖なので 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} の親としてふるまう。

一方、IntNumber には親子関係があるのに、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