Base.replace issue with type promotion

If I pass a function to replace then it doesn’t seem to promote the type of the vector.

julia> x = rand([nothing, 1,2,3], 10000);

julia> foo(v) = replace(x -> x === nothing ? missing : x, v)
foo (generic function with 1 method)

julia> foo(x)
ERROR: MethodError: Cannot `convert` an object of type Missing to an object of type Int64
Closest candidates are:
  convert(::Type{T<:Number}, ::T<:Number) where T<:Number at number.jl:6
  convert(::Type{T<:Number}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T<:Integer}, ::Ptr) where T<:Integer at pointer.jl:23
  ...
Stacktrace:
 [1] convert(::Type{Union{Nothing, Int64}}, ::Missing) at ./some.jl:22
 [2] setindex!(::Array{Union{Nothing, Int64},1}, ::Missing, ::Int64) at ./array.jl:767
 [3] _replace!(::getfield(Main, Symbol("##15#16")), ::Array{Union{Nothing, Int64},1}, ::Array{Union{Nothing, Int64},1}, ::Int64) at ./set.jl:628
 [4] #replace#251 at ./set.jl:572 [inlined]
 [5] replace at ./set.jl:572 [inlined]
 [6] foo(::Array{Union{Nothing, Int64},1}) at ./REPL[32]:1
 [7] top-level scope at none:0

Since then I’ve figured a better way to use the replace function but I am still curious why the above usage fails.

julia> bar(v) = replace(v, nothing => missing)
1 Like

The output container eltype is easier to deduce for the Pair-based signature so special handling has been added to Base for that. For the version of replace which takes an arbitrary function this is more difficult and Base simply calls similar but this results in the bug you’ve noticed.

For your original version, you can just use map instead of replace which works correctly. The only difference seems to be the presence of the count keyword.

foo(v) = map(x -> x === nothing ? missing : x, v)
1 Like