Anonymous function's type and methods last forever?

My understanding is that an anonymous function is implemented as an instance of a type assigned to a generated name in the global scope, and the type and its methods should thus persist for the rest of the process. However I read in several places that anonymous functions were garbage collected and wasn’t sure of the implications, so I wanted to check. The general concern here would be evaling an arbitrary number of anonymous functions would result in an unavoidable memory leak.

Example of a generated name:

julia> names(Main;all=true) # fresh session
5-element Vector{Symbol}:
 Symbol("##meta#58")
 :Base
 :Core
 :InteractiveUtils
 :Main

julia> (x->x) |> typeof # type's generated name
var"#1#2"

julia> names(Main;all=true) # generated name is here
6-element Vector{Symbol}:
 Symbol("##meta#58")
 Symbol("#1#2")
 :Base
 :Core
 :InteractiveUtils
 :Main

julia> isconst(Main, Symbol("#1#2")) # seems persistent
true

The particular instantiation is garbage collected, but the type & associated constructor sticks around. I don’t think there’s any source claiming that the type itself would be collected too?

1 Like

Right, there’s no even semi-official source about collecting anonymous functions, these were errant vaguely worded comments. I did think at first they were wholly referring to instances, but half the examples had instances that didn’t need to be garbage collected so there was an implication that something else was. It contradicted everything I could observe, but worth verifying.

Do you have an example of such an anonymous function that doesn’t need to be garbage collected?

If you are doing code generation of lots of anonymous functions symbolically, then compiling them via eval, you might be better of with something like DynamicExpressions.jl.

4 Likes

Discussed recently:

1 Like

Sanity considered checked, thanks everyone!

Ones isolated to a local scope would just live on the stack.

1 Like

Pretty sure a local function is actually implemented as a global function, with an automatically generated name. As long as it’s not a closure.

Reading the README, how on earth is it only a bit slower than a compiled method? Or does the discrepancy widen when things get more complicated and there’s more to optimize?

The type gets the name:

julia> (x->x) isa getproperty(Main, Symbol("#1#2")) # fresh session
true
2 Likes