In Julia, what is the type of an anonymous function when an outer scope is involved?

99 Views Asked by At

I can create an anonymous function within a list comprehension in Julia. I expect that this would create a type of Vector{Function}. Instead, the type is something like Vector{var"#2#4"}.

For example, when involving an outer scope with an anonymous function

typeof(x->x) <: Function  # true

a = 1
typeof(x-> x + a) <: Function  # false

while

f(x) = x
typeof(f) <: Function  # true

a = 1
g(x) = x + a
typeof(g) <: Function  # true

Why is the anonymous function typed differently than the regular function when the outer scope is involved?

Now, in a list comprehension:

typeof([x->x, x->x]) <: AbstractVector{Function}  # true
typeof([x->x+i for i in 1:2]) <: AbstractVector{Function}  # false
typeof([x->x for i in 1:2]) <: AbstractVector{Function}  # false

whether or not the index i is involved. I initially expected true in each case.

1

There are 1 best solutions below

0
Bogumił Kamiński On BEST ANSWER

First note that your initial example is not correct, as you have:

julia> typeof(x->x) <: Function  # true
true

julia>

julia> a = 1
1

julia> typeof(x-> x + a) <: Function
true

So both anonymous functions have a type that is a subtype of Function.

Now as for comprehension note that this is mostly unrelated with the presence of parameter, as you have:

julia> typeof([x->x, x->x]) <: AbstractVector{Function}
true

julia> typeof([x->x]) <: AbstractVector{Function}
false

julia> typeof([x->x for i in 1:2]) <: AbstractVector{Function}
false

The reason for this behavior is when you write something like [...] then Julia picks a narrow element type of the comprehension (if it did not try to do so you would have Any element type - the behavior that Python have for its standard lists).

So now how to solve your issue.

To make sure that comprehension has element type of Function you need to write:

julia> typeof(Function[x->x]) <: AbstractVector{Function}
true

julia> typeof(Function[x->x for i in 1:2]) <: AbstractVector{Function}
true

See also here and here in the manual where it explains how element type for a result of a comprehension and an array literal is picked.

If you omit the Function prefix the element type is narrower, and in this case the proper subtyping condition would be as follows:

julia> typeof([x->x]) <: AbstractVector{<:Function}
true

julia> typeof([x->x for i in 1:2]) <: AbstractVector{<:Function}
true

(note the extra <:).

This behavior is related to the fact that in Julia such parametric types are invariant, which is explained here in the Julia manual.