# Dynamically Create a Function - Initial Idea with eval failed due to World Age Issue

**URL:** <https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139>\
**Category:** General Usage\
**Tags:** metaprogramming, eval, runtimegeneratedfunc, world\_age\
**Created:** [October 27, 2020, 6:26pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139 "2020-10-27T18:26:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 27, 2020, 6:26pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/1 "2020-10-27T18:26:32Z")

</div>

Hey all,

I’m struggling to implement a certain problem.  
My current situation is the following: Some parts of my project consist of using [Tullio](https://github.com/mcabbott/Tullio.jl) functions. Since my arrays can be of arbitrary dimensions and I want to apply some user specific operations on them, I was creating functions with meta programming to compose the different Tullio expressions.

This minimal working example somehow shows my use case:

```julia
# function to create a complex expression
function N_dim(dim)
    if dim == 1
        return Meta.parse("arr[1, 1] .+ arr[1, 2]")
    else
        return Meta.parse("arr[1] .+ arr[2]")
    end 
end

# convenient wrapper to create a function from that expression 
function make_f(dim)
    f = @eval arr -> $(N_dim(dim))
    return f
end

# a function from which our f should be called
function foo()
    f = make_f(1)
    @show f([1 2; 3 4]) # fails here
    return 0
end

foo()

```

As known, this case runs into the world age issue:

```julia
ERROR: MethodError: no method matching (::var"#21#22")(::Int64)
The applicable method may be too new: running in world age 27854, while current world is 27855.

```

I somehow grasped the issue of that and already read a lot of regarded posts. However, in my case I can’t really use `Base.invokelatest` because I want to pass that function into Zygote.jl. Furthermore this function is performance critical and should be as fast as possible (hence the reason for using Tullio).

I would be really happy if someone could help me in solving that issue. Generating functions for different inputs at compile time is not really feasible since my real `make_f` has several input parameters.

Thanks,

Felix

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [October 27, 2020, 6:34pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/2 "2020-10-27T18:34:34Z")

</div>

Have you seen this thread:

> [@Best performance for eval of expressions with parameters](https://discourse.julialang.org/t/best-performance-for-eval-of-expressions-with-parameters/43443):
>
> I try to achieve better/best performance for eval(expression). Up to now I found 5 more or less different possibilities (in respect to my code logic) to eval an expression. It’s best, to see it in code. The following MWE can be directly copy&pasted into a REPL. It reflects my real projects structure in a minimal way. Some notes about it: the try...catch...end is needed, because the real expressions e can result in errors, e.g. (-0.5) ^ 0.9 f3::Function is not a real option, its just for co…

I am happy with

```julia-auto
GeneralizedGenerated

```

and all my tests I have shown that it results in best performance for generated functions.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 27, 2020, 6:37pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/3 "2020-10-27T18:37:59Z")

</div>

Thanks for the link, looks promising.  
I must have somehow missed it 🙄

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 27, 2020, 6:53pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/4 "2020-10-27T18:53:00Z")

</div>

I guess it works:

```julia
using GeneralizedGenerated

function N_dim(dim)
    if dim == 1
        return Meta.parse("arr -> arr[1, 1] .+ arr[1, 2]")
    else
        return Meta.parse("arr -> arr[1] .+ arr[2]")
    end 
end

function make_f(dim)
    f = mk_function(N_dim(dim))
    return f
end

function foo()
    @time f = make_f(1)
    x = randn((1000, 1000))
    @time f(x)
    @time f(x)
    @time f(x)

    @time f = make_f(2)
    @time f(x)
    @time f(x)
    return f

end

g = foo()

```

Output:

```julia
 2.923102 seconds (4.99 M allocations: 278.121 MiB, 4.95% gc time)
  0.213862 seconds (237.62 k allocations: 13.494 MiB)
  0.000002 seconds (1 allocation: 16 bytes)
  0.017559 seconds (14.34 k allocations: 779.257 KiB)
  0.080903 seconds (62.61 k allocations: 3.381 MiB)
  0.000002 seconds (1 allocation: 16 bytes)

```

So the first creation takes a while and I’m also surprised how much it allocates.

My final question would be: is that really _the_ solution to that?

Thanks,

Felix

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [October 27, 2020, 7:03pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/5 "2020-10-27T19:03:30Z")

</div>

> [@roflmaostc](#):
>
> My final question would be: is that really _the_ solution to that?

Don’t know, got no different solution in my old thread.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 27, 2020, 9:52pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/6 "2020-10-27T21:52:17Z")

</div>

Turns out that the naive approach doesn’t work with GeneralizedGenerated.

See on [GitHub](https://github.com/JuliaStaging/GeneralizedGenerated.jl/issues/56).

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [October 27, 2020, 11:10pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/7 "2020-10-27T23:10:32Z")

</div>

You need `Base.invokelatest`.

```julia
julia> function N_dim(dim)
           if dim == 1
               return Meta.parse("arr[1, 1] .+ arr[1, 2]")
           else
               return Meta.parse("arr[1] .+ arr[2]")
           end
       end
N_dim (generic function with 1 method)

julia> function make_f(dim)
           f = @eval arr -> $(N_dim(dim))
           return x->Base.invokelatest(f, [1 2; 3 4]) # CHANGED HERE
       end
make_f (generic function with 1 method)

julia> function foo()
           f = make_f(1)
           @show f([1 2; 3 4]) # no longer fails here
           return 0
       end
foo (generic function with 1 method)
https://arxiv.org/abs/2010.07516
julia> foo()
f([1 2; 3 4]) = 3
0

```

If you want an explanation for why this is necessary, see this preprint on arXiv:

> **[World Age in Julia: Optimizing Method Dispatch in the Presence of Eval...](https://arxiv.org/abs/2010.07516)**
>
> Dynamic programming languages face semantic and performance challenges in the presence of features, such as eval, that can inject new code into a running program. The Julia programming language introduces the novel concept of world age to insulate...

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [October 28, 2020, 4:50am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/8 "2020-10-28T04:50:11Z")

</div>

Did you try [GitHub - SciML/RuntimeGeneratedFunctions.jl: Functions generated at runtime without world-age issues or overhead](https://github.com/SciML/RuntimeGeneratedFunctions.jl) ? GeneralizedGenerated.jl builds massive types. This is usually a bit leaner and causes less related segfaults.

---

<div class="post-metadata">

**Author:** ![thautwarm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thautwarm/32/37760_2.png) [@thautwarm](https://discourse.julialang.org/u/thautwarm)\
**Post date:** [October 28, 2020, 5:46am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/9 "2020-10-28T05:46:51Z")

</div>

@ChrisRackauckas Tullio generates closures…

However there are still [some obstacles](https://github.com/JuliaStaging/GeneralizedGenerated.jl/issues/56#issuecomment-717708185) to using GG for Tullio.

Hence, unfortunately, I don’t think there is an existing approach for Tullio to avoid world age problem by now…

Advice:

1. Slightly modifying the code generator to avoid explicit annotations and use GG.
2. Try eliminating closures to use RuntimeGeneratedFunctions.  
This is possible, because after a short investigation on Tullio generated code, I find that nested functions never escape, and all closures are used locally.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [October 28, 2020, 5:50am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/10 "2020-10-28T05:50:58Z")

</div>

> [@thautwarm](#):
>
> Tullio generates closures…

Oh… yeah that’s going to give RuntimeGeneratedFunctions.jl issues. Why are the closures needed though? What’s it enclosing? If it’s enclosing something, it could runtime eval the inner function and enclose data on the runtime eval’d version.

---

<div class="post-metadata">

**Author:** ![lungben](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lungben/32/12314_2.png) [@lungben](https://discourse.julialang.org/u/lungben)\
**Post date:** [October 28, 2020, 6:42am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/11 "2020-10-28T06:42:51Z")

</div>

You can use multiple dispatch for this example, no need for metaprogramming:

```julia
f(::Val{1}) = arr -> arr[1, 1] .+ arr[1, 2]
f(::Any) = arr -> arr[1] .+ arr[2]

f(Val(dim)) # returns the 1st definition if dim=1, otherwise the 2nd one

```

Alternatively, just a regular higher-order function which returns the functions above, without needing `@eval`.

Or do I miss something?

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 28, 2020, 8:29am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/12 "2020-10-28T08:29:19Z")

</div>

My functions are unfortunately much more general than my simple example.

```julia
function N_dim(dim, offset)
    if dim == 1
        return Meta.parse("arr -> @tullio res = arr[i] + arr[i+offset]")
    else
        return Meta.parse("arr -> @tullio res = arr[i, j] + arr[i+offset,j] + arr[i, j] + arr[i,j + offset]")
    end 
end

```

More information for the generated code is also in this [issue](https://github.com/mcabbott/Tullio.jl/issues/11).

---

<div class="post-metadata">

**Author:** ![oheil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oheil/32/220745_2.png) [@oheil](https://discourse.julialang.org/u/oheil)\
**Post date:** [October 28, 2020, 8:29am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/13 "2020-10-28T08:29:30Z")

</div>

> [@ChrisRackauckas](#):
>
> Did you try [https://github.com/SciML/RuntimeGeneratedFunctions.jl](https://github.com/SciML/RuntimeGeneratedFunctions.jl) ? GeneralizedGenerated.jl builds massive types. This is usually a bit leaner and causes less related segfaults.

I got a case with a crash of Julia with GeneralizedGenerated.jl, so I’m going to try RuntimeGeneratedFunctions.jl in my little project to see, how this is working. Thanks for the tip.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 28, 2020, 8:31am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/14 "2020-10-28T08:31:21Z")

</div>

Zygote doesn’t work with `Base.invokelatest`. I maybe ask that in a Zygote issue (or someone here know the answer)?

```julia
julia> using Zygote

julia> function f()
           g = @eval x -> $(:(x+1))
           Zygote.gradient(Base.invokelatest(g, 10))
       end
f (generic function with 1 method)

julia> f()
ERROR: MethodError: objects of type Int64 are not callable
Stacktrace:
 [1] macro expansion at /home/fxw/.julia/packages/Zygote/c0awc/src/compiler/interface2.jl:0 [inlined]
 [2] _pullback(::Zygote.Context, ::Int64) at /home/fxw/.julia/packages/Zygote/c0awc/src/compiler/interface2.jl:12
 [3] _pullback(::Int64) at /home/fxw/.julia/packages/Zygote/c0awc/src/compiler/interface.jl:38
 [4] pullback(::Int64) at /home/fxw/.julia/packages/Zygote/c0awc/src/compiler/interface.jl:44
 [5] gradient(::Int64) at /home/fxw/.julia/packages/Zygote/c0awc/src/compiler/interface.jl:53
 [6] f() at ./REPL[6]:3
 [7] top-level scope at REPL[7]:1
 [8] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.2/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

```

Furthermore I’m afraid of the performance drop due to it.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 28, 2020, 8:37am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/15 "2020-10-28T08:37:17Z")

</div>

Thanks for your support (here and Github)!

> 1. Slightly modifying the code generator to avoid explicit annotations and use GG.  
> You mean the Tullio code generator?

> 1. Try eliminating closures to use RuntimeGeneratedFunctions.  
> This is possible, because after a short investigation on Tullio generated code, I find that nested functions never escape, and all closures are used locally.

That’s also regarding Tullio, isn’t it?

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [October 28, 2020, 8:39am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/16 "2020-10-28T08:39:14Z")

</div>

Try `Base.invokelatest(Zygote.gradient, g, 10)` instead. `Zygote.gradient` expects a function, you are passing it just the result of `Base.invokelatest(g, 10)` though.

---

<div class="post-metadata">

**Author:** ![roflmaostc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roflmaostc/32/30123_2.png) [@roflmaostc](https://discourse.julialang.org/u/roflmaostc)\
**Post date:** [October 28, 2020, 8:58am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/17 "2020-10-28T08:58:30Z")

</div>

Thanks, that’s obviously true 😅

Here MWE:

```julia
using Tullio
using Zygote
using BenchmarkTools

function f() 
    g = @eval arr -> $(:(@tullio r = (arr[i,j] + arr[i+1, j+1] + arr[i-1, j] + arr[i, j-1])^2))
    m = arr -> @tullio r = (arr[i,j] + arr[i+1, j+1] + arr[i-1, j] + arr[i, j-1])^2
    x = randn((1000, 1000))
    @btime Base.invokelatest(Zygote.gradient, $g, $x)

    @btime Zygote.gradient($m, $x)
end

f()

```

Output:

```julia
  2.701 ms (164 allocations: 7.64 MiB)
  2.648 ms (149 allocations: 7.64 MiB)

```

So it seems to work.

I’ll try that in my project and report back whether performance issues or errors come up

**EDIT:**  
It works in my larger project. But you need to put the `Base.invokelatest` at the very outer call.  
For example this doesn’t work:

```julia
using Tullio
using Zygote
using BenchmarkTools

function f() 
    g = @eval arr -> $(:(@tullio r = (arr[i,j] + arr[i+1, j+1] + arr[i-1, j] + arr[i, j-1])^2))
    m = arr -> @tullio r = (arr[i,j] + arr[i+1, j+1] + arr[i-1, j] + arr[i, j-1])^2
    x = randn((512, 512))
    @btime Zygote.gradient(x -> Base.invokelatest($g, x), $x)

    @btime Zygote.gradient($m, $x)
end

f()

```

Because then Zygote wants to do the derivative of `Base.invokelatest`

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [October 28, 2020, 10:04am UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/18 "2020-10-28T10:04:33Z")

</div>

> [@roflmaostc](#):
>
> Because then Zygote wants to do the derivative of `Base.invokelatest`

I think it should be possible to add a rule for `Base.invokelatest`, but not 100% sure how this plays out with Zygote’s internals.

---

<div class="post-metadata">

**Author:** ![thautwarm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thautwarm/32/37760_2.png) [@thautwarm](https://discourse.julialang.org/u/thautwarm)\
**Post date:** [October 28, 2020, 2:44pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/19 "2020-10-28T14:44:20Z")

</div>

> [@ChrisRackauckas](#):
>
> Why are the closures needed though?

I have the same point here. You might check [How to implement n-dimensional functions? · Issue #11 · mcabbott/Tullio.jl · GitHub](https://github.com/mcabbott/Tullio.jl/issues/11)

> **some code generated by Tullio**
>
> ```julia
> begin
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:962 =#
> let 𝒜𝒸𝓉! = 𝒜𝒸𝓉!
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:963 =#
> begin
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:956 =#
> local function ℳ𝒶𝓀ℯ(arr)
> $(Expr(:meta, :inline))
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:956 =#
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:957 =#
> local 𝒶𝓍i = intersect(eachindex(arr), eachindex(arr) .- 1)
> local 𝓇𝒽𝓈(arr, i) = begin
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:780 =#
> identity((arr[i] - arr[i + 1]) ^ 2)
> end
> begin
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:789 =#
> local 𝒯1 = Core.Compiler.return_type(𝓇𝒽𝓈, typeof((arr, first(𝒶𝓍i))))
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:790 =#
> local 𝒯2 = if Base.isconcretetype(𝒯1)
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:791 =#
> 𝒯1
> else
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:793 =#
> nothing
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:794 =#
> typeof(𝓇𝒽𝓈(arr, first(𝒶𝓍i)))
> end
> end
> local 𝒯3 = 𝒯2
> local 𝒯 = 𝒯3
> local ℛ = convert(𝒯, zero(𝒯))
> (Tullio.thread_scalar)(𝒜𝒸𝓉!, (Tullio.storage_type)(arr), ℛ, tuple(arr), tuple(𝒶𝓍i), +, 87382, nothing)
> end
> end
> #= C:\Users\twshe\.julia\packages\Tullio\Q2RDS\src\macro.jl:964 =#
> ((Tullio.Eval)(ℳ𝒶𝓀ℯ, ∇ℳ𝒶𝓀ℯ))(arr)
> end
> end
> 
> ```

You can see `Tullio.thread_scalar` accepts the generated closure…  
I guess everything is possible to get “statically” inlined before execution, but it might be difficult for people to implement.

Suddenly I feel that we might need something like _high order macros_ and _local macros_.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [October 28, 2020, 2:51pm UTC](https://discourse.julialang.org/t/dynamically-create-a-function-initial-idea-with-eval-failed-due-to-world-age-issue/49139/20 "2020-10-28T14:51:42Z")

</div>

No time today but perhaps this page of docs is useful, explaining what functions it creates: [https://gist.github.com/mcabbott/7494c8cc46a906fe88172dbfc8b92ad2](https://gist.github.com/mcabbott/7494c8cc46a906fe88172dbfc8b92ad2)

I’m never too sure of the terminology, they don’t close over values (which they explicitly receive as arguments) but I guess the outer functions “make” close over the kernel function “act!”.
