# 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[1] > i[2]
if A[i[1], i[2]] > 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[1]:2
isUpperTriangle(::Array{Number,2}, ::Number) at REPL[1]:2
Stacktrace:
[1] top-level scope at REPL[3]: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[1] > i[2]
if A[i[1], i[2]] > 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[1] > i[2]
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