Why isn't this true: `[mean, mean, mean] isa Vector{Function}`? but this is: `[mean, mean, sum] isa Vector{Function}`?

Why isn’t this true: [mean, mean, mean] isa Vector{Function}? but this is: [mean, mean, sum] isa Vector{Function}?

Basically I am writing a function like this, but it’s not dispatching on applyfns2tuples([mean, mean,mean],[rand(5), rand(5), rand(5)]).

function applyfns2tuples(fns::Vector{Functions}, tuples)
    ((fns1(tuples1) for (fns1,tuples1) in zip(fns, tuples)...)
end

Type covariance. Vector{Float64} is not a Vector{Number}. mean is a function and every function is a type, so Vector{#mean} where #mean <: Function. Same thing as the number case.

If you want, you can do Function[mean, mean, mean] to get Vector{Function}.

For another option, see this package which really, really, really should be in Base.

This is the same phenomenon as

julia> Vector{Int} <: Vector{Number}
false

even though

julia> Int <: Number
true

I believe it has to do with type covariance.

On the other hand

julia> Vector{Int} <: Vector{T} where {T<:Number}
true

You can instead use

julia> [mean, mean, mean] isa Vector{T} where {T<:Function}
true

Or use shorthand notation

julia> [mean, mean, mean] isa Vector{<:Function}
true
2 Likes

It makes sense now. That’s why Julia can specialise on functions where functions are passed as arguments

4 Likes

If you need to dispatch you can use Vector{<:Function} which encompasses all the vectors of something that is a subtype of Function

This will work, and simplifies your notation a bit (drop the splatting):

apply(funs::Vector{<:Function}, args) = (f(arg) for (f, arg) in zip(funs, args))

I played around and tried to use tuples instead of Vector{<:Function}, but could not find a way to express it. For example,

julia> [mean, mean, sum] isa Vector{<:Function}
true

while

julia> (mean, mean, sum) isa NTuple{3, <:Function}
false
1 Like
julia> (mean, mean, sum) isa NTuple{3, Function}
true

julia> NTuple{3, <:Function}
Tuple{#s1,#s1,#s1} where #s1<:Function
2 Likes