Escaping in a macro

In the documentation of Julia v1.0 there is almost nothing about escaping in a macro. I tried a simple debug function, which can be inserted at critical points of a function. It should print an expression or variable name, and its current value. I defined a macro in a module, but I get weird messages (b = c =) at loading the module, and within a function only the last value of several calls to the macro is actually shown in REPL. If I type in REPL @db(1+2) or a = falses(2); @db(a) I get the desired effects.

Could anyone point to relevant information or tell, how I such simple macro should be written?

module TestModule
export @db, f

macro db(x)         # only works in REPL, single call in a line
    print(x," = ")
    return :($(esc(x)))
end

function f()
   b = falses(3)
   @db(b)
   c = trues(5)
   @db(c)
end

end

I tried to open a Julia issue about the need of improved documentation of esc(), and about its unexpected behavior, but the issue was immediately closed.

1 Like

This isn’t really an escaping issue at all. Macros are source code transformations, and they get run before your code executes. As a result, your print statements are being run when the macro is run, during the parsing and lowering of your source code.

If you want the macro to actually cause something to be printed at run-time, then that println needs to be inside the expression that the macro returns.

3 Likes
macro db(x)
    return quote
           println($"$x = ", $x)
    end
end

May be more what you are looking for in this particular case. But then again, this is basically Base.@show.

I tried to put more inside the escaped expression and around it, all gave various errors. Could you give an example, how it is done?

Thanks, but the listed macro does not work. It needs escaping, which the documentation does not tell, how to do.

Aah, I had not tested my macro from within a function.

I think that

macro db(x)
    return esc(quote
           println($"$x = ", $x)
    end)
end

should do what you want.

But I, too, feel that I’m mostly trying things around until something works without a deep understanding. A deep and rigorous tutorial would be helpful.

1 Like

or the shorter:

macro db(x)
    return esc(:(println($"$x = ", $x)))
end

You should ESC x not the whole thing.
In general you escape each input once and exactly once.

You may find the source of Base.@show instructive, it does something similar.

If you are ever confused about what your macro is doing, remember to use @macroexpand to see the code that it will give (though I do wish the output of this were easier to ready).

1 Like

Thanks, Niklas! Your macros work. If only we get decent Julia documentation on these.

Given the additional input we have received (thanks everyone!), we should maybe revise the macro to:

macro db(x)
    return :(println($"$x = ", $(esc(x))))
end

I would argue that the documentation is “decent”, even if it could be improved (which is generally true for most documentation).

Escaping is tricky (because it involves hygiene), and more examples could always help, but most of the tools that were suggested in this topic are documented with examples.

Maybe the documentation is good for experts, like you guys, but for a novice user it is just confusing. E.g. I could not find a description of the construct $"…". The function esc() is said to be " Only valid in the context of an Expr returned from a macro". It is not clear, we can use it in assignments to local variables. So, where is it allowed? And so on…

Interpolation is described in https://docs.julialang.org/en/stable/manual/metaprogramming/#Interpolation-1.

I would read through the whole chapter since just spot-reading a particular subsection will not give you the complete picture of meta-programming in Julia.

3 Likes

Macros are tricky, especially when it comes to hygiene. I agree that more documentation would be nice, especially in the form of tutorials, and I guess those will appear in due time now that we have a stable release.

Also, don’t underestimate what you can learn from just reading the source. Base and the standard libraries have examples of complex macros, usually written by people who know Julia well.

That said, perhaps a novice user should wait a while before writing macros. They are not at all essential to using the language and even writing complex and powerful libraries.

1 Like

I would read through the whole chapter

I did, and did not find any example or description of the construct $"…". What did I miss?

a novice user should wait a while before writing macros

Maybe, but such a simple thing as showing an expression and its value could be simple enough for a newb.

The link above takes you there directly, so possibly that part?

I am not sure I agree; perhaps you could show us a language where this is simple. Eg in C, it is rather complex, in Common Lisp it would be simpler than Julia because that language does not have hygiene by default (so more complex macros need to work around that).

I have not seen a language where macros of any kind are novice material.

You are the best expert of missing documentation. Please make a pull request to propose a good example in the right place. However, be prepared to iterate because it seems that a lot of people have an opinion on this matter.

2 Likes

The link above takes you there directly

I read that section, too thoroughly. It says that “Julia allows interpolation of literals or expressions into quoted expressions. Interpolation is indicated by a prefix $ .” Accordingly, (and those are the examples given) you can write $a, $(exp), and $:(expr).

There is nothing about the construct of $"…". This is what I missed, or overlooked. What does it stand for?