# Function that creates generated function using interpolation?

**URL:** <https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951>\
**Category:** General Usage\
**Tags:** metaprogramming\
**Created:** [March 24, 2018, 9:45pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951 "2018-03-24T21:45:32Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [March 24, 2018, 9:45pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/1 "2018-03-24T21:45:32Z")

</div>

Hi, so I’d like to to create a function that allows me to automate the creation of generated functions.

Here is an example of the type of generated function I’d like to create:

```nohighlight
using Reduce
expr = :(x^2)
@generated function fun(x,::Val{p}) where p
    :(@fastmath $(horner(df(expr,:x,p))))
end

```

However, I’d like to create a function that creates this function for me, so I want to be able to do

```nohighlight
genfun(:fun,expr)

```

where `:fun` symbol will be the name of the generated function and `expr` is substituted into it.

An approximation of what I want looks like this:

```nohighlight
function genfun(fun::Symbol,expr)
    @eval begin
        @generated function $fun(x,::Val{p}) where p
            :(@fastmath $(horner(df($$(QuoteNode(expr)),:x,p))))
        end
    end
end

```

However, I’m not exactly sure how to properly interpolate the expressions for the desired result.

Does anyone know how to do something like this?

**NOTE** this requires the master branch of `Reduce`, so `Pkg.checkout`

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [March 24, 2018, 10:12pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/2 "2018-03-24T22:12:35Z")

</div>

You should use a macro for that.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [March 24, 2018, 10:14pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/3 "2018-03-24T22:14:09Z")

</div>

As I understand it, if I use a macro then the generated function will not be in the desired scope.

I need the function to be defined in my module, if I use a macro, it will not be available to call, right?

Either way, the main hurdle is the interpolation, which I would need to do whether it’s a macro or not.

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [March 24, 2018, 11:12pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/4 "2018-03-24T23:12:55Z")

</div>

> [@chakravala](#):
>
> I need the function to be defined in my module, if I use a macro, it will not be available to call, right?

Assuming that you mean `Reduce` with “my module”:  
If you add a method to a function defined in your module then it will be available there too.

Then, it it not just:

```julia
function genfun(fun::Symbol,expr)
    @eval begin
        @generated function $fun(x,::Val{p}) where p
            :(@fastmath $(horner(df(expr),:x,p))))
        end
    end
end

```

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [March 24, 2018, 11:51pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/7 "2018-03-24T23:51:35Z")

</div>

Hm… it is closer to solution, but does not work completely yet. Since I had `expr = :(x^2)` defined in REPL, it seemed to work, but if I restart Julia, then `expr` is not found:

```nohighlight
function genfun(fun::Symbol,expr)
    @eval begin
        @generated function $fun(x,::Val{p}) where p
            :(@fastmath $(horner(df(expr,:x,p))))
        end
        $fun(x,p::Int) = $fun(x,Val(p))
    end
end

```

example

```nohighlight
julia> genfun(:f1,:((4x^4-44x^3+61x^2+270x-525)/100))
f1 (generic function with 3 methods)

julia> f1(2,0)
ERROR: UndefVarError: expr not defined
Stacktrace:
 [1] f1(...) at ./REPL[17]:4
 [2] f1(::Int64, ::Int64) at ./REPL[17]:6
 [3] macro expansion at ./REPL.jl:97 [inlined]
 [4] (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:73

```

So `expr` should be inserted into the generated function when `genfun` is called, but it is not inserted.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [March 25, 2018, 4:09pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/8 "2018-03-25T16:09:02Z")

</div>

Does nobody know how to get `expr` to insert into the generated function?

There must be a way to quote it…

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [March 25, 2018, 5:19pm UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/9 "2018-03-25T17:19:20Z")

</div>

Not exactly what you asked for, but this is what it looks like now that I finally got it to work:

```julia
function genfun(fun::Symbol,expr)
    quote_expr = Expr(:quote, expr)
    fun_expr_val = quote
        @generated function $fun(x,::Val{p}) where p
            expr = $quote_expr
            dfexpr = df(expr,:x,p)
            hdfe = horner(dfexpr)
            :(@fastmath $hdfe)
        end
    end
    fun_expr = quote
        function $fun(x, p)
            Base.Cartesian.@nif 10 i -> (i-1 == p) i -> ($fun(x, Val{i-1}())) i -> throw("Unsupported!")
        end
    end
    @eval begin
        $fun_expr_val
        $fun_expr
    end
end
genfun(:f8,:((4x^4-44x^3+61x^2+270x-525)/100))
f8(2,0)#-0.29
f8(2,Val(0))#-0.29
f8(2,1)#1.14

```

To more directly answer your question, quote the expr: `quote_expr = Expr(:quote, expr)`, so now you have two layers of quotes. Inserting deletes one layer, so you still have another left.

What I have above can probably be condensed. I just broke things into pieces while playing with it, to try and figure out what was going on.

EDIT:  
A cool upgrade on the Base.Cartesian.@nif version would be to run inference on the return type of `$fun` for some `Val(i)`, and then instead of throwing an error if `p>10-1` (or whatever the cutoff for number of if statements), have it do a dynamic dispatch with a type assertion thrown on so the whole thing remains type stable.  
You can’t do the dynamic dispatch without an assertion and have it still be type stable.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [March 26, 2018, 11:34am UTC](https://discourse.julialang.org/t/function-that-creates-generated-function-using-interpolation/9951/10 "2018-03-26T11:34:52Z")

</div>

Here is another challenge:

Thanks for your help. You actually somewhat predicted my next goal, but I wanted something different:

```nohighlight
hdf(expr,p::Int) = horner(df(expr,:x,p))

```

```nohighlight
function genfun(fun::Symbol,expr;depth=-1)
    if depth < 0
        @eval begin 
            @generated function $fun(x,::Val{p}) where p
                expr = $(Expr(:quote,expr))
                :(@fastmath $(horner(df(expr,:x,p))))
            end
            $fun(x,p::Int) = $fun(x,Val(p))
        end
    else
        lexpr = [Expr(:quote,hdf(expr,i)) for i ∈ 0:depth-1]
        @eval begin
            N = 1+$depth
            expr = $lexpr
            function $fun(x,p)
                macroexpand(quote
                    Base.Cartesian.@nif($N,
                        i->(i-1 == p),
                        i->($(:(expr[i]))),
                        i->throw(error("$(p)-th derivative not supported")))
                end)
            end
        end
    end
end

```

So the purpose of this function is that you can either make a generated function that evaluates derivatives up to arbitrary depth by default (with slower performance), but one should have the option of specifying a depth limit, which should then produce a plain function that uses the if-then-else structure.

```nohighlight
julia> genfun(:fa,:(x^2))
fa (generic function with 2 methods)

julia> fa(2,0)
4

```

So the generated function now works properly. However, producing the right kind of if-then-else plain function for faster performance requires the `expr[i]` derivatives to be inserted in each conditional.

In order to help debug this, the produced function outputs the if-then-else code instead of evaluating:

```nohighlight
julia> genfun(:fb,:(x^2);depth=2)
fb (generic function with 1 method)

julia> fb(2,0)
quote # REPL[211], line 17:
    if 0 == p
        expr[1]
    else 
        if 1 == p
            expr[2]
        else 
            throw(error("$(p)-th derivative not supported"))
        end
    end
end

```

If someone could figure out how to insert / interpolate `expr[i]` into that, I would be very thankful.

Otherwise, instead of using `Base.Cartesian.@nif`, a new function needs to take care of it.
