8
votes

I'd like to write a simple macro that shows the names & values of variables. In Common Lisp it would be

(defmacro dprint (&rest vars)
  `(progn
    ,@(loop for v in vars
            collect `(format t "~a: ~a~%" ',v ,v))))

In Julia I had two problems writing this:

  1. How can I collect the generated Expr objects into a block? (In Lisp, this is done by splicing the list with ,@ into progn.) The best I could come up with is to create an Expr(:block), and set its args to the list, but this is far from elegant.
  2. I need to use both the name and the value of the variable. Interpolation inside strings and quoted expressions both use $, which complicates the issue, but even if I use string for concatenation, I can 't print the variable's name - at least :($v) does not do the same as ',v in CL...

My current macro looks like this:

macro dprint(vars...)
  ex = Expr(:block)
  ex.args = [:(println(string(:($v), " = ", $v))) for v in vars]
  ex
end

Looking at a macroexpansion shows the problem:

julia> macroexpand(:(@dprint x y))
quote 
    println(string(v," = ",x))
    println(string(v," = ",y))
end

I would like to get

quote 
    println(string(:x," = ",x))
    println(string(:y," = ",y))
end

Any hints?

EDIT: Combining the answers, the solution seems to be the following:

macro dprint(vars...)
  quote
    $([:(println(string($(Meta.quot(v)), " = ", $v))) for v in vars]...)
  end
end

... i.e., using $(Meta.quot(v)) to the effect of ',v, and $(expr...) for ,@expr. Thank you again!

2

2 Answers

10
votes

the @show macro already exists for this. It is helpful to be able to implement it yourself, so later you can do other likes like make one that will show the size of an Array..

For your particular variant: Answer is Meta.quot,

macro dprint(vars...)
     ex = Expr(:block)
     ex.args = [:(println($(Meta.quot(v)), " = ", $v)) for v in vars]
     ex
end

See with:

julia> a=2; b=3;

julia> @dprint a
a = 2

julia> @dprint a b
a = 2
b = 3
3
votes

oxinabox's answer is good, but I should mention the equivalent to ,@x is $(x...) (this is the other part of your question).

For instance, consider the macro

macro _begin(); esc(:begin); end

macro @_begin()(args...)
    quote
        $(args...)
    end |> esc
end

and invocation

@begin x=1 y=2 x*y

which (though dubiously readable) produces the expected result 2. (The @_begin macro is not part of the example; it is required however because begin is a reserved word, so one needs a macro to access the symbol directly.)

Note

julia> macroexpand(:(@begin 1 2 3))
quote  # REPL[1], line 5:
    1
    2
    3
end

I consider this more readable, personally, than pushing to the .args array.