I am surprised that promotion does not occur here

Consider the following:

julia> [[1,2,3]//3,[1,2,3]*Int128(1)]
2-element Array{Array{T,1} where T,1}:
 Rational{Int64}[1//3, 2//3, 1//1]
 Int128[1, 2, 3]

Julia cannot compute the type of the combined array, but Julia is able to compute

julia> promote_type(Rational{Int64},Int128)
Rational{Int128}

but curiously this does not carry over to arrays:

julia> promote_type(Vector{Rational{Int64}},Vector{Int128})
Array{T,1} where T

Though this works in a slightly different case:

julia> promote_type(Vector{Int64},Vector{Int128})
Array{Int128,1}

I am surprised and went through the source to find what happens. The stuff happens in typejoin,
which is very complicated and does not seem to use promotion of eltype. Can someone explain that to me, and why the simpler case is handled but not the more complicated one?

1 Like

Seems like it should happen, given that

julia> [[1,2,3]//3,[1,2,3]*0.5]
2-element Vector{Vector{Float64}}:
 [0.3333333333333333, 0.6666666666666666, 1.0]
 [0.5, 1.0, 1.5]

Actually my question is motivated by a problem I have in my code, with different types. I am expecting that when I have defined (or Julia has defined) T=promote_type(T1,T2) (and a promote_rule also) then elements in a mixed vector of T1 and T2 are promoted to T (this happens) and also that mixed Vector{T1} and Vector{T2} in a Vector are promoted to Vector{T} (this is very inconsistent, depending on T1 and T2).

yeah no that should not be expected:

julia> promote_type(BigInt, Float32)
BigFloat

julia> promote_rule(Vector{BigInt}, Vector{Float32})
Vector{T} where T (alias for Array{T, 1} where T)

Basically I think 1) it’s not clear there’s a definitive rule regarding how far should Julia go to promote different nested types such that it appears to be natural and “completed” to our eyes. 2) even if there is a mathematically sound rule, it may be too much cost to *always do it.

1 Like

Ok but then can anyone document what happens? Which examples work and which do not?

1 Like

I believe this is an implementation detail (depending on heuristics). (think of trying to document when will a function be inlined)

Overall, there is never a type-promotion covariance(?) promised by Julia in the docs anywhere to begin with, so there’s no need to document a non-feature.

1 Like

I had in my mind that Julia should have something like

promote_rule(Array{T},Array{T1})=Array{promote_type(T,T1)}

but perhaps it does not make sense…

(sorry my previous example post had a bug that I used promote_rule in both places)

I would have imagined the same, maybe others would agree we should specialize on promote_rule for Arrays.

That mental model seem to work on Real subtypes except for when you mix BigInt and Float** together.

I think it’s the right thing to not promote container types.

julia> [[1,2,3]//3,[1,2,3]*0.5]
2-element Vector{Vector{Float64}}:
 [0.3333333333333333, 0.6666666666666666, 1.0]
 [0.5, 1.0, 1.5]

is fine because it’s the result of a literal evaluation, but let’s put it slightly differently:

julia> vec_rat = [1,2,3]//3;

julia> vec_float = [1,2,3]*0.5;

julia> vecs = [vec_rat, vec_float]
2-element Array{Array{Float64,1},1}:
 [0.3333333333333333, 0.6666666666666666, 1.0]
 [0.5, 1.0, 1.5]

Do you spot a problem?

Now vecs[2] is bound to vec_float but vecs[1] and vec_rat are completely decoupled. WTH?

I don’t spot a problem since promote_type(Rational{Int},Float64)==Float64. I wish this would happen consistently. Do you spot a problem?

The problem is, when one writes c = [a, b], is it fine if c[1] !== a or c[2] !== b when a and b are mutable?

2 Likes

Well, we saw some examples where the promotion happened. I would think there is no problem
when a and b are arrays; or otherwise one should prohibit the examples which worked above.
For me the problem is the inconsistency.

Inconsistency is indeed a problem.
I’d just ignore the existence of type promotion for arrays of arrays and write my own function for the specific case I need, though.

2 Likes

One could argue that the problem you highlight is a question of !== being too coarse a measure of equality for the case in hand.

According to the manual:

39.18 Be careful with type equality
You generally want to use isa and <: for testing types, not ==. Checking types for exact equality typically only makes sense when comparing to a known concrete type (e.g. T == Float64), or if you really, really know what you’re doing.