1-Element Lists of Functions have a weird Type

Viewed 53

I want to create a Vector of Functions. This works fine, if the vector has multiple elements:

[mean, sum]
2-element Vector{Function}:
mean (generic function with 95 methods)
sum (generic function with 30 methods)

As expected the type here is Vector with element type Function.

But if the vector has only one element, this happens:

[mean]
1-element Vector{typeof(mean)}:
mean (generic function with 95 methods)

Suddenly it is a Vector with element type typeof(mean). Is this a bug or am I not smart enough to get the point?

3 Answers

Function in Julia is an abstract type:

julia> isabstracttype(Function)
true

For a given function foo calling typeof(foo) returns a concrete type:

julia> dump(typeof(mean))
typeof(mean) <: Function

Since typeof(foo) returns a concrete type you can have a vector of union of such types:

julia> Union{typeof(mean), typeof(sum)}[mean, sum]
2-element Vector{Union{typeof(sum), typeof(mean)}}:
 mean (generic function with 8 methods)
 sum (generic function with 20 methods)

This also means you can create such empty Vector:

v = Vector{Union{typeof(mean), typeof(sum)}}()
Union{typeof(sum), typeof(mean)}[]

and now you can add elements to it:

julia> push!(v,mean)
1-element Vector{Union{typeof(sum), typeof(mean)}}:
 mean (generic function with 8 methods)

julia> push!(v,sum)
2-element Vector{Union{typeof(sum), typeof(mean)}}:
 mean (generic function with 8 methods)
 sum (generic function with 20 methods)

When you just do [mean, sum] Julia tries to find a type that captures both typeof(mean) and typeof(sum) and you end-up with the abstract Function type.

For a function, let's say mean, its type is simply typeof(mean) and end of the story. So when you only have mean in a vector, Julia tries to use the most specific type. When you have two different functions, there's no more common type other than the abstract type Function, thus Julia will just fall back to that.

This is analogous to:


julia> [1]
1-element Vector{Int64}:
 1

julia> [1, "blah"]
2-element Vector{Any}:
 1
  "blah"

You can create empty Vector{Function} and push the function to it, or append! another vector of functions to it:

using Statistics
funcs  = Vector{Function}()
push!(funcs, mean)
push!(funcs, sum)
append!(funcs, [minimum, maximum])
println(funcs)
Related