$(Main.r) vs $(r) -- confusion

I am trying to write a logging-type macro that will print an informational string depending on a numerical condition. (Yes, I am aware of the Logging module :-).) Using the @assert macro as a starting point I have essentially made minimal changes to it so that the string is printed using println(). Yet I see differences between the expanded version of the two.

For example, my @trace macro gets expanded to

julia> @macroexpand @trace 8 "$r, $c"  # 8 < 100 so print string
:(Main.println("$(Main.r), $(Main.c)"))

while in a similar situation @assert yields

julia> @macroexpand @assert 9==8 "$r, $c"
:(if 9 == 8
      nothing
  else
      Base.throw(Base.AssertionError((Base.Main).Base.string("$(r), $(c)")))
  end)

While @assert functions correctly my @trace does not with the REPL reporting:

ERROR: UndefVarError: r not defined
Stacktrace:
 [1] top-level scope at REPL[52]:1

There must be something I do not understand because the only difference I detect between the two strings returned is $(Main.r) versus $(r). Can this be the cause of failure for my @trace when the macros are called from a file? And if so, how can I fix it?

Many thanks.

If you take a look at the definition of @assert from Base, you’ll note it returns an expression. This is how macros that should work “at runtime” are written. Although you didn’t provide the definition of @trace (which would be helpful), I expect it performs the “work” of the macro at macro expansion time rather than runtime.

macro assert(ex, msgs...)
    msg = isempty(msgs) ? ex : msgs[1]
    if isa(msg, AbstractString)
        msg = msg # pass-through
    elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol))
        # message is an expression needing evaluating
        msg = :(Main.Base.string($(esc(msg))))
    elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg)
        msg = Main.Base.string(msg)
    else
        # string() might not be defined during bootstrap
        msg = quote
            msg = $(Expr(:quote,msg))
            isdefined(Main, :Base) ? Main.Base.string(msg) :
                (Core.println(msg); "Error during bootstrap. See stdout.")
        end
    end
    return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg)))
end

Agreed. Sorry, but I feared too much information / noise, which is why I held off. Thank you for the explanation although I am not getting the distinction. Is there a place in the documentation you could point me to, please? FWIW my macro definition is:

macro trace(tv, msgs...)
    msg = isempty(msgs) ? "empty trace message" : msgs[1]
    if isa(msg, AbstractString)
        msg = msg # pass-through
    elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol))
        # message is an expression needing evaluating
        msg = :(Main.Base.string($(esc(msg))))
    elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg)
        msg = Main.Base.string(msg)
    end
    return :($(esc(tv)) > TRACELIMIT ? $(nothing) : println(string($(msg))))
end

As you can see, it is a pirated copy of @assert :slight_smile:
Thank you again.

I’m not seeing the same thing you are. If I define a global const TRACELIMIT, I see:

julia> @macroexpand @trace 8 "$r, $c"
:(if 8 > Main.TRACELIMIT
      nothing
  else
      Main.println(Main.string((Main.Main).Base.string("$(r), $(c)")))
  end)

julia> let r = 1, c = 2
          @trace 8 "$r, $c"
       end
1, 2

Which seems correct. I’m on julia 1.5 and 1.6

Yes, this is what I am seeing now, too. I’m sorry but I must have been confused yesterday and posted the wrong macro output. Thank you for looking at this anyway.

1 Like

No problem :slight_smile:
Glad it works the way you expected after all.