# Can someone explain closures to me

**URL:** <https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605>\
**Category:** Internals & Design\
**Tags:** performance, type-stability, closure\
**Created:** [October 31, 2023, 3:56am UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605 "2023-10-31T03:56:51Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![HashBrown](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hashbrown/32/43601_2.png) [@HashBrown](https://discourse.julialang.org/u/HashBrown)\
**Post date:** [October 31, 2023, 3:56am UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/1 "2023-10-31T03:56:51Z")

</div>

Hi, I am familiar with what a closure is (a function that captures state outside its scope) but I am interested in how they work in Julia so that I can understand performance.

In [Performance Tips](https://docs.julialang.org/en/v1/manual/performance-tips/) there is a section dedicated to closures (Performance of captured variable).

Namely it explains that the following is poor performance:

```julia
function abmult(r::Int)
    if r < 0
        r = -r
    end
    f = x -> x * r
    return f
end

```

And the next is better:

```julia
function abmult2(r0::Int)
    r::Int = r0
    if r < 0
        r = -r
    end
    f = x -> x * r
    return f
end

```

And lastly the below is best:

```julia
function abmult3(r::Int)
    if r < 0
        r = -r
    end
    f = let r = r
            x -> x * r
    end
    return f
end

```

I do not see how any more information is added in any of the examples (i.e if I were a compiler I would compile them all the same). It is clear to me that r is an Int when it is captured. It is also clear to me that no other piece of code can change r. So why is it not clear to the compiler?

Basically I think without a technical explanation of some internals I can’t understand what is going on in this example or any use of functions as values in general (not just functions that capture state outside their scope, a.k.a closures). Another thing I don’t understand is how a function that looks like what is below is compiled:

```julia
call_function(f, x) = begin
    f(x)
end

```

Is a new specialization for `call_function` created on every invocation with a different `f`?

For example:

```julia
call_alot_of_functions(xs) = begin
    squares = similar(xs)
    for (i, x) in enumerate(xs)
        f = y -> y * x
        squares[i] = call_function(f, x)
    end
    squares
end

```

Is this going to be horribly slow? Is f going to be of a different type on each iteration of the loop? Leading to call\_function having to be specialized on each iteration? And is f itself going to be recompiled each time?

Surely using closures and anonymous functions isn’t super unperformant given their prevalent use, but I just can’t wrap my mind about how they work internally and feel like it is a hole in my understanding of how the compiler will treat the code that I write

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 31, 2023, 10:22am UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/2 "2023-10-31T10:22:24Z")

</div>

This [longstanding issue](https://github.com/JuliaLang/julia/issues/15276) has moved a bit since the beginning but is far from over, hence the tip. I’m not certain on the exact boundaries of captured variable inferrability, but here’s what I know so far, starting from some basics.

A closure is a function that uses an outer local scope’s variables; this is _not_ different from how other local scopes reuse outer local variables. The distinction of “capturing” is that a nested function is also an instance that can outlive the outer scope, so captured variables can also outlive the scope. It’s important to note that as a consequence, if the captured variable is reassigned anywhere, that change is also shared by all closures.

> **Example of reassignment of captured variables.**
>
> ```julia
> julia> let
> v1 = 0
> f() = v1
> println(f())
> v1 += 1
> g() = v1
> println((f(), g()))
> v1 += 1
> println((f(), g()))
> end
> 0
> (1, 1)
> (2, 2)
> 
> ```

A method is compiled separately for each unique call signature, which is the types of the function and the ordered runtime arguments. The compiler uses the argument types to infer the types of values and local variables, so inferred types vary among call signatures. Conversely, a variable captured from an outer scope must have the same inferred type in that scope and in the infinite possible call signatures. A captured variable cannot be inferred if it is reassigned in a closure because the compiler cannot infer the value across the infinite call signatures it has not encountered yet.

> **A reassigned captured variable has an unfixed type.**
>
> ```julia
> let
> x = 0 # x::Int
> function setx(y)
> x = y # setx captures x, shares x with let block
> end
> setx(1.0) # x::Float64
> setx(true) # x::Bool
> setx(1im) # x::Complex{Int}
> # ... so the compiler can only say x::Any
> end
> 
> ```

Since a call-wise compiler can’t be exhaustive, the ~~parser~~ lowerer (or whatever the noun is) checks the expression, and if a variable is never reassigned or has a type declaration, the type is known to be unchanged, despite only being a symbol at that point.

Closures are implemented as functors; each captured variable resides in a field of a hidden `struct`’s callable instance. Uninferrable or reassigned variables are stored in `::Core.Box` fields, which is like an internal version of `Ref{Any}`; this includes type-declared variables like `r::Int` in `abmult2`, the type being restored by `convert` and `assert` steps. Inferred and non-reassigned variables are stored in parametric fields `::T`; you can see this parameter show up in closure’s types. Given the inherent difficulty of inferring variables shared across call signatures, I think the biggest possible improvements would be 1) doing `::T` when all reassignments occur prior to all closures, like in `abmult`, 2) doing `::Ref{T}` when the shared variable has 1 type declaration (\>1 causes a syntax error) and is reassigned within or after any closure, and 3) inferring closures better when a captured variable is inferred with a small `Union` type; someone correct me if there are any misconceptions there.

