在一个模块里定义一个宏和在全局下定义一个宏时代码展开不同,求教


我将这个宏封装到module 中

module Chain
macro thread_as(nums_expr,as,exprs...)

    fn(from,as,to) = Expr(:call,replace(to.args,as=>from)...)

    iter = function iter(exprs,res)
        if length(exprs) == 1
            fn(res,as,first(exprs))
        else
            iter(Iterators.drop(exprs,1),fn(res,as,first(exprs)))
        end
    end

    iter(exprs,nums_expr)
end
end

并且导入我的fib模块

module Fib
mutable struct Fibs
    value::Int
    rest::Fibs
    Fibs(v) = new(v)
    Fibs(v, r) = new(v, r)
end

fib = Fibs(0, Fibs(1))

function Base.iterate(fibs::Fibs, (a,b)=(fibs, fibs.rest))
    value = a.value + b.value
    if !isdefined(b, :rest)
        b.rest = Fibs(value)
    end
    return value, (b, b.rest)
end

我需要调用
@thread_as Iterators.take(fib,40) _ collect(_) filter(x->x<500,_)

这是没有用模块包装@thread_as展开的代码

julia> @macroexpand1 @thread_as Iterators.take(fib,40) _ collect(_) filter(x->x<500,_)
:(Main.filter((var"#7#x"->begin
              #= REPL[6]:1 =#
              var"#7#x" < 500
          end), Main.collect((Main.Iterators).take(Main.fib, 40))))

这是使用模块包装@thread_as扔出的错误

julia> @thread_as Iterators.take(fib,40) _ collect(_) filter(x->x<500,_)
ERROR: UndefVarError: fib not defined
Stacktrace:
 [1] top-level scope at REPL[6]:1

这是展开的代码

julia> @macroexpand1 @thread_as Iterators.take(fib,40) _ collect(_) filter(x->x<500,_)
:(Main.Chain.filter((var"#8#x"->begin
              #= REPL[7]:1 =#
              var"#8#x" < 500
          end), Main.Chain.collect((Main.Chain.Iterators).take(Main.Chain.fib, 40))))

这里宏代码展开出现了错误,这应该怎么改

你在一个Module里面定义宏的时候,里面的符号默认都是在这个模块内的,所以你需要esc一下。
https://docs.julialang.org/en/v1/base/base/#Base.esc
具体的做法就是把你原来的宏改成函数,然后再写个同名宏转发一下。

macro thread_as(args...)
    thread_as(args...) |> esc
end

可以参考一下文档的卫生宏部分。
https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene-1