start Bens "trivial" example

This commit is contained in:
Stephen Chang 2016-10-18 17:08:25 -04:00
parent 28fa5dd033
commit ba1c954b96
4 changed files with 554 additions and 3 deletions

View File

@ -4,6 +4,12 @@
(define (stx-cadr stx) (stx-car (stx-cdr stx)))
(define (stx-caddr stx) (stx-cadr (stx-cdr stx)))
(define (stx-cddr stx) (stx-cdr (stx-cdr stx)))
(define (stx->datum stx)
(if (syntax? stx)
(syntax->datum stx)
(map syntax->datum stx)))
(define (stx-rev stx)
(reverse (stx->list stx)))
@ -57,8 +63,10 @@
(define (stx-foldr f base . lsts)
(apply foldr f base (map stx->list lsts)))
(define (stx-append stx1 stx2)
(append (stx->list stx1) (stx->list stx2)))
(define (stx-apply f stx)
(apply f (stx->list stx)))
(define (stx-append . stxs)
(apply append (stx-map stx->list stxs)))
(define (stx-appendmap f . stxs)
(stx-flatten (apply stx-map f stxs)))

View File

@ -492,7 +492,7 @@
(define τ (get-orig ty))
(cond
[(identifier? τ) (symbol->string (syntax->datum τ))]
[(stx-pair? τ) (string-join (stx-map type->str τ)
[(stx-list? τ) (string-join (stx-map type->str τ)
#:before-first "("
#:after-last ")")]
[else (format "~s" (syntax->datum τ))]))

View File

@ -0,0 +1,129 @@
#lang typed/racket
(require "../trivial.rkt"
"rackunit-typechecking.rkt"
typed/rackunit)
; testing #%datum (check no loop)
(check-equal? 9 9)
(check-type 9 : (Int 9))
(check-type "9" : Any)
(define vec10 (build-vector 10 (lambda (x) x)))
;; testing make-vector and vector-ref
(check-equal? (make-vector 10) (make-vector 10))
(check-type (make-vector 10) : (Vec 10))
(check-equal? (vector-ref vec10 9) 9)
(typecheck-fail (vector-ref (make-vector 10) 10)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(typecheck-fail (vector-ref vec10 10)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(typecheck-fail (vector-ref vec10 -1)
#:with-msg "index is out of range, given -1, valid range: \\[0, 9\\]")
;; λ
;; TODO: resolve clash between TR and my annotations
;; for now, only allow TR annotations
(check-equal? (procedure? (tr:λ (x) x)) #t)
(check-equal? (procedure? (tr:λ ([x : tr:Any]) x)) #t)
(check-equal? (procedure? (λ (x) x)) #t)
(check-equal? (procedure? (λ ([v : VectorTop]) v)) #t)
(define vec-ref9
(λ ([v : VectorTop]) (vector-ref v 9)))
(define vec-ref10
(λ ([v : VectorTop]) (vector-ref v 10)))
(check-equal? (procedure? vec-ref10) #t)
(check-equal? (vec-ref9 vec10) 9)
(typecheck-fail
(vec-ref10 vec10)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(check-equal?
(vec-ref9 (make-vector 10))
0)
(typecheck-fail
((λ ([v : VectorTop]) (vector-ref v -1))
(make-vector 10))
#:with-msg "index is out of range, given -1, valid range: \\[0,.*")
(check-equal? (vec-ref9 vec10) 9)
(define (vec-ref8 [v : VectorTop])
(vector-ref v 8))
(typecheck-fail
(vec-ref8 (make-vector 8))
#:with-msg "index is out of range, given 8, valid range: \\[0, 7\\]")
(define (my-vec-ref [v : VectorTop] [n : Integer])
(vector-ref v n))
(typecheck-fail
(my-vec-ref vec10 10)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(typecheck-fail
(my-vec-ref vec10 -1)
#:with-msg "index is out of range, given -1, valid range: \\[0, 9\\]")
(check-equal?
(my-vec-ref vec10 9)
9)
;; add1
(check-equal? (add1 1) 2)
(check-type (add1 1) : (Int 2))
(check-equal? (sub1 2) 1)
(check-type (sub1 2) : (Int 1))
(define (vec-ref-1 [v : VectorTop] [n : Integer])
(vector-ref v (sub1 n)))
(check-equal? (vec-ref-1 (make-vector 10) 10) 0)
(typecheck-fail
(vec-ref-1 vec10 11)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(typecheck-fail
(vec-ref-1 vec10 (add1 10))
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(define (vec-ref+4 [v : VectorTop] [n : Integer])
(vector-ref v (+ n 4)))
(typecheck-fail
(vec-ref+4 vec10 6)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(check-equal? (vec-ref+4 vec10 5) 9)
(define (vec-ref-4 [v : VectorTop] [n : Integer])
(vector-ref v (- n 4)))
(typecheck-fail
(vec-ref-4 vec10 14)
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(check-equal? (vec-ref-4 vec10 13) 9)
(typecheck-fail
(vec-ref-4 vec10 (+ (- 14 4) 4))
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(typecheck-fail
(let ([x 10])
(vec-ref-4 vec10 (+ x 4)))
#:with-msg "index is out of range, given 10, valid range: \\[0, 9\\]")
(check-equal?
(let ([x 10])
(let ([y (- x 1)])
(vec-ref-4 vec10 (+ y 4))))
9)

View File

@ -0,0 +1,414 @@
#lang turnstile
(require (prefix-in tr: typed/racket))
;; TODO:
;; ) do I need separate → and CCs types, both with constraints?
;; - yes?
;; - → is for introducing constraints
;; - and CCs is for propagating them
;;
;; ) why doesnt eval-syntax work with quote?
;; NOTES:
;; - using inference algorithm similar to turnstile/examples/infer
;; - tyvars flow down
;; - types, constraints, and tyvar mappings flow up
;; - currently, type variables must be numbers
(provide Any Int Vec
(rename-out [datum/tc #%datum]
[app/tc #%app])
λ tr:λ tr:Any define begin let
(typed-out
[make-vector : ( (N) [] (Int N) (Vec N))]
[vector-ref
: ( (M N)
[[(<= 0 N)
(< N M)
"index is out of range, given ~a, valid range: [0, ~a]"
((lambda (x) x) N) (sub1 M)]]
(Vec M) (Int N) Any)]
[build-vector
: ( (P)
[]
(Int P) Any (Vec P))]
;; using different tyvars, to make debugging easier to read
[add1
: ( (A)
[]
(Int A) (Int (+ A 1)))]
[sub1
: ( (B)
[]
(Int B) (Int (- B 1)))]
[+
: ( (C D)
[]
(Int C) (Int D) (Int (+ C D)))]
[-
: ( (E F)
[]
(Int E) (Int F) (Int (- E F)))])
procedure?)
(define-base-type Any) ; different from tr:Any
(define-internal-type-constructor Vec)
(define-syntax Vec
(syntax-parser
[(_ n) ; n = (quote _) = length of vec; can be numeric expr
(mk-type #'(Vec- n))]))
(define-internal-type-constructor Int)
(define-syntax Int
(syntax-parser
[(_ n) ; n = (quote _) = length of vec; can be numeric expr
(mk-type #'(Int- n))]))
(define-internal-binding-type )
(define-syntax ( stx)
(syntax-parse stx
[(_ (X:id ...) ; tyvars
[C ...] ; constraints
ty_in ... ty_out)
#'(→- (X ...) [void C ...] ty_in ... ty_out)]))
;; TODO: pattern expander for MaybeCCs
(define-internal-binding-type CCs)
(define-syntax (CCs stx)
(syntax-parse stx
[(_ (X:id ...) ; tyvars
[C ...] ; constraints
[B ...] ; leftover binding constraints
ty)
#'(CCs- (X ...) [void C ...] [void B ...] ty)]))
;; redefine some parameters ---------------------------------------------------
(begin-for-syntax
;; eval for constraints
(define (eval-C stx)
; (displayln "evaling:")
; (displayln stx)
; (pretty-print (stx->datum stx))
;; TODO: eval-syntax does not work due to quoting err, e.g.:
; quote: unbound identifier in the transformer environment;
; also, no #%app syntax transformer is bound
;; - using eval for now
(parameterize ([current-namespace (make-base-namespace)])
(define res
(with-handlers ([exn?
(lambda (e) #;(displayln e) stx)])
(eval (syntax->datum stx))))
; (displayln res)
res))
;; current-typecheck-relation
;; go under CCs if necessary
(define old-type=? (current-type=?))
(define (new-type=? t1 t2) ; extend to literals
;; (printf "t1: ~a\n" (syntax->datum t1))
;; (printf "t2: ~a\n" (syntax->datum t2))
(or (Any? t2)
(old-type=? t1 t2)
(equal? (syntax-e t1) (syntax-e t2))
;; unwrap CCs if necessary
(syntax-parse (list t1 t2)
[((~CCs _ _ _ t1*) _)
((current-type=?) #'t1* t2)]
[(_ (~CCs _ _ _ t2*))
((current-type=?) t1 #'t2*)]
[_ #f])))
(current-type=? new-type=?)
(current-typecheck-relation (current-type=?))
;; current-type?
;; TODO: disabling type validation for now
(current-type? (lambda _ #t))
;; current-type-eval
;; reduce numberic expressions when possible
(define old-teval (current-type-eval))
(define (new-teval t)
(define t+ (old-teval t))
(syntax-parse t+
[(~Int (_ e:integer)) t+] ; already reduced
[(~Int e)
#:with e- (eval-C #'e)
(if (typecheck? #'e #'e-) ; couldnt reduce
t+
((current-type-eval) #'(Int (#%datum- . e-))))]
[(~CCs a b c (~Int (_ e:integer))) t+] ; already reduced
[(~CCs a b c (~Int e))
#:with e- (eval-C #'e)
(if (typecheck? #'e #'e-) ; couldnt reduce
t+
((current-type-eval) #'(CCs- a b c (Int (#%datum- . e-)))))]
[_ t+]))
(current-type-eval new-teval)
;; type inference helpers ---------------------------------------------------
;; A "B" is a type binding, eg (X ty) or (ty X)
;; returns a stx list of Bs
;; TODO: add fall through case
(define (unify t expected-t)
; (printf "unifying:\n~a\nand\n~a\n"
; (syntax->datum t)
; (syntax->datum expected-t))
(syntax-parse (list t expected-t)
[(_ ~Any) #f]
[((~CCs _ _ _ t1) _)
(unify #'t1 expected-t)]
[(_ (~CCs _ _ _ t2))
(unify t #'t2)]
[((~Int n) (~Int m))
(unify #'n #'m)]
[((~Vec n) (~Vec m))
(unify #'n #'m)]
[(t X:id) #'(X t)]
[(X:id t) #'(X t)]))
(define (unifys ts expected-ts)
(filter
(lambda (x) x) ; filter out #f
(stx-map unify ts expected-ts)))
;; Bs should be listof either (Y ty) or (ty Y)
;; returns ty (when X = Y), or #f
(define (lookup1 X Bs)
(let L ([Bs (stx->list Bs)])
(if (null? Bs)
#f
(syntax-parse (car Bs)
[(Y:id t)
#:when (free-identifier=? #'Y X)
#'t]
[(_ Y:id t) ; expanded version
#:when (free-identifier=? #'Y X)
#'t]
[_ (L (cdr Bs))]))))
;; returns (as list):
;; - list of unsolved Xs
;; - list of solved Xs
;; - tys for solved Xs
(define (lookup Xs Bs)
(define (lookupX X) (lookup1 X Bs))
(let L ([Xs (stx->list Xs)]
[unsolved null]
[solved null]
[tys-solved null])
(if (null? Xs)
(list (reverse unsolved)
(reverse solved)
(reverse tys-solved))
(let ([res (lookupX (car Xs))])
(if res
(L (cdr Xs)
unsolved
(cons (car Xs) solved)
(cons res tys-solved))
(L (cdr Xs)
(cons (car Xs) unsolved)
solved tys-solved))))))
(define (append-Bs Bss)
; (printf "appending\n")
; (pretty-print (syntax->datum Bss))
(define appended
(apply stx-append
(stx-map
(syntax-parser
[((~literal #%plain-app) (~literal void) . rst)
#'rst]
[rst #'rst])
Bss)))
; (printf "appended\n")
; (pretty-print (stx->datum appened))
appended)
;; prune out solved entries
(define (prune-Bs Bs)
(filter ; drop #fs
(lambda (x) x)
(stx-map
(syntax-parser
[(t1 t2) #:when (typecheck? #'t1 #'t2) #f]
[b #'b])
Bs)))
;; A "C" is a stx obj that evals to a boolean
;; - function types may additionally specify a list of Cs
;; and they are checked when the fn is applied
;; Constraint expr-stx -> Constraint or #t or type error
(define (check-C C src-e)
(syntax-parse C
[(_ e ... (_ msg:str) msg-e ...)
(or (and (eval-C #'(and e ...))
C)
(type-error
#:src src-e
#:msg (apply format (syntax-e #'msg)
(map
;; this is here bc, due to the "and",
;; constraint may fail before all the tyvars
;; are inferred
;; TODO: improve err msg in this case
(lambda (s)
(if (syntax? s)
(syntax->datum s)
s))
(stx-map eval-C #'(msg-e ...))))))]
[_ C]))
(define (check-Cs Cs e)
(filter
syntax?
(stx-map
(lambda (c) (check-C c e))
(stx-cddr Cs)))) ; cddr drops app, void
(define (append-Cs Css)
(apply
append
(list #'#%app #'void)
(stx-map
(syntax-parser
[(_ _ . rst) ; drop app void
#'rst])
Css)))
)
;; λ --------------------------------------------------------------------
;; no annotations allowed
;; type of lambda inferred from lambda body (as much as possible)
(define-typed-syntax λ #:datum-literals (:)
[(_ ((~and (~or (~and x:id (~parse ty #'tr:Any))
[x:id : ty])) ...)
. es)
#:with (X ...) (generate-temporaries #'(x ...))
[([X X- : #%type] ...) ([x x- : X] ...)
(begin . es) e- τ_out]
;; TODO: investigate why this extra syntax-local-introduce is needed?
#:with τ_out* (syntax-local-introduce #'τ_out)
#:with (~or (~CCs Ys Cs Bs τ_out**)
(~and τ_out**
(~parse (Ys Cs Bs)
#'(() (#%app void) (#%app void)))))
#'τ_out*
; #:when (begin (displayln "Bs:")
; (stx-map
; (compose pretty-print syntax->datum)
; (stx-cddr #'Bs)))
#:with (unsolved solved solved-tys)
(lookup #'(X- ...) (stx-cddr #'Bs))
; #:when (begin (printf "unsolved: ~a\n" (syntax->datum #'unsolved)))
; #:when (begin (printf "solved: ~a\n" (syntax->datum #'solved)))
#:with (ty-arg ...)
(stx-map
(lambda (x)
(if (stx-member x #'unsolved)
x
(cdr
(stx-assoc x (stx-map cons #'solved #'solved-tys)))))
#'(X- ...))
; #:when (begin
; (printf "lambda type:\n")
; (pretty-print
; (syntax->datum #'(→- Ys Cs ty-arg ... τ_out**))))
-------
[ (tr:λ ([x- tr:: ty] ...) e-)
(→- Ys Cs ty-arg ... τ_out**)]])
;; #%app --------------------------------------------------------------------
(define-typed-syntax app/tc
[(_ e_fn e_arg ...)
; #:when (begin (displayln "applying: ----------------------")
; (displayln (syntax->datum #'e_fn)))
;; TODO: propagate constraints from the function expression
[ e_fn e_fn- (~→ Xs Cs τ_in ... τ_out)]
; #:when (begin (displayln "fn type:")
; (pretty-print (syntax->datum #'(→ Xs Cs τ_in ... τ_out))))
;; TODO: create pattern expander for maybe-ccs
[ e_arg e_arg-
(~or (~CCs Xs-arg Cs-arg Bs-arg τ_arg)
(~and τ_arg
(~parse (Xs-arg Cs-arg Bs-arg)
#'(() (#%app void) ()))))]
...
; #:when (begin (displayln "ty-args:")
; (pretty-print (syntax->datum #'(τ_arg ...))))
#:with Bs (unifys #'(τ_arg ...) #'(τ_in ...))
#:with (unsolved solved tys-solve) (lookup #'Xs #'Bs)
; #:when (begin
; (displayln "solved:")
; (displayln (syntax->datum #'solved))
; (displayln (syntax->datum #'tys-solve)))
#:with (Cs* Bs* ty-in* ... ty-out*)
(substs #'tys-solve #'solved #'(Cs Bs τ_in ... τ_out))
#:with Bs** (prune-Bs #'Bs*)
; #:when (begin (displayln "checking Cs:")
; (pretty-print (syntax->datum #'Cs*)))
#:with remaining-Cs (check-Cs #'Cs* stx)
; #:when (printf "remaining Cs: ~a\n"
; (syntax->datum #'remaining-Cs))
#:with ty-out**
#`(CCs #,(stx-apply stx-append #'(unsolved Xs-arg ...))
remaining-Cs
#,(append-Bs #'(Bs** Bs-arg ...))
ty-out*)
; #:when (begin
; (displayln "app out type:")
; (pretty-print (syntax->datum #'ty-out**))
; (displayln "end apply ---------------------------"))
--------
[ (tr:#%app e_fn- e_arg- ...) ty-out**]]
[(_ e ...)
--------
[ (tr:#%app e ...) Any]])
;; #%datum --------------------------------------------------------------------
(define-typed-syntax datum/tc
[(_ . n:integer)
-------
[ (tr:#%datum . n) (Int (#%datum- . n))]]
[(_ . x)
-------
[ (tr:#%datum . x) Any]])
(define-typed-syntax procedure?
[(_ e)
[ e e- _]
-------
[ (tr:procedure? e-) Any]])
;; TODO: resolve clash between TR and my annotations
;; currently, no annotations allowed
(define-typed-syntax define
[(_ x:id e)
[ e e- ty]
#:with x* (generate-temporary)
----------
[ (begin-
(tr:define-syntax x
(make-rename-transformer ( x* : ty)))
(tr:define x* e-))]]
[(_ (f b ...) . es)
[ (λ (b ...) (begin . es)) fn- ty-f]
#:with g (generate-temporary #'f)
--------
[ (begin-
(tr:define-syntax f
(make-rename-transformer ( g : ty-f)))
(tr:define g fn-))]])
(define-typed-syntax begin
[(_ e_unit ... e)
[ e_unit e_unit- _] ...
[ e e- τ_e]
--------
[ (tr:begin e_unit- ... e-) τ_e]])
(define-typed-syntax let
[(_ ([x e] ...) . es)
[ e e- τ_x] ...
[[x x- : τ_x] ... (begin . es) e_body- τ_body]
--------
[ (tr:let ([x- e-] ...) e_body-) τ_body]]
[(_ . rst)
-------
[ (tr:let . rst) X]])