> **Inferred and non-reassigned variables contribute type parameters to closures.**
>
> ```julia
> julia> let
> x = 1
> f() = x
> end
> (::var"#f#1"{Int64}) (generic function with 1 method)
> 
> julia> let
> x = 1
> x = 2 # x is no longer inferrable
> f() = x
> end
> (::var"#f#2") (generic function with 1 method)
> 
> ```

Inferring a variable doesn’t always make things type-stable though. Just today, there was [a thread](https://discourse.julialang.org/t/using-an-assignment-outside-of-threaded-for-loop-degraded-performance-by-a-huge-amount/105552) demonstrating that inferring a variable as `T::DataType` doesn’t make for inferrable calls e.g. `one(T)`. The most adaptable workaround was refactoring to capture an instance of `T`.

---

<div class="post-metadata">

**Author:** ![alfaromartino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alfaromartino/32/52986_2.png) [@alfaromartino](https://discourse.julialang.org/u/alfaromartino)\
**Post date:** [October 31, 2023, 10:47pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/3 "2023-10-31T22:47:34Z")

</div>

The problem with closures arises when you have a function defined inside a function AND you’re redefining variables/arguments. The compiler gets confused and you end up with type unstable code.

For example, note in this example everything is fine when you don’t redefine `a`.

```julia
function outer_foo()
    a = 1
    a = 1
    inner_foo() = a 
    return inner_foo()
end

@code_warntype outer_foo() # type unstable

function outer_foo()
    a = 1
    b = 1
    inner_foo() = a
    return inner_foo()
end

@code_warntype outer_foo() # type stable

```

I recommend not using closures if possible. Most of the time, you can redefine the inner function outside the outer function.

If you really need to use closures, you should be fine in most cases when you don’t redefine variables or function arguments. But always check with `@code_warntype`.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 1, 2023, 2:19am UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/4 "2023-11-01T02:19:14Z")

</div>

> [@alfaromartino](#):
>
> I recommend not using closures if possible.

I only halfway agree because closures are often a lot more convenient to write than functors and methods, especially with comprehensions and generators that are built on closures. If performance isn’t important, it’s fine; if performance is important, it’s fine if variables are not reassigned.

However, explicit functors are nice because it’s easier to reason about stored instances rather than captured variables. A shared variable can be type-stably mimicked by passing an inferrable instance as an argument to the functors’ type constructors. The compiler can infer this much better because no actual variables are shared by multiple methods. A reassignable variable can be mimicked by storing a `Ref{T}` instance.

---

<div class="post-metadata">

**Author:** ![HashBrown](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hashbrown/32/43601_2.png) [@HashBrown](https://discourse.julialang.org/u/HashBrown)\
**Post date:** [November 1, 2023, 3:33am UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/5 "2023-11-01T03:33:07Z")

</div>

```julia
begin
    a = 1
    f() = a
    g() = a
    f, g
end

```

So in this case can the parser tell that `a` is not reassigned and do proper type inference? Or does a variable being captured by multiple closures cause inference to result in `Ref{Any}` for the field of the function object? Also I guess is the “proper” thing to do here `Ref{Int}` or just `Int`

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 1, 2023, 6:36am UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/6 "2023-11-01T06:36:45Z")

</div>

That is different from what I described earlier because `f` and `g` are functions in the global scope accessing a global variable `a`; unlike local scopes, global scopes persist and cannot be outlived, so my understanding is that they are not considered [closures in Julia](https://docs.julialang.org/en/v1/devdocs/functions/#Closures) because they do not need to capture anything in fields. In any case, `a` is an global variable with no type or `const` declaration, and those can only be inferred as `::Any` because they can be reassigned arbitrarily.

If it were a local `let` block, you would be correct that `a` is inferrable.

> [@HashBrown](#):
>
> result in `Ref{Any}` for the field of the function object?

This does not happen, Julia uses `Core.Box`, which has more differences than just the type and name; the relevant similarity is that the type of the contained instance is unfixed.

---

<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:** [November 1, 2023, 12:59pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/7 "2023-11-01T12:59:34Z")

</div>

I love coding with closures because it makes it so easy to reuse memory buffers and temporary variables without explicitly storing them.

Such as:

```julia
function build_forward(A, B)
    f = let buffer = similar(A), A=A, B=B
        function f(x)
             buffer .= # do something with x, A, B
             buffer .= # do something with x and A
             return B
         end
   end
   return f
end

```

But yeah, with the `let ... end` it’s always quite some boilerplate.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [November 1, 2023, 1:03pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/8 "2023-11-01T13:03:49Z")

</div>

It may be helpful to note that closures of re-assigned variables are a really weird language construct.

Normally, variables in julia are bindings; mere shorthands for the value that was last assigned to them. They are not real objects that exist in any way:

```julia
function foo()
   r_contents = 1
...

```

If you want to reify the variable into a kind of “slot” that can be written and read, you can do that explicitly:

```julia
function foo()
   r_address = Ref(1)
...

```

This is similar to C:

```julia
void foo(){
   long r_contents = 1;
...
}

void foo(){
   long r_address[1];
   r_address[0] = 1;
...
}

```

Of course the compiler is permitted to look at your code and try to de-reify variables, i.e. try to replace the `r = Ref(1)` by `r_contents = 1`, and then replace all uses `r[]` by `r_contents` . In llvm, that’s the job of mem2reg / memssa passes.

Now C has a really weird language construct: The addressof operator. Instead of having to declare types, you can do

```plaintext
void foo(){
   long r_contents = 1;
   long* r_address = &r_contents;
...
}

```

This is pretty surprising: The C compiler is expected to do type inference in order to figure out whether a variable is a binding, or a reified slot. And this type inference doesn’t follow control-flow, it’s a non-local syntactic property of the code:

```plaintext
void foo(){
   long r_contents = 1; // looks like a binding
   if(false){
      // this code is dead. Doesn't matter, it's a syntactic nonlocal property of your code
      // let's promote r_contents from binding to reified slot, after the fact!
      long* r_address = &r_contents;
  }
...
}

```

Luckily julia doesn’t do such nonsense. You must decide from the beginning whether your variable is supposed to be a binding or a reified slot. And this is a local property, so it’s easy to understand code when reading it.

…just joking. Julia does not have an explicit addressof operator (imo for very good reasons!).

But closures work this way:

```julia
function foo()
    r_contents = 0; # looks like a binding
...
   return
   # this code is dead. Doesn't matter, it's a syntactic nonlocal property of your code
   # let's promote r_contents from binding to reified slot, after the fact!
    get_r() = r_contents
    set_r(v) = (r_contents = v;)
end

```

This sucks for the compiler and the reader of your code ☹

edit: So the punchline regarding your original question is:

Closures in julia do two things:

1. they capture state from outside their scope. This is not an issue and has no surprising perf impact. Like a functor.
2. They don’t just create a functor, they implicitly promote the captured state from outside their scope from binding to addressable object – and this is syntactically invisible “outside their scope”. This is where all the surprising perf impacts come from.

---

<div class="post-metadata">

**Author:** ![alfaromartino](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alfaromartino/32/52986_2.png) [@alfaromartino](https://discourse.julialang.org/u/alfaromartino)\
**Post date:** [November 1, 2023, 7:12pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/9 "2023-11-01T19:12:45Z")

</div>

My advice to not use closures was more along the lines of “unless you know what you’re doing, avoid it if you can”.

I agree that closures arise naturally and make the code easier to read. But they can easily make your code type unstable even when you’re writing code in a typical Julian way.

I was simplifying also when I said that the problem is when you reassign variables. Consider the following.

Example 1:

```julia
function foo()
    bar() = x
    x = 1       

    return bar()
end

@code_warntype foo() # type unstable

```

In fact, the following doesn’t solve the problem:

```julia
function foo()
    bar()::Int64 = x::Int64
    x::Int64 = 1       

    return bar()
end

@code_warntype foo() # type unstable

```

but this does

```julia
function foo()
    x = 1
    bar() = x
    
    return bar()
end

@code_warntype foo() # type stable

```

so that the order in which you include the functions would matter.

Example 2:

```julia
function foo(x)
    closure1(x) = x
    closure2(x) = closure1(x)
    
    return closure2(x)
end

@code_warntype foo(1) # type stable

function foo(x)
    closure2(x) = closure1(x)
    closure1(x) = x
    
    return closure2(x)
end

@code_warntype foo(1) # type unstable

```

In both cases, you can fix it by making the inner function have everything that it’s used as an argument. But this would include functions. So, if you don’t want to care about the order of functions, you’d have to do the following:

```julia
function foo(x)
    closure2(x, closure1) = closure1(x)
    closure1(x) = x
    
    return closure2(x, closure1)
end

@code_warntype foo(1) # type stable

```

Overall, every time I use closures I double check that I didn’t add some type instability without noticing it.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 1, 2023, 11:55pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/10 "2023-11-01T23:55:55Z")

</div>

> [@roflmaostc](#):
>
> But yeah, with the `let ... end` it’s always quite some boilerplate.

It doesn’t appear that you need the `let` block there, `function build_forward` already makes a local scope that holds a local `A` and `B`, and it could hold a local `buffer` too. You only need that pattern when you want to reuse variable names without the closures actually sharing variables.

> [@alfaromartino](#):
>
> Example 1:

The functor seems to store `x` in a `Core.Box` because `x` is unassigned at that point. Unlike a global function that can wait until call-compilation to figure out global variables, a nested function definition immediately instantiates a functor whose fields are set in stone to contain the captured variables. Maybe the parser could also improve to recognize that no closures are called before the captured variable is assigned once and defer the functor instantiation? Not sure, could probably come up with pathological situations.

> [@alfaromartino](#):
>
> In fact, the following doesn’t solve the problem:

I don’t see that, I see the typical effect of annotating `x::Int = 1` when it is assigned after a closure. Annotating `bar()` was unnecessary.

> **\`@code\_warntype\` report.**
>
> ```julia
> julia> @code_warntype foo()
> MethodInstance for foo()
> from foo() in Main at REPL[1]:1
> Arguments
> #self#::Core.Const(foo)
> Locals
> x::Core.Box
> bar::var"#bar#1"
> Body::Int64
> 1 ─ (x = Core.Box())
> │ (bar = %new(Main.:(var"#bar#1"), x))
> │ %3 = x::Core.Box
> │ %4 = Base.convert(Main.Int64, 1)::Core.Const(1)
> │ %5 = Core.typeassert(%4, Main.Int64)::Core.Const(1)
> │ Core.setfield!(%3, :contents, %5)
> │ %7 = (bar)()::Int64
> └── return %7
> 
> ```

> [@alfaromartino](#):
>
> Example 2:

Again, an unassigned `closure1` was stored in a `Core.Box`.

On a wider topic, it’s expected that nested functions, including closures, are more limited than global functions. In a local scope, you shouldn’t (and have no good reason to) [overwrite method signatures](https://github.com/JuliaLang/julia/issues/36037), nor can you feasibly [conditionally define methods](https://github.com/JuliaLang/julia/issues/15602); those linked issues are related, and the limitations are necessary for letting those underlying functors be performant. Hopefully one day those limitations can be laid out clearly in the docs and in errors, maybe loosened a little with an improved parser-lowerer. Having written this out, I’m agreeing with this a bit more:

> [@alfaromartino](#):
>
> My advice to not use closures was more along the lines of “unless you know what you’re doing, avoid it if you can”.

If there’s no time to understand local scoping rules and still-obscure nested function limitations, use [functors to store state](https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects) for calls, they’re a great and underappreciated core feature (functions are built on stateless functors). Fields holding mutable instances like `Ref{T}` allow functors to share state.

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [November 2, 2023, 2:36pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/11 "2023-11-02T14:36:01Z")

</div>

> [@Benny](#):
>
> it’s fine if variables are not reassigned.

Thanks for the pro tip. This erased most of my concerns about closures in Julia. Lack of ergonomic and performant closures would be very unsatisfying if you want to write programs in a functional style, but treating variables as bindings (that are never reassigned) is exactly what’s encouraged by a functional style, so this problem solves itself.

---

<div class="post-metadata">

**Author:** ![croberts](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/croberts/32/9465_2.png) [@croberts](https://discourse.julialang.org/u/croberts)\
**Post date:** [November 2, 2023, 4:01pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/12 "2023-11-02T16:01:07Z")

</div>

> [@Benny](#):
>
> If there’s no time to understand local scoping rules and still-obscure nested function limitations, use [functors to store state](https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects) for calls, they’re a great and underappreciated core feature (functions are built on stateless functors). `Ref` fields allow functors to share state.

I agree that functors are great. But they can be frustrating to use if they are intended for some one-off calculation. There are two problems:

(1) Do I really want to be creating a new data type in global scope and then a new method for this type when I am intending a one-off calculation? Do I want to add minimum 4 lines of code (3 for the struct, parameter, end) when I just want a one-off calculation?  
(2) Structs are a pain to use interactively: if I need to tweak my struct definition, I must either (A) restart my session (B) write a new struct with a new name (“my\_struct2”).

What would be nice is something like an anonymous functor. The closest you can get to this is defining methods for namedtuples:

```julia
(nt::NamedTuple{(:x, :y)})(z::Float64) = sum(z .* nt.y) + nt.x
my_funct = (x = 1, y = [1, 2])
my_funct(10.0) 
# evaluates to 31.0 

```

This works because {NamedTuple:Struct::anonymous function:function}. But it involves type piracy – do I really want to write a method in global scope for all NamedTuples with a particular type signature?

This problem is closely related to discussions of piping syntax. People have suggested creating syntax something like:

```julia
f(a, b) = a^b
3 |> f(2, _) 
# returns 8 

```

In the above example, the f(2, \_) expression can be thought of as an anonymous closure or an anonymous functor. It should be syntactically feasible to express the idea of a closure as succinctly as above.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [November 2, 2023, 7:32pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/13 "2023-11-02T19:32:44Z")

</div>

> [@croberts](#):
>
> This problem is closely related to discussions of piping syntax. People have suggested creating syntax something like:
> 
> ```julia
> f(a, b) = a^b
> 3 |> f(2, _) 
> # returns 8 
> 
> ```
> 
> In the above example, the f(2, \_) expression can be thought of as an anonymous closure or an anonymous functor. It should be syntactically feasible to express the idea of a closure as succinctly as above.

I’m not sure what the problem that you’re discussing is. You can already do things like `(f ∘ g ∘ h)(x)`.

Is it just that you want to reverse the “flow of information”, that is you want the input to “flow” from left to right, instead of from right to left? In any case, I’m not a fan of syntax sugar, I’d rather see Julia lose some of it than gain more.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 2, 2023, 8:21pm UTC](https://discourse.julialang.org/t/can-someone-explain-closures-to-me/105605/14 "2023-11-02T20:21:41Z")

</div>

> [@greatpet](#):
>
> treating variables as bindings (that are never reassigned) is exactly what’s encouraged by a functional style

It’s interesting to note that if bindings are not reassigned, it’s the same effect as and implemented as if the instance were captured. But you can’t always capture instances because closures like `do` blocks and comprehensions are expected to reassign local variables like any other local scopes; for example, a `foreach`-`do` block should do the same thing as a `for`-loop. Here’s a link to [the middle of a thread](https://discourse.julialang.org/t/why-not-an-even-harder-scope/50087/22) for a few comments addressing this. Note that as explained prior, the examples’ reassigned variables are `Core.Box`ed, and the lack of type declarations means retrieving the variable propagates type instability. Annotating the variable, even generically like `t::T = 0`, isolates the type instability to the `Core.Box`, though there’s nothing that can be done about the extra heap allocation because a variable that outlives its scope cannot be stored in a stack.

> [@croberts](#):
>
> Do I want to add minimum 4 lines of code (3 for the struct, parameter, end) when I just want a one-off calculation?  
> …  
> Structs are a pain to use interactively: if I need to tweak my struct definition, I must either (A) restart my session (B) write a new struct with a new name (“my\_struct2”).

Right, closures handle their underlying functors for you so you don’t have to edit multiple places in code, hence “often a lot more convenient to write”.

> [@croberts](#):
>
> What would be nice is something like an anonymous functor.

That wouldn’t work, an unnamed composite cannot distinguish 2 separate closures that capture the same variables. Closures do make names for the underlying structs, kind of `gensym`ed but not sure how.
