Promote not working for nothing, while promote_type works

julia> promote(nothing, 4)
ERROR: promotion of types Nothing and Int64 failed to change any arguments
Stacktrace:
 [1] error(::String, ::String, ::String) at ./error.jl:42
 [2] sametype_error(::Tuple{Nothing,Int64}) at ./promotion.jl:306
 [3] not_sametype(::Tuple{Nothing,Int64}, ::Tuple{Nothing,Int64}) at ./promotion.jl:300
 [4] promote(::Nothing, ::Int64) at ./promotion.jl:283
 [5] top-level scope at none:0

julia> promote(typeof(nothing), typeof(4))
(Nothing, Int64)

Is there a reasons why it is not allowed to promote types to a Union type?

promote return values, but there are no values of type Union{T1, T2} for T1 != T2.

1 Like

What would be the expected result of this promotion in your view?

1 Like

I guessed it would return the same values.

promote(nothing, 4) == (nothing, 4)

My imagined usecase is to preallocate an Array with type from promote_type and to cast all values with promote respectively.

Instead of guessing, please read ?promote:

If no arguments can be converted, an error is raised.

The motivation is that returning the same values would lead to infinite recursion in fallback methods that try to promote before dispatch.

Hi @Tamas_Papp please don’t assume bad behaviour. I am feeling slightly offended by your phrasing. Guessing is a really good thing to do and I myself would like to encourage everyone not only to read, but also to think.

So let me apply what you quoted and what I also read before:

  • Can nothing be converted to Union{Nothing, Int}? yes it can
  • Can 4 be converted to Union{Nothing, Int} yes it can
  • can all arguments be converted? yes they can

The motivation you mentioned is something completely different, and I haven’t known that yet and also couldn’t find it in the documentation. Sounds very interesting.

Can you point out where the infinite recursion would happen if I would enable promote(nothing, 4) == (nothing, 4)?

foo(x::T, y::T) where T = x + 2*y
foo(x, y) = foo(promote(x, y)...)
foo(1, nothing) # would go into an infinite loop without an error

That’s not how it works (eg everything can trivially be “converted” to a common type Any, so by your logic promote could just return its arguments and call it a day).

awesome! understood. Yes, and this seems the common usecase of promote. Still it is not mentioned in the documentation to the best of my understanding. To repeat, the documentation says

promote(xs…)
Convert all arguments to a common type, and return them all (as a tuple). If no arguments can be converted, an error is raised.

The open question is, what is this common type? I would have guessed it is promote_type(typeof.(xs)...). And then, at least in mind, the logic would apply which I sketched.

In this sense I would argue that the documentation is crucially incomplete. promote seems to be made for exactly the foo use case you described, and hence the common type needs to be a concrete type.

Yes, perhaps that should be clarified. Please consider making a small PR to the docs.

1 Like