Say I have a package MyPackage
that contains a macro @newfunction
that returns a function. The macro works perfectly fine, but the functions it returns are defined in the namespace of MyPackage
. Is there a way to export the function to the namespace of the caller? The caller might be code running in the REPL or a different package that uses MyPackage
.
Of course one could do myfunction = @newfunction args
. What I'm wondering is if there is a way to do @newfunction myfunction args
, AND have myfunction
exported to the namespace of the caller.
You just have to esc
the name of the function in the macro body
e.g.
macro foo(name)
escname = esc(name)
:($escname(x) = x + 1)
end
Yeah, this works! Thank you.
I think I understand hygiene a little bit better now.
Yeah it’s a kinda tricky concept at first but it’s not so complicated. Julia handles it a little different from most lisps in that it’s hygienic by fault instead of by opting in.
Though I often end up just esc
ing the entire returned expression and then manually opting into the hygiene where I choose because I find that easier to think about
This is the first non-trivial macro I write (in Julia or any language). At least for me, this is one instance where the documentation only starts to make sense after I've tried writing something and failing.
In this case, I didn't associate my problem with hygiene. After seeing your solution, I re-read that section in the manual and sure enough, it says that escaped expressions are evaluated in the context of the caller. That sentence didn't make sense to me before, it certainly does now :grinning:
so like to adapt the above example, I would often write something more like
macro foo(name)
@gensym x
:($name($x) = $one($x) + $x) |> esc
end
here, everything is escaped so all symbols are expanded in the callers namespace, but I manually interpolated my one
function into the expression to make sure it gets used rather than the caller’s one
(in case they made that symbol mean something different) and I manually mangled the symbol x
Either style has pitfalls, but I don’t like the look of $(esc(x))
all over my code, so I often gravitate towards the second style.
I think it looks very clean, and certainly more readable than having esc
all over the place.
I would also consider using the @generated
macro here.
Why?
I may be misunderstanding the question, but isn't @generated
essentially a macro that returns a function?
Sorta. It’s a way to create a custom function where the function body is programmatically generated based on the input types.
It’s a pretty heavy piece of machinery though and not always necessary
I don't think it's necessary in my case, since the input types are not really important.
Last updated: Dec 28 2024 at 04:38 UTC