0

I want to make a macro to substitute a long expression used in several functions to improve code readability. Each function has the same name for a certain argument, which is used in the macro, but when compiling the function Julia says the variable used in the macro is undefined even when escaped. Minimal example:

julia> macro mtest()
    esc(x)
end
@mtest (macro with 1 method)

julia> ftest(x) = @mtest
ERROR: UnderVarError: x not defined

Now it gets weird:

julia> x = 1
1
julia> @mtest
1
julia> ftest(x) = @mtest
ftest (generic function with 1 method)
julia> ftest(2)
1

Why doesn't this function definition simply evaluate to ftest(x) = x? How can I tell the macro to use the x from the scope of the calling function rather than from the REPL? I want to use a macro to simply substitute a literal block of text, as in the C library I am using:

#define CHECK_STUFF \
    big \
    complicated \
    expression \
    involving x;

void func(x, y, z) {
    //stuff
    CHECK_STUFF
    //more stuff
}

In this case CHECK_STUFF must be macro, not a function, because it contains a goto to a label in func. My task is to translate this to Julia.

Matthew Bedford
  • 697
  • 6
  • 15
  • In the macro, `x` need to be the symbol `:x`. Your macro refers to the global variable `x`, which only exists after you defined it in the REPL. See also http://stackoverflow.com/q/37358528/6172490. – tim Nov 17 '16 at 18:19
  • To be more specific, variables used in a macro refer to the global variables in the module where the macro is defined. In your case it is defined in the main module and thus refers to `x` defined there. Your macro then returns the expression consisting only of the literal value of `x`. Escaping doesn't change anything in that case. See the output of `macroexpand`, which is very helpful when writing macros. – tim Nov 17 '16 at 18:41

1 Answers1

1

Your macro computes a value, not an expression. Thus, since macros run at compile time and not at runtime, it will essentially look to esc(x) at compile time but, since x is 1 at compile time, your function will compile to f(x)=1. What you want to instead do is have your macro return the expression x so that way your function compiles to f(x)=x. That is:

macro mtest()
    quote 
       x
    end
end

will work. While you could just have this as :x, this syntax scales really nicely.

In fact, I tend to program macros in a "quote later" way. You can first write your macro and test it in the REPL without it returning an expression and instead returning the value. That's what you did here. It's easier to then get it working. However, for it to actually be useful in a function, it has to return an expression. So the easiest way is to do what I just showed: take what you had and put quote end around it. Now, if you had arguments for your macro you then need to modify your expression so that way you interpolate the values correctly. But if the general algorithm already worked, this part won't be that difficult (and I have left a trail of breadcrumbs for how to interpolate many odd things on SO, so just search around if you want to interpolate weird stuff).

Chris Rackauckas
  • 18,645
  • 3
  • 50
  • 81