# Distinguishing anonymous functions inside types

Suppose I do the following (extracted from actual code):

``````julia> struct MyTest{F}
f::F
end

julia> function initialize(x::T) where {T}
return MyTest(i->(i==0) ? x : zero(x))
end
initialize (generic function with 1 method)

julia> x = initialize(1.0)
MyTest{getfield(Main, Symbol("##5#6")){Float64}}(getfield(Main, Symbol("##5#6")){Float64}(1.0))

julia> y = initialize(2.0)
MyTest{getfield(Main, Symbol("##5#6")){Float64}}(getfield(Main, Symbol("##5#6")){Float64}(2.0))

julia> typeof(x) == typeof(y)
true
``````

I was not expecting the anonymous functions to be the same and hence the types to be the same. I in fact need these two types to be different. How can I achieve this?

This works:

``````julia> struct MyTest{F}
f::F
end

julia> function initialize(::Val{x}) where {x}
return MyTest(i->(i==0) ? x : zero(x))
end
initialize (generic function with 1 method)

julia> x = initialize(Val(1.0))
MyTest{getfield(Main, Symbol("##5#6")){1.0}}(getfield(Main, Symbol("##5#6")){1.0}())

julia> y = initialize(Val(2.0))
MyTest{getfield(Main, Symbol("##5#6")){2.0}}(getfield(Main, Symbol("##5#6")){2.0}())

julia> typeof(x) == typeof(y)
false
``````

but Iâ€™d be also interested to hear why OPs only constructs one function.

2 Likes

This seems to be a bug to me: the two functions are actually different:

``````julia> x.f(0)
1.0

julia> y.f(0)
2.0
``````

So they should not be given the same name.

No this is not a bug. This is exactly how closures should behave. Their type does not carry all the information about their behavior

3 Likes

Hmm. I had in my head the idea that distinct functions had distinct types. (Apparently I was wrong.)

Can you suggest a solution for my original question in that case?

Also I thought that each time an anonymous function was created, it automatically had a new type.

Maybe this may help

``````function initialize2(::Val{x}) where {x}
@eval foo=i->(i==0) ? \$x : zero(\$x)
return MyTest(foo)
end
``````

I suppose the anonymous function gets parsed once in `initialize` and any subsequent calls to `initialize` just recall the same defined symbolâ€¦ This should solve it by explicitly redefining a new function to be passed to the constructor.

Luckily not! Consider

``````ret = 0
n = 10
for i = 1 : n
f = () -> 2 * i
global ret += f()
end
``````

Compile time would scale with `n`.

1 Like

You could keep a global counter and include it in the `struct`, or have an explicit

``````struct XTester{T}
x::T
end

(f::XTester)(i) = i == 0 ? f.x : zero(f.x)
``````

but it is hard to say more without context.

1 Like

Hereâ€™s the deal: itâ€™s each location where you write an anonymous function that a new type gets created. The anonymous function is created by syntax lowering. (If youâ€™re really curious, you can see how the anonymous function works with `Meta.@lower function initiialize ...`, but itâ€™s messy). The struct that describes the anonymous function is â€śliftedâ€ť out of `initialize` and created at the same time that the `initialize` function is created.

The anonymous function that `initialize` returns is a closure around the argument you pass. In fact, you can even pull out the captured variable with normal field access:

``````julia> t = initialize(2)
MyTest{getfield(Main, Symbol("##5#6")){Int64}}(getfield(Main, Symbol("##5#6")){Int64}(2))

julia> t.f.x
2

julia> t = initialize(1//2)
MyTest{getfield(Main, Symbol("##5#6")){Rational{Int64}}}(getfield(Main, Symbol("##5#6")){Rational{Int64}}(1//2))

julia> t.f.x
1//2
``````

Also note how the anonymous function is parameterized by the type of the captured variable â€” thatâ€™s so it can be fast and type-stable. Even in the parametric case that @mauro3 describes above, youâ€™re just creating one type for the anonymous function â€” itâ€™s just that the exact value is known at compile time and so itâ€™s used as the parameter itself.

12 Likes

Great explanation, thanks!

[quote=â€śmauro3, post:2, topic:21216â€ť]

This almost works, but

``````julia> z = initialize(Val(1.0))
MyTest{getfield(Main, Symbol("##3#4")){1.0}}(getfield(Main, Symbol("##3#4")){1.0}())

julia> x == z
true
``````

I guess the big question is why do you need these to compare different? Theyâ€™ll behave identically. Of course if `MyTest` is mutable it will create a new object each time. You could also throw a simple mutable anonymous function wrapper in between `MyTest` and the function to achieve this:

``````julia> struct MyTest{F}
f::F
end

julia> mutable struct F{FF}
f::FF
end
(f::F)(x...) = f.f(x...)

julia> function initialize(x)
return MyTest(F(i->(i==0) ? x : zero(x)))
end
initialize (generic function with 1 method)

julia> initialize(1) == initialize(1)
false
``````

The types are still the same, but now equality is different.

1 Like

If you really want a new function type each time, call `eval`. But as others have mentioned, this could cause compile time issues.

2 Likes

These functions give the coefficients of a Taylor series;
they effectively encode the AST of a composite function.

I think / thought that I need them to be different objects since

they are put in an array which is then manipulated and in which

the new objects created are of different types.

But thinking about it now, all I need to do is change the type of the array that is created.

In any case, I learned a lot; thanks to all for the comments and explanations!