Is `where T` the same as `where T <: Any` in all cases?

So, my question is pretty straight-forward (and right there entirely in the title), but to give a bit of background/justification…

I realize that the function foo(bar::Baz{T}) where T syntax is relatively new, and so some of my discomfort with it could be chalked-up to unfamiliarity, but even after some months I still think that function foo(bar::Baz{T}) where T <: Any reads better. Before I start trying out that style in my own code, I thought I’d just toss this simple question out there: are those equivalent in all cases? I know that conceptually they should be the same, and they are in every case I could think to test, but maybe I’m missing something?

It’s odd. I would have assumed the answer is “no”, because I can have a type parameter which is not a type (for example, in Array{Int, 1}, 1 is an Int, not a type, and 1 <: Any is not true (in fact it’s nonsensical). However, Julia does consider it a match for Array{Int, N} where {N <: Any} as well as Array{Int, N} where N:

julia> Array{Int, 1} <: (Array{Int, N} where {N <: Any})
true

julia> Array{Int, 1} <: (Array{Int, N} where N)
true

julia> 1 <: Any
ERROR: TypeError: issubtype: expected Type, got Int64
2 Likes

What’s the syntax to constrain N to be of type Int for example?

AFAICT there is no such syntax. You can verify constraints like that in inner constructors.

1 Like

They are equivalent.

foo(bar::Baz{T}) where T

defaults to T<:Any.
I believe either style is fine. What is discouraged (and wrong in my opinion) is to leave it Any, when in reality it should be something else, (e.g., Number, Real, etc.)

@xiaodai, do you refer to

function magic(obj::AbstractVector{T}) where T <: Integer
    total = zero(T)
    for each ∈ obj
        total += each
    end
    return total
end

This allows methods for all subtypes of Integer.
If you want only to establish methods for Int64 use

function magic(obj::AbstractVector{T}) where T<:Int64
    total = zero(T)
    for each ∈ obj
        total += each
    end
    return total
end

However, since it is only defined for Int64 I could define it equivalently as

function magic(obj::AbstractVector{<:Int64})
    total = zero(Int64)
    for each ∈ obj
        total += each
    end
    return total
end

Why would one do this? Int64 is a concrete type. Same applies to <:Int64.

I think he’s referring to restricting N to be a value of type Int, not a type that is a subtype of Integer.

As in Array{Int, 2}, where the first type parameter is a subtype of Integer, but the second parameter is the value 2.

This should just use AbstractVector{Int64}.

Aye, the cases are just to demonstrate the general syntax. If one needs to implement a method for a concrete type, generally one should dispatch on abstract types and then specialize on concrete if one needs to or for multiple dispatch. For concrete types, one doesn’t need to use the T where syntax as there is no ambiguity and one can hardcode the necessary information for type-stability. For example,

function magic(obj::AbstractVector{Int64})
    total = zero(Int64)
    for each ∈ obj
        total += each
    end
    return total
end