Consider the type of the resulting Vector
in these three cases:
[1, 1.0] # case 1: Vector{Float64}, elements are promoted
v = Any[1, 1.0]
[v...] # case 2: gives identical result to case 1
[x for x in v] # case 3: Vector{Real}, elements are not promoted
My first question is, what is the logic behind case 3 being different from case 1 and case 2?
In any case, I would like to replicate behavior analogous to this with custom types. I was able to reproduce the splatting behavior (case 1/2), by implementing methods for promote_rule
and convert
, but I do not know how to achieve the behaivor analogous to case 3 for list comprehension.
Let me walk through an example implementing the desired promotion for splatting (but failing for list comprehension):
struct Foo{T}
x::T
end
f1 = Foo(1)
f2 = Foo(1.0)
Note that at this stage these three cases all give the same Vector{Foo}
:
[f1, f2] # case1
[Any[f1, f2]...] # case 2
[x for x in Any[f1, f2]] # case 3
However, the desired behavior, in analogy with the first Number
example, is that the first two cases should give a Vector{Foo{Float64}}
by promoting the elements, and the last case should give a Vector{Foo{Real}}
leaving the elements untouched. To achieve the right behavior for case 1 & 2, I implement promote_rule
and convert
methods (following examples given in the docs):
Base.promote_rule(::Type{Foo{T}}, ::Type{Foo{S}}) where {T, S} =
Foo{promote_type(T, S)}
Base.convert(::Type{F}, f::Foo) where {F <: Foo} = F(f)
Foo{T}(f::Foo) where {T} = Foo{T}(f.x)
Now case 1 and case 2 correctly promotes the elements to Vector{Foo{Float64}}
, however, case 3 gives an error
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Foo{Int64}
This error came as a surprise. Why does it end up attempting to convert an Int
to a Foo
here? How do I get the desired result of case 3 resulting in a Vector{Foo{Real}}
with elements Foo{Int64}(1), Foo{Float64}(1.0)
in analogy with Number
s?