`Replace` function defaulting to type Any when using broadcasting, but not with a for-loop

I had a situation the other day where I had a 2D vector of integers, and I wanted to use the replace function with a list of replacement pairs, on the integers in the inner vectors.

If I use a for-loop list comprehension, Julia can correctly determine the type of the output:

function replace_listcomp(r :: Vector{Vector{Int}}, p :: Vector{Pair{Int, Int}})
    result = [replace(rr, p...) for rr in r]
    return result
end

julia> @code_warntype replace_listcomp([Int[]], Pair{Int, Int}[])
# the output is long, but the relevant bit is:
  result::Vector{Vector{Int64}}
Body::Vector{Vector{Int64}}

However, if I try to do the same thing using broadcasting instead … suddenly, Julia can’t determine the output type, and defaults to Any:

function replace_broadcast(r :: Vector{Vector{Int}}, p :: Vector{Pair{Int, Int}})
    result = replace.(r, p...)
    return result
end

julia> @code_warntype replace_broadcast([Int[]], Pair{Int, Int}[])
# the relevant part again:
Locals
  result::Any
Body::Any #(these show up in red in the terminal)

Why is this happening? It seems like it should be able to determine the output type, but can’t for some reason. Is this a bug in Julia?

(PS: I already have an alternate solution for my situation that avoids the replace function entirely, but I figured I’d ask anyway because I’m curious about what’s happening here.)

1 Like

Your code (without @code_warntype) does not run:

Is that intentional?

julia> function replace_listcomp(r :: Vector{Vector{Int}}, p :: Vector{Pair{Int, Int}})
           result = [replace(rr, p...) for rr in r]
           return result
       end
replace_listcomp (generic function with 1 method)

julia> replace_listcomp([Int[]], Pair{Int, Int}[])
ERROR: MethodError: no method matching promote_valuetype()
Closest candidates are:
  promote_valuetype(::Pair{K, V}) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:580
  promote_valuetype(::Pair{K, V}, ::Pair...) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:581
Stacktrace:
 [1] replace(::Vector{Int64}; count::Nothing)
   @ Base ./set.jl:570
 [2] replace(::Vector{Int64})
   @ Base ./set.jl:570
 [3] #3
   @ ./array.jl:0 [inlined]
 [4] iterate
   @ ./generator.jl:47 [inlined]
 [5] collect(itr::Base.Generator{Vector{Vector{Int64}}, var"#3#4"{Vector{Pair{Int64, Int64}}}})
   @ Base ./array.jl:724
 [6] replace_listcomp(r::Vector{Vector{Int64}}, p::Vector{Pair{Int64, Int64}})
   @ Main ./REPL[7]:2
 [7] top-level scope
   @ REPL[8]:1

julia> function replace_broadcast(r :: Vector{Vector{Int}}, p :: Vector{Pair{Int, Int}})
           result = replace.(r, p...)
           return result
       end
replace_broadcast (generic function with 1 method)

julia> 

julia> replace_broadcast([Int[]], Pair{Int, Int}[])
ERROR: MethodError: no method matching promote_valuetype()
Closest candidates are:
  promote_valuetype(::Pair{K, V}) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:580
  promote_valuetype(::Pair{K, V}, ::Pair...) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:581
Stacktrace:
 [1] replace(::Vector{Int64}; count::Nothing)
   @ Base ./set.jl:570
 [2] replace(::Vector{Int64})
   @ Base ./set.jl:570
 [3] _broadcast_getindex_evalf
   @ ./broadcast.jl:670 [inlined]
 [4] _broadcast_getindex
   @ ./broadcast.jl:643 [inlined]
 [5] getindex
   @ ./broadcast.jl:597 [inlined]
 [6] copy
   @ ./broadcast.jl:899 [inlined]
 [7] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(replace), Tuple{Vector{Vector{Int64}}}})
   @ Base.Broadcast ./broadcast.jl:860
 [8] replace_broadcast(r::Vector{Vector{Int64}}, p::Vector{Pair{Int64, Int64}})
   @ Main ./REPL[9]:2
 [9] top-level scope
   @ REPL[10]:1
julia> function replace_listcomp(r :: Vector{Vector{Int}}, p :: Vector{Pair{Int, Int}})
           result = [replace(rr, p...) for rr in r]
           return result
       end
replace_listcomp (generic function with 1 method)

julia> replace_listcomp([Int[]], Pair{Int, Int}[])
ERROR: MethodError: no method matching promote_valuetype()
Closest candidates are:
  promote_valuetype(::Pair{K, V}) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:580
  promote_valuetype(::Pair{K, V}, ::Pair...) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:581
