Is that an issue in promote()?

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

julia> Base._promote(1,missing)
(1, missing)

julia> versioninfo()
Julia Version 1.3.1
Commit 2d5741174c (2019-12-30 21:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i3 CPU         540  @ 3.07GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, westmere)
Environment:
  JULIA_NUM_THREADS = 8


What do you think the issue is? Those two have no (non-Union) common type they can be converted to.

1 Like

Before that check was added, promote was far and away the most common source of StackOverflowErrors:

some_math_operation(x::T, y::T) where T<:Number = ...
some_math_operation(x, y) = some_math_operation(promote(x, y)...)

If promote(x, y) didn’t check for a change in any argument types, the second of these would call itself in an infinite loop. StackOverflowErrors are nasty (sometimes very slow to resolve, sometimes they corrupt more than you’d like), so it’s much better to have it error immediately.

2 Likes

So if I want to call promote() on the elements of the array, leaving missing values alone, would calling Base._promote()[1] be the recommended way, or rather an explicit implementation like

my_promote(x::T, y::S) where {T,S} = begin
    R = promote_type(T, S)
    return convert(R, x)
end

It would be useful to have some context on the problem — what are you trying to do? You may not need to promote manually at all.

Assume that I know what I need to do.

That doesn’t sound great because you’ll only convert pairwise, which is no guarantee that all types will be the same. I’d consider a function with body something like

T = Union{}
for a in A
    ismissing(a) && continue
    T = promote_type(T, typeof(a))
end

and then convert the entire thing to Array{Union{T,Missing},N}.

It is a reduce-type operation, with accumulator value promoted as the array is processed, rather than the whole array converted at once. Skipping of the missing values is done elsewhere.

Is there a reason you aren’t using foldl or similar? This sounds like a problem base Julia can deal with well.

This is the function to be supplied to mapreduce as the first argument.

Well, it sounds like you know what to do for whatever it is you’re trying to do. The bottom line is that, no, promote isn’t broken, and yes, you’re allowed to call promote_type yourself and do whatever you want with the answer.

1 Like

So is it better to partially reimplement Base._promote like this, or call the internal function itself?

It’s usually best to stick with the documented & supported interface, there is no guarantee that internals won’t change.

People here including @Tamas_Papp are trying to help; I don’t think it was necessary to shut down his question like that.

3 Likes