Chris Rackauckas
Chris Rackauckas

Reputation: 19132

Macro for a function template: escaping issues

I am trying to define a macro for a function template, i.e. a template where you can easily drop in relevant code but don't want to expose all of the nitty-gritty implementation details to the user. For example, I would like something like this that drops an expression into a more complex function:

macro make_complex_function(ex)
  quote
    function (alg,f,t,u,k)
      # Add some stuff on top
      condition1 = false
      condition2 = false
      #...
      #Put in the user's code
      $(esc(ex))
      # Put a footer
      return some,stuff,here,long,annoying,list
    end
  end
end

so a user could easily insert small bits of logic (with a simplified API / documentation):

easy_func = @make_complex_function begin
   if u > 1
      print("oh no! It happened!")
    end
 end

while the full power is still available for more advanced users. However, if you run that code you'll get access to undefined reference errors. I think it's because I am not escaping the expression properly, and should somehow be escaping the entire function, but am not sure how.

Upvotes: 2

Views: 83

Answers (1)

张实唯
张实唯

Reputation: 2862

short answer: do NOT use esc or put it on the whole quote block.

julia> macro m(e)
         quote
           function f(x)
             return $(e)
           end
         end
       end
@m (macro with 1 method)

julia> f = @m x+1
#1#f (generic function with 1 method)

julia> f(2)
3

julia> macro m(e)
         esc(quote
           function f(x)
             return $(e)
           end
         end)
       end
@m (macro with 1 method)

julia> f = @m x+1
f (generic function with 1 method)

julia> f(2)
3

long answer:

  1. something about hygiene

julia will prefix any names defined inside a macro, making them unreachable anywhere else. esc will cancel these prefixes so that the names in the final expression are exact what you write. So remember only use esc if you need to access these variables outside the macro, which is not the case your example.

  1. use macroexpand to help building macroes

for example, if you put the esc at the wrong place like following:

macro m(e)
    quote
        function f(x)
            return $(esc(e))
        end
    end
end

you will got such a expression:

julia> macroexpand( :( @m x+1 ) )
quote  # REPL[9], line 3:
    function #1#f(#2#x) # REPL[9], line 4:
        return x + 1
    end
end

You can find that the f and it's argument are prefixed by a special thing(as they are not escaped), while the body is simply x, which is not defined anywhere.

  1. better use functions rather than macroes

Generally, it's better to use higher-order functions to implement "function templates", as they are easier to read and can take advantage of modern static analysis tools - though it seems Julia did't have one yet :). Write function signatures and pass them seems annoying, but they worth it.

Upvotes: 5

Related Questions