函数模板的宏:转义问题

Macro for a function template: escaping issues

我正在尝试为函数模板定义一个宏,即您可以轻松放入相关代码但不想向用户公开所有细节实现细节的模板。例如,我想要这样的东西,将表达式放入更复杂的函数中:

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

因此用户可以轻松插入少量逻辑(使用简化的 API / 文档):

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

而更高级的用户仍然可以使用全部功能。但是,如果您 运行 该代码,您将可以访问未定义的引用错误。我认为这是因为我没有正确转义表达式,应该以某种方式转义整个函数,但我不确定如何转义。

简答:不要使用esc或将其放在整个quote块上。

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

长答案:

  1. 一些关于卫生的事情

julia 将在宏中定义的任何名称添加前缀,使它们在其他任何地方都无法访问。 esc 将取消这些前缀,以便最终表达式中的名称与您所写的完全一致。所以请记住,如果您需要在宏外部访问这些变量,请记住仅使用esc,您的示例不是这种情况。

  1. 使用 macroexpand 帮助构建宏

例如,如果您将 esc 放在错误的位置,如下所示:

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

你会得到这样的表情:

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

你会发现 f 和它的参数有一个特殊的前缀(因为它们没有被转义),而正文只是 x,它没有在任何地方定义。

  1. 最好使用函数而不是宏

一般来说,最好使用高阶函数来实现 "function templates",因为它们更易于阅读并且可以利用现代静态分析工具——尽管 Julia 似乎还没有这样的工具: ).编写函数签名并传递它们似乎很烦人,但它们值得。