I am however quite unsure that I’m doing things as I should regarding the use of esc and hygiene. Could somebody have a look at the above to see if there is any glaring problem?
Yes, this looks like you’ve done things correctly as far as I can tell. In terms of Hygiene, it seems that you’ve escaped everything in the output, so you could have just not done all that intermediate escaping and just had the last line been return esc(newf).
Another general comment is that the package ExprTools.jl may be quite helpful for your purposes here.
Very good question! I want to be able to extract the kwargs from an anonymous function. I believe the only way is to use a macro at parse time. As soon as (x, y; a) -> x * y * a is compiled, there is no way to know that the list of kwargs is (:a,). If there were a way, I would be really happy to learn how!
EDIT: In other words, I want to use @par as an alternative syntax to write anonymous functions because of its side effect of providing their kwargs as the params... arguments
using ExprTools: splitdef
struct ParametricFunction{N,M,F}
f::F
params::NTuple{M,Symbol}
end
ParametricFunction{N}(f::F, p::NTuple{M,Symbol}) where {N,M,F} = ParametricFunction{N,M,F}(f, p)
(pf::ParametricFunction)(args...; kw...) = pf.f(args...; kw...)
parameters(p::ParametricFunction) = p.params
macro par(f::Expr)
(f.head == :->) || throw(ArgumentError("Only @par(args -> ..., parameters...) syntax supported"))
d = splitdef(f)
kwargs = get!(d, :kwargs, []) #use get! because the :kwargs might not exist
params = (x -> x isa Symbol ? x : x.args[1]).(kwargs) |> Tuple
N = length(get!(d, :args, []))
esc(:(ParametricFunction{$N}($f, $params)))
end
and then
julia> @macroexpand @par (x, y; a) -> x * y * a
:(ParametricFunction{2}(((x, y; a)->begin
#= In[90]:38 =#
x * y * a
end), (:a,)))
julia> pf = @par (x, y; a) -> x * y * a
ParametricFunction{2,1,var"#74#76"}(var"#74#76"(), (:a,))
julia> parameters(pf)
(a,)
julia> pf(1, 2, a=3)
6
To be clear, there’s nothing wrong with the syntax you’re using, I just personally find it easier to understand the kwarg syntax.