How to use destructuring assignment with macro?

question
macros

#1

I expect this macro to define variables with same characters, but it doesn’t work.
How should I write this?

macro chars(cs...)
    return :( $(esc(cs)) = $(map(c->string(c)[1], cs)) )
end

@chars a b c # is '(a,b,c) = ('a','b','c')

#2
macro chars(cs...)
    return :( $(esc(Expr(:tuple, cs...))) = $(map(c->string(c)[1], cs)) )
end

@chars a b c

I did

dump(quote 
        a,b,c = 1,2,3
        end)

to figure it out.


#3

Thank you. I have a question. What difference is it between cs and Expr(:tuple, cs...)? cs returns (:a :b :c). This is a tuple of symbols, isn’t it? I guess that Expr(:tuple, cs...) is a tuple of symbols too. Why did cs fail?


#4
julia> dump(:((:a,:b,:c)))
Expr
  head: Symbol tuple
  args: Array{Any}((3,))
    1: QuoteNode
      value: Symbol a
    2: QuoteNode
      value: Symbol b
    3: QuoteNode
      value: Symbol c
  typ: Any

vs

julia> dump(Expr(:tuple, :a, :b, :c))
Expr
  head: Symbol tuple
  args: Array{Any}((3,))
    1: Symbol a
    2: Symbol b
    3: Symbol c
  typ: Any

If you find Julia ASTs confusing, you are not alone :smile:


#5

:smiley: This one had me scratching my head:

julia> dump(:(a,b,c = 1,2,3))
Expr
  head: Symbol tuple
  args: Array{Any}((5,))
    1: Symbol a
    2: Symbol b
    3: Expr
      head: Symbol =
      args: Array{Any}((2,))
        1: Symbol c
        2: Int64 1
      typ: Any
    4: Int64 2
    5: Int64 3
  typ: Any

#6

Julia does not do left-hand-side destructuring on tuples (which cs is); it does destructuring on Expr objects. It is confusing, but not much can be done about it. As Stefan said, Julia suffers from “the curse of syntax”.


#7

I got it. Macros works on Expr which is not Tuple, of cause, and is not Array too. To write macro in Julia, I have to takes care of Expr only,


#8

I saw that. My saying was wrong, the first is a tuple of quoted symbol, isn’t it? Expr doesn’t quote its arguments like (:a :b :c), while : quotes its arguments like (:(:a), :(:b), :(:c)). Julia’s expanding rules confuse me, but your link is so helpful for me. Thank you!


#9

In general it is always much more robust, precise, and reliable to use the Expr constructor, so I recommend always trying out dump and then using the constructor if you want to do reliable metaprogramming.


#10

I generally think the opposite; since I am not sure how stable the internal representation of the AST is going to be, I am always reluctant to use Expr, and prefer when something else is available (eg Meta.quot, splicing into expressions, etc).


#11

My experience is different. Expr seems to be more reliable. For example, when abstract Name was changed to abstract type Name end, the Expr(:abstract,:Name) would still work in both the old and new versions of Julia, so therefore the Expr way of doing it is more reliable and robust in the long run.