关于Julia宏的知识点讲解

原帖见: Apply `esc` to returned node of `@json` to allow referring local variables · Issue #1 · davidavdav/JSObjectLiteral.jl · GitHub

知识点:

  • 宏是从AST到AST的函数
  • 对插入AST的常规julia对象求值, 返回其自身
  • 每一个模块都有eval。如果你引用一个non-qualified的eval, 它将是当前模块的eval, 例如在REPL里,eval就是Main.eval
  • 当前模块可以用@__MODULE__ 访问。
  • __module____source__只能在宏定义的主体内被访问。(它们其实分别是宏的第2和第1个参数,用户书写的第n个参数其实最后会是第n+2个参数)。
  • 一个宏调用返回一个AST,它将会被插入到其(指这个宏)被调用的地方。 如果这个宏返回的AST没有被esc给包住,那么将会被处理为卫生宏,从而无法访问宏调用处的作用域。(当前模块的全局作用域可以被访问)
function() __module__
end # wrong
macro f()
   mod = __module__
   :(1 + $(mod.a))
end
a = 1
@f 
# => 2

macro g1()
   :(1 + x)
end

function f(x)
     @g1
end
f(2) # `x` is not defined!

macro g2()
   :(1 + x) |> esc
end

function f(x)
     @g2
end

f(2) # => 3
3 个赞

我有几个问题想问一下:
1.第二点中的常规对象具体指什么(是不是指整数浮点数这些不可再化简的值)
2.最后一点说当前模块的作用域可以被访问,其实是不是这样:一个表达式如果没有被esc围起来,那么它返回的表达式中变量来自于宏定义的父环境,如果被esc围起来了,那么变量就来自于调用的环境
例子:

julia> module X
       x = 'a'
       macro g()
       :(1+x)
       end
       end
Main.X
julia> module Y
       using Main.X
       x = 1
       function f()
       Main.X.@g
       end
       end
Main.Y
julia> Y.f()
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)

在这里@g的x是‘a’而不是1
3.我记得卫生宏和gensym有点关系,还是我记错了?

  1. 常规对象就是非Expr, Symbol等文档中说的AST类型的实例
  2. esc后来自调用环境没问题,但是不esc的话,等于是宏去生成代码时的环境,也就是宏调用模块的全局作用域。
  3. 卫生宏实现多的很,你生成用户写不出来的Symbol,把没esc的Symbol进行mangling(gensym)就行