# Extracting kwargs from anonymous function

**URL:** <https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350>\
**Category:** General Usage\
**Tags:** macros, metaprogramming, kwargs\
**Created:** [April 10, 2020, 4:00pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350 "2020-04-10T16:00:02Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 10, 2020, 4:00pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/1 "2020-04-10T16:00:03Z")

</div>

EDIT: original title was “Confused about escaping in macros”

I’m trying metaprogramming in Julia for the first time, and I’m having some trouble grasping the escaping and hygiene discussion in the manual.

I’m trying to write a macro `@par(func, params...)` that inserts some `params...` as kwargs in an anonymous function `func`. So, I want to make

```julia
@par((x,y) - > x * y * a, a)

```

become

```julia
(x, y; a) -> x * y * a

```

After some fumbling I have what seems like a working macro:

```julia
macro par(f, params...)
    f.head == :-> || throw(ArgumentError("Only @par(args -> ..., parameters...) syntax supported"))
    farg = f.args[1]
    body = f.args[2]
    symbolarg = isa(farg, Symbol)
    symbolarg || isa(first(farg.args), Symbol) || throw(ArgumentError("Keyword arguments not supported"))
    N = symbolarg ? 1 : length(farg.args)
    newargs = symbolarg ? (esc(farg),) : esc.(Tuple(farg.args))
    newparams = esc.(params)
    newbody = esc(body)
    newf = :(($(newargs...), ; $(newparams...)) -> $(newbody))
    return newf
end

```

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?

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 10, 2020, 4:25pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/2 "2020-04-10T16:25:19Z")

</div>

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](https://github.com/invenia/ExprTools.jl) may be quite helpful for your purposes here.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 10, 2020, 4:26pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/3 "2020-04-10T16:26:31Z")

</div>

Thanks @Mason!

---

<div class="post-metadata">

**Author:** ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)\
**Post date:** [April 10, 2020, 4:35pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/4 "2020-04-10T16:35:55Z")

</div>

I am a bit perplexed: it seems to me that `(x, y; a) -> x * y * a` is quite readable, better than the application of the macro. What is the motivation?

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 10, 2020, 4:39pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/5 "2020-04-10T16:39:39Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 10, 2020, 5:18pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/6 "2020-04-10T17:18:48Z")

</div>

Maybe in that case, the macro should just take in `(x, y; a) -> x * y * a` and then return

```julia
(x, y, ; a) -> (params = (;a=a), x * y * a)

```

i.e. it would take in standard syntax.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 10, 2020, 5:33pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/7 "2020-04-10T17:33:52Z")

</div>

Mmm, but that would not give you access to `params` without evaluating the function, right?

My current full implementation is this

```julia
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, params...)
    (f isa Expr && f.head == :->) ||
        throw(ArgumentError("Only @par(args -> ..., parameters...) syntax supported"))
    farg = f.args[1]
    body = f.args[2]
    symbolarg = isa(farg, Symbol)
    symbolarg || isa(first(farg.args), Symbol) ||
        throw(ArgumentError("Keyword arguments not supported in @par"))
    N = symbolarg ? 1 : length(farg.args)
    newargs = symbolarg ? (esc(farg),) : esc.(Tuple(farg.args))
    newparams = esc.(params)
    newbody = esc(body)
    newf = :(($(newargs...), ; $(newparams...)) -> $(newbody))
    return :(ParametricFunction{$N}($newf, $params))
end

```

So now

```julia
julia> f = @par((x, y) -> x*y*a, a)
ParametricFunction{2,1,var"#4#6"}(var"#4#6"(), (:a,))

julia> parameters(f)
(:a,)

julia> f(2, 3; a = 3)
18

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 10, 2020, 5:48pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/8 "2020-04-10T17:48:55Z")

</div>

Here’s how I would do this:

```julia
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

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.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 10, 2020, 5:55pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/9 "2020-04-10T17:55:27Z")

</div>

I love that, much better!!

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 10, 2020, 6:01pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/10 "2020-04-10T18:01:59Z")

</div>

@Mason, do you mind if I copy your code into my package?  
(MIT license: [GitHub - pablosanjose/Quantica.jl: Simulation of quantum systems on a lattice](https://github.com/pablosanjose/Quantica.jl))

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [April 10, 2020, 6:02pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/11 "2020-04-10T18:02:23Z")

</div>

Not at all. 🙂

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [April 10, 2020, 9:35pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/12 "2020-04-10T21:35:19Z")

</div>

You don’t even need the `ParametricFunction` type if you don’t want — you could just have your macro effectively do:

```julia
f = (x, y; a) -> x * y * a
parameters(::typeof(f)) = (:a, )
return f

```

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [April 11, 2020, 4:08pm UTC](https://discourse.julialang.org/t/extracting-kwargs-from-anonymous-function/37350/13 "2020-04-11T16:08:44Z")

</div>

Clever!
