Here is an old issue: zero() and one() for incomplete and abstract types · Issue #4808 · JuliaLang/julia · GitHub

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.

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.

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

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

```
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

```
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> g(A(), B())
1
julia> g(1, 2.0)
ERROR: MethodError: no method matching _g(::Int64, ::Float64, ::Type{Real})
```

Ok, I missed the *concrete* word there.

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

The type of bound is different.

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

only works if `x`

and `y`

are of the same *concrete* type. While

```
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

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

or

```
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:

```
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> 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> f(x::T) where T <:Union{Vector{Int},Vector{Float32},Vector{Float64}} = 1
f (generic function with 1 method)
```

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.

I think you bring up a good point.

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

```
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> 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,

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> (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

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).

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> [(x,1),(y,1)] isa Array{Tuple{Stuff,Number},1}
false
```

is that

```
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> 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> 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.