# Why exactly is promote(1:2,[1,2]) designed to throw an error?

The call `promote(1:2,[1,2])` throws an error. As far as I understand, this is because on the one hand `promote_type(UnitRange{Int64},Array{Int64})` returns `AbstractArray{Int64,N} where N`, whereas on the other hand both `convert(AbstractArray{Int64},1:2)` and `convert(AbstractArray{Int64},[1,2])` will not change their input as they already both belong to `AbstractArray{Int64}`.

I understand that there is basically no reason why promote should do anything, as these types of abstract arrays are already compatible in a certain sense, however, the original `promote` call then throws an error as the output types are not equal. I wonder a bit why is is.

Lets assume that we declare two (or especially more) structs `A` and `B` that are involved in a certain type hierarchy (both below the abstract type `U`), both of which wrap any kind of abstract array and contain different additional information. Functions on `A` and `B` may work differently, but mathematically the output is determined only by the contained arrays. Given specific mathematical properties of each the array one captures in `A` and `B` however, one may do so much faster.

The properties capured by `B` are thought to be weaker than `A`, such that one might define `foo(a::U,b::U) = foo(promote(a,b)...)` where we have some `foo(a1::A, a2::A)`.

One could now define `promote_rule(::Type{A},::Type{B}) = A` as well as `convert(::Type{A}, b::B)` as something that turns `b` into the type `A` (basically just recalculating the properties captured in `A`). However, this is different behavior compared to the AbstractArray behaviour above as we would expect `promote_rule(::Type{A},::Type{B}) = U`, and even when one captures the type of the array contained as `A{T}` and `B{S}`, then `promote_rule(::Type{A{T}},::Type{B{S}}) = U{promote_type(T,S)}` will still fail as one runs into the problem explained in the beginning. Is this something promotion should not be used for, and if so, how can one circumvent this problem? After all, there might be many further types thought lower in the hierarchy.

Is it better to manually convert everything into type `A` explicitly in the beginning of `foo(u1::U,u2::U)` to then call `foo(a1::A, a2::A)` and add a function `foo(b1::B,b2::B)`? This however feels like imitating promotion in a manual, maybe improper way. It there a kind of “weak” promotion concept that does not enforce equal output types? (thereby however going against the original idea of promotion in the first place)

Or is there a fundamentally different way how one should do these things anyway? The example above might not be the most simple one to choose, but please pardon me some confusion about this on my side despite reading the manual.

FWIW, this got fixed in either 1.8 or 1.9:

``````julia> promote(1:2, [1,2])
([1, 2], [1, 2])
``````
3 Likes

Thank you for pointing out that this will be changed! So what promote should really do is convert anything being an `AbstractArray` into an actual `Array` if necessary?

I would expect this idea to follow the general rule that given a type chain

``````A <: AbstractA
AbstractB <: AbstractA
B <: AbstractB
AbstractC <: AbstractB
C <: AbstractC
``````

then canonically, we expect:
`promote_type(C,B) = AbstractB` where the type of `convert(::Type{AbstractB},::C)` is `B`,
`promote_type(C,A) = AbstractA` where the type of `convert(::Type{AbstractA},::C)` is `A`,
`promote_type(B,A) = AbstractA` where the type of `convert(::Type{AbstractA},::B)` is `A`.

Is this the right way of thinking?

However, this leads me to a second question. In original example described in my first post, I would actually desire to only promote `(A,B)` instances to `(A,A)` without changing the underlying type of the array - so to speak only promote the first level type, and not necessarily the eltype. That is, to only promote `(A{T},B{S})` instances to `(A{T},A{S})`. This however seems impossible given the complete type equality required for the return values of promote calls.