# 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:** 6
**Page:** 2

<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, 4:14pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/21 "2021-02-23T16:14:57Z")

</div>

> [@Shuhua](#):
>
> Maybe we should @ a Julia language designer for clarification, but I don’t know who

Here is an old issue: [zero() and one() for incomplete and abstract types · Issue #4808 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/4808)

---

<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, 5:06pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/22 "2021-02-23T17:06:48Z")

</div>

> [@lmiq](#):
>
> 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 don’t understand. What words do you use to describe the type `Vector{SUBTYPE} where SUBTYPE <: SUPERTYPE` ? As far as I can tell, you are describing it.

If `SUBTYPE` is an abstract type (which is a possibility if e.g `SUPERTYPE` is `Real` and `SUBTYPE` is `Integer`), then none of the elements give `typeof(element) == SUBTYPE` because a value can only have a concrete type, not an abstract type.

> [@lmiq](#):
>
> `Vector{T} where T<:Real` for me clearly suggests that `T` is _one_ of the types which are subtypes of `Real` .

It suggests that to me too, and as far as I can tell it _is_ that. That _single one_ type can be an abstract type.

> [@lmiq](#):
>
> ```julia
> 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)
> 
> ```

I think I see what you mean by “arbitrary”. Did you want that method to be called with `T` bound to `C`, like it is for `f(x::Vector{T}) where T<:C`?

Note the following three relationships:

The relationship between the `T` in `v::Vector{T}` and an element of a `v`, e.g. `v[1]`, is:  
`v[1] isa T`

The relationship between `T` in `foo(x::T)` and `x` is:  
`x isa T`  
(note that `T` must be defined. e.g. `const T = Real`)

The relationship between `T` in `foo(x::T) where {T}` and `x` is:  
`x == T`

I don’t know if that last relationship is very clearly encoded in the manual generally, but there is an example with `same_type`: [Methods · The Julia Language](https://docs.julialang.org/en/v1/manual/methods/#Parametric-Methods)

If I understood your expected behavior, you can get it like this:

```julia
function g(x::T1, y::T2)
    T = typejoin(T1, T2)
    # ...
end

```

Now `x isa T` and `y isa T`.

If you want to constrain `T`, you’ll (as of now) need to do something like

```julia
g(x::T1, y::T2) where {T1, T2} = _g(x, y, typejoin(T1, T2))

function _g(x, y, lub::Type{T}) where T<:C
    return 1
end

```

```julia
julia> g(A(), B())
1

julia> g(1, 2.0)
ERROR: MethodError: no method matching _g(::Int64, ::Float64, ::Type{Real})

```

---

<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, 5:50pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/23 "2021-02-23T17:50:13Z")

</div>

> [@goretkin](#):
>
> I don’t understand. What words do you use to describe the type `Vector{SUBTYPE} where SUBTYPE <: SUPERTYPE` ? As far as I can tell, you are describing it.

Ok, I missed the _concrete_ word there.

> [@goretkin](#):
>
> That _single one_ type can be an abstract type.

Exactly, that is what I feel that is not what it means.

> [@goretkin](#):
>
> Did you want that method to be called with `T` bound to `C` , like it is for `f(x::Vector{T}) where T<:C` ?

The type of bound is different.

```julia
f(x::T,y::T) where T<:Real = 1 

```

only works if `x` and `y` are of the same _concrete_ type. While

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

```

works even the elements of `x` have different concrete types. It is what it is, but it could mean something else. And, at this point of my understanding, would be more natural and consistent with the meaning of the `f(x::T,y::T) where T` if `T` could only be one _concrete_ type in the container. That would leave the notations

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

```

or

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

```

to the case where one wants to allow vectors of mixed concrete types (`UnionAll`).

The notation for functions is “complete”, as:

```julia
f(x::T,y::T) where T<:Real = 1

```

works only if `x` and `y` are of the same concrete type, and if we want something more general, one can write

```julia
julia> f(x::T1,y::T2) where {T1<:Real,T2<:Real} = 1
f (generic function with 1 method)

```

which is very explicit on what it means.

For a vector if I want my function to work only for vectors that have elements of the same concrete type (`Float64[],Float32[],Int[]...`, but not `Real[]` or `Numer[]`), there is no concise way to write that, and we have to be very verbose, such as

```julia
julia> f(x::T) where T <:Union{Vector{Int},Vector{Float32},Vector{Float64}} = 1
f (generic function with 1 method)

```

> [@goretkin](#):
>
> Here is an old issue: [zero() and one() for incomplete and abstract types · Issue #4808 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/4808)

The fact that that is considered an issue I think illustrates my point. If that is changed to an error, then it will break codes which rely on `zero(T)` or `one(T)` for a function that was defined with the `Vector{T} where T` parameterization if called with a container of abstract types, which means that the function should be expecting a container “completely concrete” as SK calls them there.

---

<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, 6:28pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/24 "2021-02-23T18:28:50Z")

</div>

> [@lmiq](#):
>
> For a vector if I want my function to work only for vectors that have elements of the same concrete type ( `Float64[],Float32[],Int[]...` , but not `Real[]` or `Numer[]` ), there is no concise way to write that, and we have to be very verbose, such as
> 
> ```julia
> julia> f(x::T) where T <:Union{Vector{Int},Vector{Float32},Vector{Float64}} = 1
> f (generic function with 1 method)
> 
> ```

I think you bring up a good point.

As an aside, I point out that this is less verbose:

```julia
julia> f(x::Vector{T}) where T <:Union{Int,Float32,Float64} = 1
f (generic function with 1 method)

julia> f(Vector{Int}())
1

julia> f(Vector{Real}())
ERROR: MethodError: no method matching f(::Array{Real,1})

```

Though note that it is a different method signature, and it is “less specific” than the one you proposed. The subtyping (and on top of that, method ordering) in Julia can be unexpected for sure:

```julia
julia> T1 = Vector{<:Union{Int,Float32,Float64}}
Array{var"#s12",1} where var"#s12"<:Union{Float32, Float64, Int64}

julia> T2 = Union{Vector{Int},Vector{Float32},Vector{Float64}}
Union{Array{Float32,1}, Array{Float64,1}, Array{Int64,1}}

julia> T1 == T2
false

julia> T2 <: T1
true

```

Back to your point,

> [@lmiq](#):
>
> if `T` could only be one _concrete_ type in the container.

I see, so it would feel more natural if `Vector{T}` behaved more like the method definition. Not vice-versa.

In other words, the current behavior is:

```julia
julia> (1, 1) isa Tuple{T, T} where T
true

julia> (1, 2.0) isa Tuple{T, T} where T
false

julia> [1, 1] isa Vector{T} where T
true

julia> [1, 2.0] isa Vector{T} where T
true

```

and it would feel more natural if the last value were `false`. Alternatively, you’d like a type such as `Vector{T} where isconcretetype(T)`

Looks like this has been discussed a bit here: [Explicit concrete type constraint in UnionAll · Issue #30363 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/30363)

---

<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, 6:37pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/25 "2021-02-23T18:37:31Z")

</div>

> [@goretkin](#):
>
> Alternatively, you’d like a type such as `Vector{T} where isconcretetype(T)`

Indeed, you are able to express it much more clearly. I just point that is not that “I would like”, I don’t need that as a feature for any reason. As I mentioned, I cannot find any example where a function works with containers of every concrete subtype of an abstract type but would not with a container of mixed types of the same abstract type. Thus, adding that would be one case of overspecialization of the function. (the only tricky thing is that working on mixed containers comes with a performance cost).

---

<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, 6:51pm UTC](https://discourse.julialang.org/t/why-isa-x-1-y-1-array-tuple-stuff-number-1-false/55777/26 "2021-02-23T18:51:50Z")

</div>

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

In respect to the original poster, I will give an explicit answer here with his example, I am not sure if that was clear at the end:

The reason for

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

```

is that

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

```

Note that the array is of `Tuple{Stuff,Int64}`, which means that this array _can only_ contain tuples composed by those two concrete types. That same array _cannot_ contain a tuple where the second element is a `Float64`, for example:

```julia
julia> v = [(x,1),(y,1)];

julia> push!(v,(x,2.0))
3-element Array{Tuple{Stuff,Int64},1}:
 (Stuff("x"), 1)
 (Stuff("y"), 1)
 (Stuff("x"), 2)

```

Note that the `2.0` was converted to an `Int`.

This means that this array contains a _constraint_ for the types of numbers that it can handle.

An array of type `Array{Tuple{Stuff,Number},1}` can contain different types of numbers:

```julia
julia> v = Tuple{Stuff,Number}[(x,1),(x,1)]
2-element Array{Tuple{Stuff,Number},1}:
 (Stuff("x"), 1)
 (Stuff("x"), 1)

julia> push!(v,(x,2.0))
3-element Array{Tuple{Stuff,Number},1}:
 (Stuff("x"), 1)
 (Stuff("x"), 1)
 (Stuff("x"), 2.0)

```

Therefore, while this last array can contain a greater variety of number types, it does not contain the _constraint_ that the first array carries.

This is why neither one is a subtype of the other. Both have _something_ that the other does not have. In other words, an `Array{Tuple{Stuff,Int64},1}` _is not_ simply an `Array{Tuple{Stuff,Number},1}` that happened to only have `Int64` numbers inside.

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