JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.77k stars 5.49k forks source link

constraints on non-type as type parameters #9580

Open yuyichao opened 9 years ago

yuyichao commented 9 years ago

When a type parameter is specified to be T <: Top, it actually accept non-type as parameters as well.

julia> type A{T<:Top}
       end

julia> A{1}
A{1}

julia> A{1.2}
A{1.2}

Although 1 and 1.2 are clearly not subtypes of Top

julia> 1 <: Top
ERROR: type: subtype: expected Type{T<:Top}, got Int64

julia> 1.2 <: Top
ERROR: type: subtype: expected Type{T<:Top}, got Float64

This is also the case for the builtin Type (some other types like Array doesn't even have this constraint specified)

julia> Type
Type{T<:Top}

julia> Type{1}
Type{1}

julia> Type{1.2}
Type{1.2}

No error is raised even if this parameter is used as a type of a field

julia> type A{T<:Top}
       a::T
       end

julia> A{1}
A{1}

julia> A{1}.names
(:a,)

julia> A{1}.types
(1,)

The same thing happens for T <: Any although this time it correctly reject Core.Unref

julia> type A{T <: Any}
       end

julia> A{1}
A{1}

julia> A{1.2}
A{1.2}

julia> A{Core.Undef}
ERROR: type: A: in T, expected T, got Type{Undef}

julia> Core.Undef <: Any
false

Specifying other types seems fine

julia> type C{T <: Integer}
       end

julia> C{1}
ERROR: type: C: in T, expected T<:Integer, got Int64

julia> C{Int}
C{Int64}

Another related issue is that Vararg doesn't specify any constraint on the parameter and therefore the following syntax is allowed.

julia> (1...)[1]
1...

julia> typeof((1...)[1])
DataType
uniment commented 1 year ago

That was quite a debate. I hope we can agree that 1 <: Any throwing an error makes sense, and Foo{1} <: Foo{<:Any} evaluating to true is nonsense—an artifact of an implementation detail instead of a design objective.

I'd like to throw out some possibilities to test the ergonomics of what could be:

Foo{T} where T         # T can be any type or any isbits value
Foo{:}                 # synonym for `Foo{T} where T`
Foo{T} where T<:Any    # T must be a type
Foo{<:Any}             # synonym for `Foo{T} where T<:Any`

degenerate cases:

T where T              # synonym for `Any`
T where T<:Any         # synonym for `Any`
Tuple{1} <: Tuple{Any} # throw TypeError? or return false?

Introducing type assertion :: allows for this:

Foo{n} where n::Int    # n must be a value of type `Int`
Foo{::Int}             # synonym for `Foo{n} where n::Int`

but also allows these redundancies:

Foo{::Type}            # synonym for `Foo{<:Any}`
Foo{::Type{Int}}       # synonym for `Foo{Int}`
Foo{::Type{<:Number}}  # synonym for `Foo{<:Number}`

It's attractive, but I'm not sure how to feel since <: returns a Boolean while :: does not; I could also argue for isa here.

uniment commented 1 year ago

Why was this closed?

frankwswang commented 1 year ago

Will this issue be reconsidered/reopened anytime soon in the future?