Hi all,
I’m trying to write a function that accepts an 1d-array/vector of Colors. I want to use the most abstract type Colorant
from ColorTypes.jl; i.e. like this:
using Colors
using ColorTypes
function test(colors::Vector{Colorant})
# do stuff
end
However, when I try to specify a list of concrete colors, I get an error:
c = [colorant"red", colorant"blue"]
typeof(c) # Array{RGB{FixedPointNumbers.Normed{UInt8,8}},1}
test(c) # >MethodError: no method matching test(::Array{RGB{FixedPointNumbers.Normed{UInt8,8}},1})
I find this in particular weird because the individual elements (colors) of the vector are indeed of abstract type Colorant
:
typeof(c[1])<:Colorant # true
typeof(c[2])<:Colorant # true
What am I doing wrong?
I’m on Julia 1.3.1, ColorTypes v0.9.1 and Colors v0.11.2.
edit: As an additional remark, replacing the Colorant
type in function test
with type Any
still throws the same error (?!).
Use Vector{<:Colorant}
or (even more general) AbstractVector{<:Colorant}
.
1 Like
Ahh okay, so the problem is that I ask for the abstract type Colorant
itself and not for potential subtypes?
This also seems to work:
function test(colors::Vector{T}) where T<:Colorant
# do stuff
end
These are probably all kind of equivalent solutions?
colors::Vector{Colorant}
means that each element of colors
may be a different subtype of Colorant
. Internally, therefore, it is an array of pointers to “boxes” (values with a type tag), a different box for each element. (Operations on such an array are consequently rather slow because the compiled code needs to look up the type of each element at runtime.) Vector{Colorant}
is one specific concrete type, and no other type is allowed.
In contrast a parameter colors::AbstractVector{<:Colorant}
is declaring that the argument can be any one of a set of types V{T}
, for any V <: AbstractVector
and T <: Colorant
. A Vector{Colorant}
is allowed, but so is a Vector{RGB{Float64}}
, for example. For any particular type of colors
that is passed, the compiler will generate a different specialized implementation (one for each type). For example, a colors::Vector{RGB{Float64}}
is stored as an array of “inlined” (r,g,b)
triplets (3 Float64
values in a row for each color) with no pointers or type tags for individual elements, and since the Colorant
type is known at compile time the generated code can be quite efficient (as efficient as operating in C on an array of struct { double r; double g; double b; }
values).
See also the discussion here, for example, and the Julia manual on Parametric Types.
2 Likes
Thanks a lot for the explanation and links! This is really helpful (and important to understand). I’ll read this part of the manual more thoroughly this time