Here’s how I would do this:
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.