Expected Vector{Number}, got a value of type Vector{Int64}

So I have this function, but it is giving me a very strange error.

function runCommands(;programName::String="simulation", dt::Number=0.01, T::Number=2, temps::Vector{Number}, N::Integer, sizeArr::Vector{Integer})
    cmdStrings = map((temp, size) -> `./$programName dt=$(dt)_T=$(T)_temp=$(temp)_N=$(N)_size=$(size)`, temps, sizeArr)
    map(run, cmdStrings)
end
runCommands(temps= [0], N=4, sizeArr=[4])

But it gives me this error, and I can’t see why it should be a problem:
ERROR: TypeError: in keyword argument temps, expected Vector{Number}, got a value of type Vector{Int64}

When getting errors, it is best to read carefully and assume the details are correct, even if the reason is still not clear.

In this case, temps is indeed a Vector{Int64} and a Vector{Number} is expected as specified in the definition. As evidence:

julia> typeof( [0] )
Vector{Int64} (alias for Array{Int64, 1})

whereas

julia> typeof( Number[0] )
Vector{Number} (alias for Array{Number, 1})

Perhaps the function definition should go with temps::Vector{<:Number} which matches Vector{Int64} too.

Better still, the typing of parameters is not mandatory in Julia and it is usually cleaner to just drop them and let the compiler infer and compile the correct version of a function. For example, dropping all parameter types:

function runCommands(;programName="simulation", dt=0.01, T=2, temps, N, sizeArr)
    cmdStrings = map((temp, size) -> `./$programName dt=$(dt)_T=$(T)_temp=$(temp)_N=$(N)_size=$(size)`, temps, sizeArr)
    map(run, cmdStrings)
end
1 Like

Thanks, the subtype declaration solved the issue, but why does Julia care if it is Vector{Int64} and not Vector{Number}? Why is there an explicit need for this when x::Number can readily accept any subtypes anyway?

This is connected to the way the type system in Julia has been defined (since forever) and it is actually quite a good thing. Specifically: Vector{Int64} is NOT a subtype of Vector{Number}. This property is sometimes called invariant-parametric types. The best thing is to read up on it in the docs:
https://docs.julialang.org/en/v1/manual/types/#man-parametric-composite-types

4 Likes

In short, Number is an abstract type of no definite size. A Vector{Number} is therefore stored as a collection of pointers to whatever Numbers are put in there. On the other hand, an Int64 is a concrete type of size 8 bytes. A Vector{Int64} is therefore a contiguous chunk of memory of 8-byte integers. This enables the compiler to generate much more efficient code.

Specifying a formal argument as Vector{Number} requires the actual argument to be just that, a collection of pointers to various types of Numbers. Specifying it as Vector{<:Number} means that the actual argument can be a Vector of any number type, in particular a Vector{Int64}. The function will be compiled for the actual argument, i.e. there will be a compiled version for Vector{Int64} as well as a compiled version for Vector{Float16} (if you ever call the function with such thing).

It would be possible to let Vector{Number} mean a vector of any number type, i.e. having covariant type parameters, and the parameters work like that for the types Union and Tuple. There are drawbacks and benefits with everything, but julia has always had invariant type parameters (except for Union and Tuple).

4 Likes

The way I think of it is that a Vector{Number} is a Vector that can accept and store a number of any type, while Vector{Int64} can only store Int64. Therefore they cannot be the same. On the other hand x::Number can be any type of number.

1 Like