Stacktrace:
 [1] replace(::Vector{Int64}; count::Nothing)
   @ Base ./set.jl:570
 [2] replace(::Vector{Int64})
   @ Base ./set.jl:570
 [3] #3
   @ ./array.jl:0 [inlined]
 [4] iterate
   @ ./generator.jl:47 [inlined]
 [5] collect(itr::Base.Generator{Vector{Vector{Int64}}, var"#3#4"{Vector{Pair{Int64, Int64}}}})
   @ Base ./array.jl:724
 [6] replace_listcomp(r::Vector{Vector{Int64}}, p::Vector{Pair{Int64, Int64}})
   @ Main ./REPL[7]:2
 [7] top-level scope
   @ REPL[8]:1

julia> function replace_broadcast(r :: Vector{Vector{Int}}, p :: Vector{Pair{Int, Int}})
           result = replace.(r, p...)
           return result
       end
replace_broadcast (generic function with 1 method)

julia> 

julia> replace_broadcast([Int[]], Pair{Int, Int}[])
ERROR: MethodError: no method matching promote_valuetype()
Closest candidates are:
  promote_valuetype(::Pair{K, V}) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:580
  promote_valuetype(::Pair{K, V}, ::Pair...) where {K, V} at ~/Programme/julia-1.7.0/share/julia/base/set.jl:581
Stacktrace:
 [1] replace(::Vector{Int64}; count::Nothing)
   @ Base ./set.jl:570
 [2] replace(::Vector{Int64})
   @ Base ./set.jl:570
 [3] _broadcast_getindex_evalf
   @ ./broadcast.jl:670 [inlined]
 [4] _broadcast_getindex
   @ ./broadcast.jl:643 [inlined]
 [5] getindex
   @ ./broadcast.jl:597 [inlined]
 [6] copy
   @ ./broadcast.jl:899 [inlined]
 [7] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(replace), Tuple{Vector{Vector{Int64}}}})
   @ Base.Broadcast ./broadcast.jl:860
 [8] replace_broadcast(r::Vector{Vector{Int64}}, p::Vector{Pair{Int64, Int64}})
   @ Main ./REPL[9]:2
 [9] top-level scope
   @ REPL[10]:1

This seems to run but produces the same red line with @code_warntype.
EDIT:

julia> replace_broadcast([Int[1]], Pair{Int, Int}[1 => 1])
1-element Vector{Vector{Int64}}:
 [1]

julia> replace_listcomp([Int[1]], Pair{Int, Int}[1 => 1])
1-element Vector{Vector{Int64}}:
 [1]

I was playing around, and replacing your vector p with a tuple seems to fix the instability.

function replace_listcomp(r :: Vector{Vector{Int}}, p)
    result = [replace(rr, p...) for rr in r]
    return result
end

function replace_broadcast(r :: Vector{Vector{Int}}, p)
    result = replace.(r, p...)
    return result
end

function main()
    r = [[1], [2]]
    p = (2 => 20, 1 => 10) 
    @show replace_listcomp(r, p)
    @show replace_broadcast(r, p)
        
    return nothing
end

# REPL
julia> main()
replace_listcomp(r, p) = [[10], [20]]
replace_broadcast(r, p) = [[10], [20]]

julia> @code_warntype replace_broadcast([Int[1], Int[2]], (1=>12, 2=>22))
MethodInstance for replace_broadcast(::Vector{Vector{Int64}}, ::Tuple{Pair{Int64, Int64}, Pair{Int64, Int64}})
  from replace_broadcast(r::Vector{Vector{Int64}}, p) in Main at /home/fxw/lel.jl:6
Arguments
  #self#::Core.Const(replace_broadcast)
  r::Vector{Vector{Int64}}
  p::Tuple{Pair{Int64, Int64}, Pair{Int64, Int64}}
Locals
  result::Vector{Vector{Int64}}
Body::Vector{Vector{Int64}}
1 ─ %1 = Core.tuple(Main.replace, r)::Tuple{typeof(replace), Vector{Vector{Int64}}}
│   %2 = Core._apply_iterate(Base.iterate, Base.broadcasted, %1, p)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(replace), Tuple{Vector{Vector{Int64}}, Base.RefValue{Pair{Int64, Int64}}, Base.RefValue{Pair{Int64, Int64}}}}
│        (result = Base.materialize(%2))
└──      return result
1 Like

Yeah, @code_warntype only looks for the types of the values, not the values themselves, when you call it - which is why I simply put empty vectors that match the types.

To actually use it you’d have to put values into those vectors (which I see you did in your edit). Since @code_warntype looks at the types only, it will produce the same output.

1 Like