# Usage of Correct Types in Array

Hi,

I want to write a function that can take matrix composing of both int and float and return if it is a upper triangle matrix or not:

``````function isUpperTriangle(A::Array{Number, 2}, tol::Number=1e-3)::Bool
for i in CartesianIndices(A)
if i > i
if A[i, i] > tol
return false
end
end
end
true
end
``````

However, when I test it out like:

``````tt = [ 1 2 3; 0 3 4; 0 0 5]
isUpperTriangle(tt) # Should return true
``````

it returns:

``````julia> isUpperTriangle(tt) # Should return true
ERROR: MethodError: no method matching isUpperTriangle(::Array{Int64,2})
Closest candidates are:
isUpperTriangle(::Array{Number,2}) at REPL:2
isUpperTriangle(::Array{Number,2}, ::Number) at REPL:2
Stacktrace:
 top-level scope at REPL:1
``````

If I change the type annotation from `Number` to `<:Number` then it works fine. The thing I don’t understand is I can use the annotation like this:

``````function test_fun(x::Number)
println(x)
end
``````

and that accepts both Int and Float fine. Why is that?

Thank you!

Types in Julia are invariant. Thus `x{A}<:x{B}` implies `A===B`. https://stackoverflow.com/questions/8481301/covariance-invariance-and-contravariance-explained-in-plain-english is a pretty good rundown of the difference.

2 Likes

As mentioned, parametric types “Type{T}” with different values for T are never consided the same type.

What you can do is explicitely initiate your input array as:

Array{Number,2}([ 1 2 3; 0 3 4; 0 0 5])

The better approach is to define `isUpper` using `<:Number`

This is 8x slower on my laptop, so I would not recommended this in place of just doing things the proper way.

1 Like

To clarify, the proper way is the following:

``````function isUpperTriangle(A::Array{<:Number, 2}, tol::Number=1e-3)::Bool
for i in CartesianIndices(A)
if i > i
if A[i, i] > tol
return false
end
end
end
true
end

julia> tt = [ 1 2 3; 0 3 4; 0 0 5];

julia> isUpperTriangle(tt)
true
``````

The only difference is the `Array{<:Number, 2}` in the function signature instead of `Array{Number, 2}`.

By the way, it is often useful to write methods that are as general as possible (maybe not in this case, but that’s up to you). You could get the same functionality with this more general function:

``````function isUpperTriangle(A::AbstractMatrix, tol = 1e-3)
for i in CartesianIndices(A)
if i > i
if A[i] > tol
return false
end
end
end
true
end
``````

Now this still works:

``````julia> isUpperTriangle(tt)
true
``````

But now so does any matrix type, as long as `tol` was something comparable with the elements of `A`. Note below that `tt'` (tt transpose) is not an `Array`, but the function still works.

``````julia> isUpperTriangle(tt')
false
``````

The following will also work

``````julia> isUpperTriangle(Char.(tt), Char(1))
true

julia> isUpperTriangle(Char.(tt'), Char(1))
false
``````
1 Like

This is because `Number` is an abstract type and `Float64 <: Number`. Therefore a function written for `x::Number` can be compiled for `Float64`. However, `Vector{Number}` is a concrete type, and `Vector{Float64}` is therefore not a subtype of `Vector{Number}`. Since it is not a subtype, a function written for `Vector{Number}` cannot accept `Vector{Float64}`. This is why you must specify `Vector{<:Number}`.

By the way, this can be understood by realizing that it is possible to promote an array of `Float64`s to an array of `Numbers`, but it is never possible to promote a single `Float64` to a `Number`. If a type can “exist” as an object, it is necessarily concrete.

``````julia> typeof(Vector{Number}([1.0,2.0]))
Array{Number,1}

julia> typeof(Number(1.0))
Float64
``````
1 Like