Skip to content

Commit

Permalink
add implicit contravariant parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
stevengj committed Feb 2, 2017
1 parent 6b7249f commit f538103
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 7 deletions.
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ New language features
Anonymous functions can have type parameters via the syntax
`((x::Array{T}) where T<:Real) -> 2x`.
* Implicit type parameters, e.g. `Vector{<:Real}` is equivalent to
`Vector{T} where T<:Real` ([#20414]).
`Vector{T} where T<:Real`, and similarly for `Vector{>:Int}` ([#20414]).
* Much more accurate subtype and type intersection algorithms. Method sorting and
identification of equivalent and ambiguous methods are improved as a result.

Expand Down
17 changes: 16 additions & 1 deletion doc/src/manual/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,17 @@ julia> Pointy{Real} <: Pointy{Float64}
false
```

The notation `Pointy{<:Real}` can be used to express the Julia analogue of a
*covariant* type, while `Pointy{>:Int}` the analogue of a *contravariant* type,
but technically these represent *sets* of types (see [UnionAll Types](@ref)).
```jldoctest pointytype
julia> Pointy{Float64} <: Pointy{<:Real}
true
julia> Pointy{Real} <: Pointy{>:Int}
true
```

Much as plain old abstract types serve to create a useful hierarchy of types over concrete types,
parametric abstract types serve the same purpose with respect to parametric composite types. We
could, for example, have declared `Point{T}` to be a subtype of `Pointy{T}` as follows:
Expand Down Expand Up @@ -743,6 +754,9 @@ This relationship is also invariant:
```jldoctest pointytype
julia> Point{Float64} <: Pointy{Real}
false
julia> Point{Float64} <: Pointy{<:Real}
true
```

What purpose do parametric abstract types like `Pointy` serve? Consider if we create a point-like
Expand Down Expand Up @@ -997,7 +1011,8 @@ The syntax `Array{<:Integer}` is a convenient shorthand for `Array{T} where T<:I
Type variables can have both lower and upper bounds.
`Array{T} where Int<:T<:Number` refers to all arrays of `Number`s that are able to contain `Int`s
(since `T` must be at least as big as `Int`).
The syntax `where T>:Int` also works to specify only the lower bound of a type variable.
The syntax `where T>:Int` also works to specify only the lower bound of a type variable,
and `Array{>:Int}` is equivalent to `Array{T} where T>:Int`.

Since `where` expressions nest, type variable bounds can refer to outer type variables.
For example `Tuple{T,Array{S}} where S<:AbstractArray{T} where T<:Real` refers to 2-tuples whose first
Expand Down
8 changes: 4 additions & 4 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1764,16 +1764,16 @@
(expand-where (expand-wheres body (cdr vars)) (car vars))))

; given e = (curly T params...), return (newparams . whereparams) where any <:X expression
; in params is converted to T and T<:X is added to whereparams. (This implements
; the syntactic sugar Foo{<:Bar} --> Foo{T} where T<:Bar.)
; in params is converted to T and T<:X is added to whereparams; similarly for >:X.
; (This implements the syntactic sugar Foo{<:Bar} --> Foo{T} where T<:Bar.)
(define (extract-implicit-whereparams e)
(define (extract params newparams whereparams)
(if (null? params)
(cons (reverse newparams) (reverse whereparams))
(let ((p (car params)))
(if (and (list? p) (= (length p) 3) (eq? (car p) 'call) (eq? (cadr p) '|<:|))
(if (and (list? p) (= (length p) 3) (eq? (car p) 'call) (or (eq? (cadr p) '|<:|) (eq? (cadr p) '|>:|)))
(let ((T (gensy)))
(extract (cdr params) (cons T newparams) (cons (list '|<:| T (caddr p)) whereparams)))
(extract (cdr params) (cons T newparams) (cons (list (cadr p) T (caddr p)) whereparams)))
(extract (cdr params) (cons p newparams) whereparams)))))
(extract (cddr e) '() '()))

Expand Down
10 changes: 9 additions & 1 deletion test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ f18348{T<:Any}(::Type{T}, x::T) = 2
f12721{T<:Type{Int}}(::T) = true
@test_throws MethodError f12721(Float64)

# implicit type parameters:
# implicit "covariant" type parameters:
type TwoParams{S,T}; x::S; y::T; end
@test TwoParams{<:Real,<:Number} == (TwoParams{S,T} where S<:Real where T<:Number) ==
(TwoParams{S,<:Number} where S<:Real) == (TwoParams{<:Real,T} where T<:Number)
Expand All @@ -879,3 +879,11 @@ ftwoparams(::TwoParams{<:Real,<:Real}) = 3
@test !([TwoParams(3,4)] isa Vector{TwoParams{<:Real,<:Real}})
@test TwoParams{<:Real,<:Real}[TwoParams(3,4)] isa Vector{TwoParams{<:Real,<:Real}}
@test [TwoParams(3,4)] isa (Vector{TwoParams{T,T}} where T<:Real)

# implicit "contravariant" type parameters:
@test TwoParams{>:Int,<:Number} == (TwoParams{S,T} where S>:Int where T<:Number) ==
(TwoParams{S,<:Number} where S>:Int) == (TwoParams{>:Int,T} where T<:Number)
@test TwoParams(3,0im) isa TwoParams{>:Int,<:Number}
@test TwoParams{Real,Complex}(3,0im) isa TwoParams{>:Int,<:Number}
@test !(TwoParams(3.0,0im) isa TwoParams{>:Int,<:Number})
@test !(TwoParams(3,'x') isa TwoParams{>:Int,<:Number})

0 comments on commit f538103

Please sign in to comment.