infer quantified type variables in the order that they occur (L-to-R)

- backwards incompatible change: quantified type variables no longer sorted lex
- fix and add tests, remove some annotations in tests
- small speed improvement due to eliminating some extra expansions
- closes #9, closes #11
This commit is contained in:
Stephen Chang 2016-04-19 15:10:07 -04:00
parent 29be57dffe
commit c64251cac0
6 changed files with 123 additions and 72 deletions

View File

@ -114,21 +114,23 @@
(define (inst-types tys-solved Xs tys)
(stx-map (lambda (t) (inst-type tys-solved Xs t)) tys))
;; computes unbound ids in tys, to be used as tyvars
;; compute unbound tyvars in one unexpanded type ty
(define (compute-tyvar1 ty)
(syntax-parse ty
[X:id #'(X)]
[() #'()]
[(C t ...) (stx-appendmap compute-tyvar1 #'(t ...))]))
;; computes unbound ids in (unexpanded) tys, to be used as tyvars
(define (compute-tyvars tys)
(if (stx-null? tys)
#'()
(let L ([Xs #'()]) ; compute unbound ids; treat as tyvars
(define ctx (stx-car tys))
(with-handlers
([exn:fail:syntax:unbound?
(λ (e)
(define X (stx-car (exn:fail:syntax-exprs e)))
;; X is tainted, so need to launder it
(define Y (datum->syntax ctx (syntax->datum X)))
(L (cons Y Xs)))])
((current-type-eval) #`( #,Xs (ext-stlc:→ . #,tys)))
(stx-sort Xs))))))
(define Xs (stx-appendmap compute-tyvar1 tys))
(filter
(lambda (X)
(with-handlers
([exn:fail:syntax:unbound? (lambda (e) #t)]
[exn:fail:type:infer? (lambda (e) #t)])
(let ([X+ ((current-type-eval) X)])
(not (or (tyvar? X+) (type? X+))))))
(stx-remove-dups Xs))))
;; define --------------------------------------------------
;; for function defs, define infers type variables
@ -267,16 +269,20 @@
[_:id
#:when (and (not (stx-null? #'(X ...)))
(not (stx-null? #'(τ ...))))
(type-error
#:src stx
#:msg
(string-append
(format "constructor ~a must instantiate ~a type argument(s): "
'Cons (stx-length #'(X ...)))
(string-join (stx-map type->str #'(X ...)) ", ")
"\n"
(format "and be applied to ~a arguments with type(s): "(stx-length #'(τ ...)))
(string-join (stx-map type->str #'(τ ...)) ", ")))]
(raise
(exn:fail:type:infer
(string-append
(format "TYPE-ERROR: ~a (~a:~a):"
(syntax-source stx) (syntax-line stx) (syntax-column stx))
"\n"
(format "Constructor ~a must be applied to ~a argument(s) with type(s): "
'Cons (stx-length #'(τ ...)))
(string-join (stx-map type->str #'(τ ...)) ", ")
"\n"
(format "The arguments should instantiate ~a type argument(s): "
(stx-length #'(X ...)))
(string-join (stx-map type->str #'(X ...)) ", "))
(current-continuation-marks)))]
[(C τs e_arg ...)
#:when (brace? #'τs) ; commit to this clause
#:with {~! τ_X:type (... ...)} #'τs
@ -905,7 +911,7 @@
( (for*/list ([x- e-] ...) body-) : (List ty_body))])
(define-typed-syntax for/fold
[(_ ([acc init]) ([x:id e] ...) body)
#:with [init- ty_init] (infer+erase #'init)
#:with [init- ty_init] (infer+erase #`(pass-expected init #,stx))
#:with ([e- (ty)] ...) (⇑s (e ...) as Sequence)
#:with [(acc- x- ...) body- ty_body]
(infer/ctx+erase #'([acc : ty_init][x : ty] ...) #'body)

View File

@ -57,6 +57,9 @@
(define (stx-appendmap f stx)
(stx-flatten (stx-map f stx)))
(define (stx-remove-dups Xs)
(remove-duplicates (stx->list Xs) free-identifier=?))
(define (stx-drop stx n)
(drop (stx->list stx) n))

View File

@ -52,7 +52,7 @@
(check-type (g2 (Nil {Bool})) : (List Bool) (Nil {Bool}))
(check-type (g2 (Nil {(List Int)})) : (List (List Int)) (Nil {(List Int)}))
(check-type (g2 (Nil {( Int Int)})) : (List ( Int Int)) (Nil {(List ( Int Int))}))
;; same as tests above, but without annotations
;; annotations unneeded: same as tests above, but without annotations
(check-type (g2 Nil) : (List Int) Nil)
(check-type (g2 Nil) : (List Bool) Nil)
(check-type (g2 Nil) : (List (List Int)) Nil)
@ -278,6 +278,39 @@
(define-type (Pairof A B) (C A B))
(check-type (match (C 1 2) with [C a b -> None]) : (Option Int) -> None)
;; type variable inference
; F should remain valid tyvar, even though it's bound
(define (F [x : X] -> X) x)
(define (tvf1 [x : F] -> F) x)
(check-type tvf1 : (→/test X X))
; G should remain valid tyvar
(define-type (Type1 X) (G X))
(define (tvf5 [x : G] -> G) x)
(check-type tvf5 : (→/test X X))
; TY should not be tyvar, bc it's a valid type
(define-type-alias TY (Pairof Int Int))
(define (tvf2 [x : TY] -> TY) x)
(check-not-type tvf2 : (→/test X X))
; same with Bool
(define (tvf3 [x : Bool] -> Bool) x)
(check-not-type tvf3 : (→/test X X))
;; X in lam should not be a new tyvar
(define (tvf4 [x : X] -> ( X X))
(λ ([y : X]) x))
(check-type tvf4 : (→/test X ( X X)))
(check-not-type tvf4 : (→/test X ( Y X)))
(check-type (λ ([x : X]) (λ ([y : X]) y)) : (→/test X ( X X)))
(check-not-type (λ ([x : X]) (λ ([y : X]) y)) : (→/test {X} X (→/test {Y} Y Y)))
(check-type (λ ([x : X]) (λ ([y : Y]) y)) : (→/test {X} X (→/test {Y} Y Y)))
(check-not-type (λ ([x : X]) (λ ([y : Y]) x)) : (→/test X ( X X)))
; ext-stlc tests --------------------------------------------------
; tests for stlc extensions

View File

@ -79,7 +79,7 @@
[Leaf x -> (list x)]
[Node x rst ->
(cons x
(for/fold ([acc (nil {X})]) ([p (in-list (flatten rst))])
(for/fold ([acc nil]) ([p (in-list (flatten rst))])
(match p with
[x y -> (cons x (cons y acc))])))]))

View File

@ -14,13 +14,15 @@
(provide ok)
(provide error)
(check-type ok : (→/test A (Result A B)))
(check-type error : (→/test B (Result A B)))
(check-type (inst ok Int String) : (→ Int (Result Int String)))
(check-type (inst error Int String) : (→ String (Result Int String)))
(check-type (inst error String Int) : (→ String (Result Int String)))
(check-type
(list (Ok {Int String} 3) (Error "abject failure") (Ok 4))
(list (Ok 3) (Error "abject failure") (Ok 4))
: (List (Result Int String))
-> (list (Ok {Int String} 3) (Error "abject failure") (Ok 4)))
-> (list (Ok 3) (Error "abject failure") (Ok 4)))
(define (result-bind [a : (Result A Er)] [f : (→ A (Result B Er))]
→ (Result B Er))
@ -74,59 +76,58 @@
(define (read-tree [str : (List Char)] → (Read-Result (Tree Int)))
(cond
[(isnil str)
(error "expected a tree of integers, given nothing")]
[(equal? (head str) #\( )
(do result-bind
[tree1+str : (× (Tree Int) (List Char))
<- (read-tree (tail str))]
[(cond [(equal? (head (proj tree1+str 1)) #\space)
((inst ok Unit String) (void))]
[else
((inst error Unit String) "expected a space")])]
[int+str : (× Int (List Char))
<- (read-int (tail (proj tree1+str 1)) nil)]
[(cond [(equal? (head (proj int+str 1)) #\space)
((inst ok Unit String) (void))]
[else
((inst error Unit String) "expected a space")])]
[tree2+str : (× (Tree Int) (List Char))
<- (read-tree (tail (proj int+str 1)))]
[(cond [(equal? (head (proj tree2+str 1)) #\) )
((inst ok Unit String) (void))]
[else
((inst error Unit String) "expected a `)`")])]
((inst ok (× (Tree Int) (List Char)) String)
(tup (Node (proj tree1+str 0)
(proj int+str 0)
(proj tree2+str 0))
(tail (proj tree2+str 1)))))]
[(digit? (head str))
(do result-bind
[int+str : (× Int (List Char))
<- (read-int str nil)]
((inst ok (× (Tree Int) (List Char)) String)
(tup (Leaf (proj int+str 0))
(proj int+str 1))))]
[else
(error "expected either a `(` or a digit")]))
[(isnil str)
(error "expected a tree of integers, given nothing")]
[(equal? (head str) #\( )
(let ([do-ok (inst ok Unit String)]
[do-error (inst error String Unit)])
(do result-bind
[tree1+str : (× (Tree Int) (List Char))
<- (read-tree (tail str))]
[(cond [(equal? (head (proj tree1+str 1)) #\space)
(do-ok (void))]
[else (do-error "expected a space")])]
[int+str : (× Int (List Char))
<- (read-int (tail (proj tree1+str 1)) nil)]
[(cond [(equal? (head (proj int+str 1)) #\space)
(do-ok (void))]
[else (do-error "expected a space")])]
[tree2+str : (× (Tree Int) (List Char))
<- (read-tree (tail (proj int+str 1)))]
[(cond [(equal? (head (proj tree2+str 1)) #\) )
(do-ok (void))]
[else (do-error "expected a `)`")])]
(ok
(tup (Node (proj tree1+str 0)
(proj int+str 0)
(proj tree2+str 0))
(tail (proj tree2+str 1))))))]
[(digit? (head str))
(do result-bind
[int+str : (× Int (List Char))
<- (read-int str nil)]
(ok
(tup (Leaf (proj int+str 0))
(proj int+str 1))))]
[else
(error "expected either a `(` or a digit")]))
(check-type
(read-tree (string->list "42"))
: (Read-Result (Tree Int))
-> ((inst ok (× (Tree Int) (List Char)) String)
-> (ok
(tup (Leaf 42) nil)))
(check-type
(read-tree (string->list "x"))
: (Read-Result (Tree Int))
-> ((inst error (× (Tree Int) (List Char)) String)
-> (error
"expected either a `(` or a digit"))
(check-type
(read-tree (string->list "(42 43 (44 45 46))"))
: (Read-Result (Tree Int))
-> ((inst ok (× (Tree Int) (List Char)) String)
-> (ok
(tup (Node (Leaf 42) 43 (Node (Leaf 44) 45 (Leaf 46))) nil)))

View File

@ -135,6 +135,10 @@
(define-syntax add-expected
(syntax-parser
[(_ e τ) (syntax-property #'e 'expected-type #'τ)]))
(define-syntax pass-expected
(syntax-parser
[(_ e stx) (syntax-property #'e 'expected-type
(syntax-property #'stx 'expected-type))]))
(define-for-syntax (add-expected-ty e ty)
(or (and (syntax-e ty)
(syntax-property e 'expected-type ((current-type-eval) ty)))
@ -170,6 +174,8 @@
(define ty (syntax-property stx tag))
(if (cons? ty) (car ty) ty))
(define (tyvar? X) (syntax-property X 'tyvar))
(define type-pat "[A-Za-z]+")
;; - infers type of e
@ -284,9 +290,11 @@
(expand/df
#`(λ (tv ...)
(let-syntax ([tv (make-rename-transformer
(assign-type
(assign-type #'tv #'k)
#'ok #:tag '#,tag))] ...)
(syntax-property
(assign-type
(assign-type #'tv #'k)
#'ok #:tag '#,tag)
'tyvar #t))] ...)
(λ (x ...)
(let-syntax
([x