# Function types unexpectedly equal

``````julia> T = [x->x*k for k in 1:3]
3-element Vector{var"#54#56"{Int64}}:
#54 (generic function with 1 method)
#54 (generic function with 1 method)
#54 (generic function with 1 method)
``````

The three functions in the array are different, but they share the same type. Is this intended? I got the impression that each distinct anonymous function should have its own different type.

Depending on the type of `x` the functions could even return different types, so that seems strange to me.

Is there a way to avoid it and generate different function types instead? Use case: I would like to use function types to dispatch a hand-written `derivative(f)` function, for instance

``````derivative(f::typeof(T)) = 1;
derivative(f::typeof(T)) = 2;
derivative(f::typeof(T)) = 3;
``````

but then the last definition applies to all functions: `derivative(T)` returns `3`.

1 Like

Yes. The language is more efficient this way.

Functions are just callable structs that subtype `Function`, and the captured value is a field of the struct.

``````julia> T = [x->x*k for k=1:3];

julia> dump(first(T))
#34 (function of type var"#34#36"{Int64})
k: Int64 1

julia> [T[i].k for i=1:3]
3-element Vector{Int64}:
1
2
3
``````

Conveniently, because they share the same type, they can reside in the same array while maintaining type-stability.

This is not a problem; when a specialization on a different argument type is compiled for one, it is compiled for all.

For with Julia all things are possible.

``````julia> macro foo(n::Int) :( (\$((:(x->x*\$k) for k=1:n)...),) ) end
@foo (macro with 1 method)

julia> const fs = @foo(3)
(var"#5#8"(), var"#6#9"(), var"#7#10"())

julia> map(fs) do f; f(2) end
(2, 4, 6)
``````

However, looking at what you’re doing, this will be better served by leaving them all the same type and just accessing their member field:

``````derivative(f::typeof(T)) = f.k;
``````

That said, this isn’t public API (who knows, maybe one day we’ll hash anonymized function bodies so anonymous functions don’t need to be recompiled); you’re better served by going the direct route and making your own callable struct:

``````struct Foo{K} <: Function;  k::K  end
(f::Foo)(x) = x*f.k
derivative(f::Foo) = f.k
T = [Foo(k) for k=1:3]
``````
3 Likes

It is not necessary to subtype `Function` here - that type has some assumptions in addition to being callable.

Indeed, but I figure if we’re replacing a lambda (which subtypes `Function` anyway) it’s probable that we’d want to opt-in to some of those e.g. broadcasting behaviors.

Thanks, this clears things up for me.

One additional question then: what can I assume on the type of functions? For instance, can I assume that anonymous functions generated in different lines of code with different `->` expressions will have different types?

Yes, this is correct for now. I don’t know if it will remain correct forever though, as there’s an implicit assumption with anonymous functions that you don’t care about their name.

If you want greater assurance, you can use `gensym` with named function syntax:

``````julia> macro foo(n::Int) :( (\$((esc(:(\$(gensym(:foo))(x) = x*\$k;)) for k=1:n)...),) ) end
@foo (macro with 1 method)

julia> @foo(3)
(var"##foo#304", var"##foo#305", var"##foo#306")
``````
1 Like