# Why? isa(\[(x,1),(y,1)\], Array{Tuple{Stuff,Number},1}) = false

**URL:** https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777
**Category:** General Usage
**Created:** [February 22, 2021, 1:49pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777 "2021-02-22T13:49:54Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![maajdl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maajdl/32/22838_2.png) [@maajdl](https://discourse.julialang.org/u/maajdl)
#### Post date: [February 22, 2021, 1:49pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/1 "2021-02-22T13:49:54Z")

</div>

Hello,

I do not understand the output of this script:

```julia
struct Stuff
    a
end

x = Stuff("x")
y = Stuff("y")

test1 = isa(1, Number)
test2 = isa((x,1), Tuple{Stuff,Number})
test3 = isa([(x,1),(y,1)], Array{Tuple{Stuff,Number},1})

println(test1)
println(test2)
println(test3)

```

The output is:

```julia
true
true
false

```

I would have expected:

```julia
true
true
true

```

Since this is what this other snippet produces:

```julia
...
test1 = isa(1, Int64)
test2 = isa((x,1), Tuple{Stuff,Int64})
test3 = isa([(x,1),(y,1)], Array{Tuple{Stuff,Int64},1})

println(test1)
println(test2)
println(test3)

```

Could you help me to understand the reason of this behaviour?

Thanks,

Michel

---

<div class="post-metadata">

### Author: ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)
#### Post date: [February 22, 2021, 1:56pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/2 "2021-02-22T13:56:36Z")

</div>

Related: [Why are tuples covariant?](https://discourse.julialang.org/t/why-are-tuples-covariant/45005)

Consider

```julia
test4 = isa([(x,1),(y,1)], Array{<:Tuple{Stuff, Number},1})

```

---

<div class="post-metadata">

### Author: ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)
#### Post date: [February 22, 2021, 2:02pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/3 "2021-02-22T14:02:35Z")

</div>

> [@maajdl](#):
>
> `isa([(x,1),(y,1)], Array{Tuple{Stuff,Number},1})`

`Number` is an `abstract type`, whereas

```julia
julia> isa([(x,1),(y,1)], Array{Tuple{Stuff,Int64},1})
true

```

---

<div class="post-metadata">

### Author: ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)
#### Post date: [February 22, 2021, 3:14pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/4 "2021-02-22T15:14:15Z")

</div>

Complementing the previous answers, there is no guarantee about how Julia will infer the type of an Array/Vector literal with elements of many distinct types inside. Using a single type like you did will most probably get an Array/Vector of the specific type, but keep in mind that if you mix types the result may be either the specific `Union` of the mixed types, or a supertype of all the mixed types (what in many cases, end up being `Any`).

---

<div class="post-metadata">

### Author: ![Shuhua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuhua/32/27618_2.png) [@Shuhua](https://discourse.julialang.org/u/Shuhua)
#### Post date: [February 22, 2021, 4:00pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/5 "2021-02-22T16:00:12Z")

</div>

> [@goretkin](#):
>
> Related: [Why are tuples covariant?](https://discourse.julialang.org/t/why-are-tuples-covariant/45005)
> 
> Consider
> 
> ```julia
> test4 = isa([(x,1),(y,1)], Array{<:Tuple{Stuff, Number},1})
> 
> ```

Further explanation for a newbie: the key reason is that `Array` is not covariant (or more generally, " Julia’s type parameters are _invariant_"; see the link above).

A more concise example to show the invariance:

```julia
julia> 1 isa Real
true

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

julia> [1] isa Vector{Real}
false

julia> Int64 <: Real
true

julia> [1] isa Vector{<:Real} # <: means any subtype of Real
true

```

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 22, 2021, 4:52pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/6 "2021-02-22T16:52:04Z")

</div>

[Covariance and etc.](https://en.m.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) mean so many things outside computer science that it took me a while to get what people where saying here.

I prefer to explain, probably not as comprehensively, but at least simply, by noting that:

First, we have to differentiate two things:

a) An array that _can only_ contain numbers of type `Float64`  
b) An array that _can_ contain real numbers of different types (mixed `Float64` and `Int64`, for example).

Vectors of type (b) are not a subtype of vectors of type (a), of course, because vectors of type (a) cannot contain an `Int64`, for example. This is clear and translates to:

```julia
Vector{Real} <: Vector{Float64} == false

```

Less clear is that an array of type (a) is also not a subtype of an array of type (b). This is because an array of type (a) _has a constraint_ that vectors of type (b) do not. Thus, a vector of type (a) is not a subtype of vectors of type (a), and this translates to the more unnatural

```julia
Vector{Float64} <: Vector{Real} == false

```

Second, the usual confusion is that `Vector{Real}` is intuitively thought as _all types of vectors that contain real numbers_. Well, this is the wrong way of reading that. As pointed above, `Vector{Real}` is the type of a concrete vector that _is able_ to contain any type of real number. Thus, this does not include the vectors that _cannot_ contain `Int64`s, for instance.

We need a notation for the _set of vectors_ that may contain real numbers, restricted or not by type. The notation might sound arbitrary, but we need one, and it is `Vector{<:Real}`. Since this is the notation that encompasses different types of vectors, it is an _abstract type_\*, contrary to the other two above, which are _concrete types_.

No actual vector is, therefore, of type `Vector{<:Real}`. To be very redundant:

```julia
julia> typed(Real[1,2.0,π,Float32(7)]) == Vector{<:Real}
false

```

But all vectors that contain only real numbers, are subtypes of `Vector{<:Real}`:

```julia
julia> typeof(Real[1,2.0,π,Float32(7)]) <: Vector{<:Real}
true

julia> typeof(Int[1,2,3]) <: Vector{<:Real}
true

```

When one uses `Vector{<:Real}` we are referring a _set_ of types. The final confusion that may arise, is, for example, that:

```julia
julia> typeof(Int64[1,2,3]) == Vector{<:Int64}
false

```

This is `false` because `Vector{<:Int64}` is the _set_ of types of vectors that contain only `Int64` numbers. It is not a concrete type of vector, even if the set contains only one type which is `Vector{Int64}`.

Of course:

```julia
julia> typeof(Int64[1,2,3]) <: Vector{<:Int64}
true

```

\*Strictly speaking, in the Julia language, something like `Vector{<:Real}` is of the `UnionAll` type, which is something in between between a completely abstract type which only serve as nodes in the type tree, and a concrete type which can actually be instantiated. `UnionAll` types do have information on how they should be instantiated, by that information is not complete.

(note: the final form of this post has contributions from others, given below).

---

<div class="post-metadata">

### Author: ![maajdl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maajdl/32/22838_2.png) [@maajdl](https://discourse.julialang.org/u/maajdl)
#### Post date: [February 22, 2021, 4:52pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/7 "2021-02-22T16:52:46Z")

</div>

Thanks  
Had forgotten to say I a beginner in Julia.  
Now reminds me a bit of Scala …

---

<div class="post-metadata">

### Author: ![yha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yha/32/3502_2.png) [@yha](https://discourse.julialang.org/u/yha)
#### Post date: [February 22, 2021, 11:06pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/8 "2021-02-22T23:06:57Z")

</div>

> [@lmiq](#):
>
> No actual vector is, therefore, of type `Vector{<:Real}` . To be very redundant:
> 
> ```julia
> julia> Real[1,2.0,π,Float32(7)] isa Vector{<:Real}
> false
> 
> ```

That’s actually `true`, because `typeof(Real[1,2.0,π,Float32(7)]) === Vector{Real} <: Vector{<:Real}`.

```julia
julia> Real[1,2.0,π,Float32(7)] isa Vector{<:Real}
true

```

(`x isa T` is equivalent to `typeof(x) <: T`, not `typeof(x) == T`).  
I think you meant to point out that `typeof(Real[1,2.0,π,Float32(7)]) !== Vector{<:Real}`.  
Same with the `Int64[1,2,3] isa Vector{<:Int64}` example.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 22, 2021, 11:21pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/9 "2021-02-22T23:21:09Z")

</div>

Actually I had written correctly and changed it… I did not notice that `isa` is not `typeof() ==`. Didn’t like that 😐 (fixing the post). Thanks.

---

<div class="post-metadata">

### Author: ![yha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yha/32/3502_2.png) [@yha](https://discourse.julialang.org/u/yha)
#### Post date: [February 22, 2021, 11:27pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/10 "2021-02-22T23:27:59Z")

</div>

> [@lmiq](#):
>
> I did not notice that `isa` is not `typeof ==` . Didn’t like that 😐

Seems quite natural to me that `x isa T` is `typeof(x) <: T`. After all, 1 is a number, so it makes sense that `1 isa Number` 😁.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 22, 2021, 11:34pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/11 "2021-02-22T23:34:58Z")

</div>

Yes, you are right. Probably there is no better choice for that notation. That is a little bit more confusing with containers (yet I like it again 🙂 ).

---

<div class="post-metadata">

### Author: ![Shuhua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuhua/32/27618_2.png) [@Shuhua](https://discourse.julialang.org/u/Shuhua)
#### Post date: [February 23, 2021, 3:13am UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/12 "2021-02-23T03:13:37Z")

</div>

> [@lmiq](#):
>
> `Vector{Real}` is the type of a concrete vector

Indeed correct. The principle of covariance was a little confusing to me at the beginning as well especially with backgrounds in other languages like C#.

An additional comment is that `isabstractype` and `isconcretetype` can be used to check whether a type is abstract or concrete.

```julia
julia> isabstracttype(Vector{Real})
false

julia> isconcretetype(Vector{Real})
true

```

We see that `Vector{Real}` cannot even be inherited, i.e., no (strict) subtypes.

However, the following statement may not be exact:

> [@lmiq](#):
>
> it is `Vector{<:Real}` . Since this is the notation that encompasses different types of vectors, it is an _abstract type_ ,

because

```julia
julia> isconcretetype(Vector{<:Real})
false

julia> isabstracttype(Vector{<:Real})
false

```

`Vector{<:Real}` is an abstract type in the _general_ sense 😁, but not in the strict sense of Julia 😅.

```julia
isabstracttype(T)
  Determine whether type T was declared as an abstract type 
  (i.e. using the abstract keyword).

```

---

<div class="post-metadata">

### Author: ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)
#### Post date: [February 23, 2021, 3:16am UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/13 "2021-02-23T03:16:13Z")

</div>

> [@lmiq](#):
>
> The notation might sound arbitrary, but we need one, and it is `Vector{<:Real}`

I for one find it quite not-arbitrary.

```julia
julia> Vector{<:Real}
Vector{var"#s46"} where var"#s46"<:Real (alias for Array{var"#s46", 1} where var"#s46"<:Real)

```

Using a named type parameter makes it easier to read:

```julia
julia> Vector{T} where T <: Real
Vector{T} where T<:Real (alias for Array{T, 1} where T<:Real)

```

meaning the `Union` of all types `Vector{T}`, where `T` ranges over all types that are subtypes of `Real`.

Indeed that is what a `UnionAll` type is:

```julia
julia> typeof(Vector{<:Real})
UnionAll

```

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 23, 2021, 12:20pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/14 "2021-02-23T12:20:19Z")

</div>

> [@Shuhua](#):
>
> abstract type in the _general_ sense 😁, but not in the strict sense of Julia 😅

Good point. But is there a third category of types which does not fit into `concrete` or `abstract`? Shoudn’t every type which is not concrete be abstract by definition? Is that just an implementation detail?

> [@goretkin](#):
>
> I for one find it quite not-arbitrary.

With that I do not agree. Actually the parametric syntax is even more confusing. `Vector{T} where T<:Real` for me clearly suggests that `T` is _one_ of the types which are subtypes of `Real`. This is particularly confusing because the behaviour is somewhat contradictory if `T` is the parameterization of the types in a vector or the types of the arguments of a function:

```julia
julia> abstract type C end

julia> struct A <: C end

julia> struct B <: C end

julia> f(x::Vector{T}) where T<:C = 1
f (generic function with 1 method)

julia> f([A(),B()])
1

julia> g(x::T,y::T) where T<:C = 1
g (generic function with 1 method)

julia> g(A(),B())
ERROR: MethodError: no method matching g(::A, ::B)
Closest candidates are:
  g(::T, ::T) where T<:C at REPL[6]:1
Stacktrace:
 [1] top-level scope at REPL[7]:1

```

Because of that we end up not having, as far as I know, a concise syntax to indicate vectors of one of the single types which are subtypes of a supertype. I understand that that is not very useful, as any function that is able to operate on a vectors of every subtype of `Real` (for example) is able to operate on a vector of mixed types of `Real` (likely with a performance penalty for run time dispatch). Yet that leads to some arbitrary behaviours for those functions, such as:

```julia
julia> h(x::Vector{T}) where T<:Real = zero(T)
h (generic function with 1 method)

julia> typeof(h(Real[1,2.0]))
Int64

```

---

<div class="post-metadata">

### Author: ![Shuhua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuhua/32/27618_2.png) [@Shuhua](https://discourse.julialang.org/u/Shuhua)
#### Post date: [February 23, 2021, 1:22pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/15 "2021-02-23T13:22:52Z")

</div>

> [@lmiq](#):
>
> ```julia
> julia> h(x::Vector{T}) where T<:Real = zero(T)
> h (generic function with 1 method)
> 
> julia> typeof(h(Real[1,2.0]))
> Int64
> 
> ```

This seems unexpected at the first sight. However, note that

```julia
julia> p(x::Vector{T}) where T<:Real = T
p (generic function with 1 method)

julia> p(Real[1,2.0])
Real

```

Thus, the inferred type of `T` is `Real` as we have expected.

The seemingly weird behavior of `h` is due to the following fact:

```julia
julia> typeof(zero(Real))
Int64

```

It was the first time I noticed that `zero(Real)` gave an `Int64`. IMHO, `zero(Real)` should raise an error, since it cannot actually determine the concrete type.  
Maybe we should @ a Julia language designer for clarification, but I don’t know who 🤣

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 23, 2021, 1:33pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/16 "2021-02-23T13:33:25Z")

</div>

> [@Shuhua](#):
>
> `zero(Real)` should raise an error, since it cannot actually determine the concrete type

If that raised an error the parameterization should not accept any kind of vectors of mixed types, which goes in to the direction of my point that `Vector{T} where T<:Real` should better be a vector of _one_ of the subtypes of `Real`. Any change on that will be of course be breaking, but I would be more comfortable if the error was raised here already:

> [@Shuhua](#):
>
> ```julia
> julia> p(Real[1,2.0])
> Real
> 
> ```

given the fact that `T` in the function definition is not a concrete type. Note that if we stick with

```julia
julia> f(x::Vector{<:Real})

```

we are not tempted to use `zero(T)` inside the function (we can do of course `zero(eltype(x))` with the same ambiguity, and _that_ could throw an error perhaps (or just return any `zero`, as it is, because it will be promoted whenever necessary).

Edit: If `zero(Real)` returned anything else than `zero(Int)`, a function using that would never return an Int, as one could expect from, for example

```julia
f(x::Vector{T}) where T<:Real = zero(T) + 1

```

---

<div class="post-metadata">

### Author: ![Shuhua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shuhua/32/27618_2.png) [@Shuhua](https://discourse.julialang.org/u/Shuhua)
#### Post date: [February 23, 2021, 1:52pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/17 "2021-02-23T13:52:39Z")

</div>

> [@lmiq](#):
>
> my point that `Vector{T} where T<:Real` should better be a vector of _one_ of the subtypes of `Real` .

Yes, I agree. These are mostly corner cases. I have never used any mixed-type vectors. For now, an assertion (or error) may help to forbid non-concrete types.

```julia
julia> h(x::Vector{T}) where T<:Real = @assert isconcretetype(T)
h (generic function with 1 method)

julia> h([1, 2, 3])

julia> h(Real[1, 2.3])
ERROR: AssertionError: isconcretetype(T)
Stacktrace:
 [1] h(::Array{Real,1}) at .\REPL[40]:1
 [2] top-level scope at REPL[41]:1

```

---

<div class="post-metadata">

### Author: ![maajdl](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maajdl/32/22838_2.png) [@maajdl](https://discourse.julialang.org/u/maajdl)
#### Post date: [February 23, 2021, 2:09pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/18 "2021-02-23T14:09:19Z")

</div>

Would anybody do a summary …  
Or suggest the best 2 readings on the topic ?  
🤣

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [February 23, 2021, 2:12pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/19 "2021-02-23T14:12:45Z")

</div>

Sorry… I think the answers to your question ended [here.](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/6). What follows is interesting and may be helpful after one gets more or less used to the rules there, but we are diverging from the original question.

---

<div class="post-metadata">

### Author: ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)
#### Post date: [February 23, 2021, 2:20pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/20 "2021-02-23T14:20:12Z")

</div>

> [@lmiq](#):
>
> But is there a third category of types which does not fit into `concrete` or `abstract` ? Shoudn’t every type which is not concrete be abstract by definition?

I do believe you are searching for the [UnionAll types](https://docs.julialang.org/en/v1/devdocs/types/#UnionAll-types).

[Next page](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777.md?page=2)
