Problem using map. with Expr.args

Hi,
I am strugling with a strange behavior of map.() applied to the args part of an expression (I’m writing a code parser and need to recurse on Expressions)

Here is a small piece of code to illustrate the problem:

Let us have a two arguments function:

function f(x,y)
       println("x= ", x, " y=", y)
       end

And call it as:

a = [ 1, 2, 3]
map.(f, 3, a)

The result (as expected) is:

x= 3 y=1
x= 3 y=2
x= 3 y=3
3-element Vector{Nothing}:
 nothing
 nothing
 nothing

If I run it on an expression:

a = :( a + b + c)
:(a + b + c)

julia> map.(f, 3, a.args)
map.(f, 3, a.args)
ERROR: MethodError: no method matching length(::Symbol)
Closest candidates are:
  length(::Union{Base.KeySet, Base.ValueIterator}) at abstractdict.jl:58
  length(::Union{LinearAlgebra.Adjoint{T, S}, LinearAlgebra.Transpose{T, S}} where {T, S}) at /Applications/Julia-1.8.app/Contents/Resources/julia/share/julia/stdlib/v1.8/LinearAlgebra/src/adjtrans.jl:172
  length(::Test.GenericDict) at /Applications/Julia-1.8.app/Contents/Resources/julia/share/julia/stdlib/v1.8/Test/src/Test.jl:1914
  ...
Stacktrace:
  [1] _zip_min_length(is::Tuple{Symbol})
    @ Base.Iterators ./iterators.jl:340
  [2] _zip_min_length(is::Tuple{Int64, Symbol})
    @ Base.Iterators ./iterators.jl:336
  [3] length(z::Base.Iterators.Zip{Tuple{Int64, Symbol}})
    @ Base.Iterators ./iterators.jl:330
  [4] length(g::Base.Generator{Base.Iterators.Zip{Tuple{Int64, Symbol}}, Base.var"#4#5"{typeof(f)}})
    @ Base ./generator.jl:50
  [5] _similar_shape(itr::Base.Generator{Base.Iterators.Zip{Tuple{Int64, Symbol}}, Base.var"#4#5"{typeof(f)}}, #unused#::Base.HasLength)
    @ Base ./array.jl:663
  [6] collect(itr::Base.Generator{Base.Iterators.Zip{Tuple{Int64, Symbol}}, Base.var"#4#5"{typeof(f)}})
    @ Base ./array.jl:786
  [7] map(::Function, ::Int64, ::Symbol)
    @ Base ./abstractarray.jl:3055
  [8] _broadcast_getindex_evalf
    @ ./broadcast.jl:670 [inlined]
  [9] _broadcast_getindex
    @ ./broadcast.jl:643 [inlined]
 [10] getindex
    @ ./broadcast.jl:597 [inlined]
 [11] copy
    @ ./broadcast.jl:899 [inlined]
 [12] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(map), Tuple{Base.RefValue{typeof(f)}, Int64, Vector{Any}}})
    @ Base.Broadcast ./broadcast.jl:860
 [13] top-level scope
    @ REPL[22]:1

What is wrong with my approch ?

P.S.: I found a simple workaround, which works perfectly (I just create “on the fly” this F() function in my parser)

function F(x)
  f(3, x)
end

map(F, a.args)
x= 3 y=+
x= 3 y=a
x= 3 y=b
x= 3 y=c
4-element Vector{Nothing}:
 nothing
 nothing
 nothing
 nothing

But I am interested with your inputs (and wish to learn the right way to use map. on julia’s expressions).

Regards.

You are broadcasting map, i.e., your call will do something like this

[map(f, 3, x) for x in a.args]

and this fails for x being the symbol :+, i.e.,

julia> map(f, 3, 4)
x= 3 y=4

julia> map(f, 3, :+)
ERROR: MethodError: no method matching length(::Symbol)

In your case, you probably want to broadcast f directly, i.e., without the map:

julia> f.(3, [1, 2, 3])  # same as map.(f, 3, [1, 2, 3])
x= 3 y=1
x= 3 y=2
x= 3 y=3
3-element Vector{Nothing}:
 nothing
 nothing
 nothing

julia> f.(3, :(a + b + c).args)
x= 3 y=+
x= 3 y=a
x= 3 y=b
x= 3 y=c
4-element Vector{Nothing}:
 nothing
 nothing
 nothing
 nothing
1 Like

Thanks for your reply