add top lvl defines and infer instantation

- need to err msg when instantiation fails
This commit is contained in:
Stephen Chang 2016-02-25 17:02:46 -05:00
parent 6fa962f83f
commit 893d457bad
2 changed files with 171 additions and 11 deletions

View File

@ -3,9 +3,9 @@
(extends "ext-stlc.rkt" #:except #%app λ + - void = zero? sub1 add1 not let
#:rename [~→ ~ext-stlc:→])
(require (only-in "sysf.rkt" inst ~∀ Λ))
(reuse inst ~∀ ∀? Λ #:from "sysf.rkt")
(require (only-in "stlc+rec-iso.rkt" case fld unfld μ × var tup define-type-alias)
(prefix-in stlc+rec-iso: (only-in "stlc+rec-iso.rkt" define)))
#;(prefix-in stlc+rec-iso: (only-in "stlc+rec-iso.rkt" define)))
;(reuse cons [head hd] [tail tl] nil [isnil nil?] List ~List list #:from "stlc+cons.rkt")
;(reuse tup × proj #:from "stlc+tup.rkt")
;(reuse define-type-alias #:from "stlc+reco+var.rkt")
@ -27,7 +27,7 @@
; should only be monomorphic?
[((~∀ () (~ext-stlc:→ τ1 ...)) (~∀ () (~ext-stlc:→ τ2 ...)))
(compute-constraints #'((τ1 τ2) ...))]
[_ #:when (displayln τ1-τ2) #'()]))
[_ #:when #t #;(printf "shouldnt get here. could not unify: ~a\n" τ1-τ2) #'()]))
(define (compute-constraints τs)
;(printf "constraints: ~a\n" (syntax->datum τs))
(stx-appendmap compute-constraint τs))
@ -47,6 +47,44 @@
[(_ . rst) (lookup x #'rst)]
[() false])))
(define-typed-syntax define
[(_ x:id e)
#:with (e- τ) (infer+erase #'e)
#:with y (generate-temporary)
#'(begin
(define-syntax x (make-rename-transformer ( y : τ)))
(define y e-))]
; explicit "forall"
#;[(_ (~and Xs {X:id ...}) (f:id [x:id (~datum :) τ] ... (~datum ) τ_out) e)
#:when (brace? #'Xs)
#:with g (generate-temporary #'f)
#:with e_ann #'(add-expected e τ_out)
#'(begin
(define-syntax f (make-rename-transformer ( g : ( (X ...) (ext-stlc:→ τ ... τ_out)))))
(define g (Λ (X ...) (ext-stlc:λ ([x : τ] ...) e_ann))))]
[(_ (f:id [x:id (~datum :) τ] ... (~datum ) τ_out) e)
#:when (displayln #'f)
#:with Ys
(let L ([Xs #'()]) ; compute unbound ids; treat as tyvars
(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 #'f (syntax->datum X)))
(L (cons Y Xs)))])
((current-type-eval) #`( #,Xs (ext-stlc:→ τ ... τ_out)))
Xs))
#:when (displayln #'Ys)
#:with g (generate-temporary #'f)
#:with e_ann #'(add-expected e τ_out)
#`(begin
(define-syntax f
(make-rename-transformer ( g : ( Ys (ext-stlc:→ τ ... τ_out)))))
(define g
(Λ Ys (ext-stlc:λ ([x : τ] ...) e_ann))))])
;
;; internal form used to expand many types at once under the same context
(define-type-constructor Tmp #:arity >= 0 #:bvs >= 0) ; need a >= 0 arity
(define-syntax (define-type stx)
@ -135,7 +173,8 @@
( (StructName e_arg- ...) : τ_out)]
[(C . args) #:when (stx-null? #'(X ...)) #'(C {} . args)] ; no tyvars, no annotations
[(C . args) ; no type annotations, must infer instantiation
;; infer instantiation types from args left-to-right, short-circuit if done early
;; infer instantiation types from args left-to-right,
;; short-circuit if done early, and use result to help infer remaining args
#:with (~Tmp Ys . τs+) ((current-type-eval) #'(Tmp (X ...) τ ...))
(let loop ([a (stx-car #'args)] [a-rst (stx-cdr #'args)]
[τ+ (stx-car #'τs+)] [τ+rst (stx-cdr #'τs+)]
@ -173,10 +212,28 @@
#:with (acc ...) (syntax-property #'τ_e 'accessors)
( (let ([x_out (acc e-)] ...) e_out) : τ_out)]))
(define-syntax lifted→ ; wrap → with ∀
#;(define-syntax lifted→ ; wrap → with ∀
(syntax-parser
[(_ . rst) #'( () (ext-stlc:→ . rst))]))
(define-syntax lifted→ ; wrapping →
(syntax-parser
#;[(_ (~and Xs {X:id ...}) . rst)
#:when (brace? #'Xs)
#:when (with-handlers ([exn:fail:syntax:unbound? (λ (e) (displayln (exn:fail:syntax-exprs e)))])
((current-type-eval) #'(ext-stlc:→ . rst)))
#'( (X ...) (ext-stlc:→ . rst))]
[(_ . rst)
(let L ([Xs #'()]) ; compute unbound ids; treat as tyvars
(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 #'rst (syntax->datum X)))
(L (cons Y Xs)))])
((current-type-eval) #`( #,Xs (ext-stlc:→ . rst)))))]))
;#'(∀ () (ext-stlc:→ . rst))
; redefine these to use lifted→
(define-primop + : (lifted→ Int Int Int))
(define-primop - : (lifted→ Int Int Int))
(define-primop void : (lifted→ Unit))
@ -188,14 +245,62 @@
(define-primop abs : (lifted→ Int Int))
; all λs have type (∀ (X ...) (→ τ_in ... τ_out))
; all λs have type (∀ (X ...) (→ τ_in ... τ_out)), even monomorphic fns
(define-typed-syntax liftedλ #:export-as λ #:datum-literals (:)
[(_ . rst)
#'(Λ () (ext-stlc:λ . rst))])
(define-typed-syntax new-app #:export-as #%app
#;(define-typed-syntax new-app #:export-as #%app
[(_ τs f . args)
#:when (brace? #'τs)
#'(ext-stlc:#%app (inst f . τs) . args)]
[(_ f . args)
#'(ext-stlc:#%app (inst f) . args)])
(define-typed-syntax #%app
[(_ e_fn e_arg ...) ; infer args first
#:with ([e_arg- τ_arg] ...) (infers+erase #'(e_arg ...))
;#:with [e_fn- ((X ...) ((~ext-stlc:→ τ_inX ... τ_outX)))] (⇑ e_fn as ∀)
;#:with [e_fn- (~∀ (X ...) (~ext-stlc:→ τ_inX ... τ_outX))] (infer+erase #'e_fn)
;; infer type step-by-step to produce better err msg
#:with [e_fn- τ_fn] (infer+erase #'e_fn)
#:fail-unless (and (∀? #'τ_fn) (syntax-parse #'τ_fn [(~∀ _ (~ext-stlc:→ . args)) #t][_ #f]))
(format "Expected expression ~a to have → type, got: ~a"
(syntax->datum #'e_fn) (type->str #'τ_fn))
#:with (~∀ (X ...) (~ext-stlc:→ τ_inX ... τ_outX)) #'τ_fn
#:fail-unless (stx-length=? #'(τ_inX ...) #'(e_arg ...)) ; check arity
(string-append
(format "~a (~a:~a) Wrong number of arguments given to function ~a.\n"
(syntax-source stx) (syntax-line stx) (syntax-column stx)
(syntax->datum #'e_fn))
(format "Expected: ~a arguments with types: "
(stx-length #'(τ_inX ...)))
(string-join (stx-map type->str #'(τ_inX ...)) ", " #:after-last "\n")
"Given:\n"
(string-join
(map (λ (e t) (format " ~a : ~a" e t)) ; indent each line
(syntax->datum #'(e_arg ...))
(stx-map type->str #'(τ_arg ...)))
"\n"))
#:with cs (compute-constraints #'((τ_inX τ_arg) ...))
#:with (τ_solved ...) (filter (λ (x) x) (stx-map (λ (y) (lookup y #'cs)) #'(X ...)))
#:fail-unless (stx-length=? #'(X ...) #'(τ_solved ...))
(format "could not instantiate polymorphic fn ~a" (syntax->datum #'e_fn))
#:with (τ_in ... τ_out) (stx-map (λ (t) (substs #'(τ_solved ...) #'(X ...) t)) #'(τ_inX ... τ_outX))
; some code duplication
#:fail-unless (typechecks? #'(τ_arg ...) #'(τ_in ...))
(string-append
(format "~a (~a:~a) Arguments to function ~a have wrong type(s).\n"
(syntax-source stx) (syntax-line stx) (syntax-column stx)
(syntax->datum #'e_fn))
"Given:\n"
(string-join
(map (λ (e t) (format " ~a : ~a" e t)) ; indent each line
(syntax->datum #'(e_arg ...))
(stx-map type->str #'(τ_arg ...)))
"\n" #:after-last "\n")
(format "Expected: ~a arguments with type(s): "
(stx-length #'(τ_in ...)))
(string-join (stx-map type->str #'(τ_in ...)) ", "))
( (#%app e_fn- e_arg- ...) : τ_out)])

View File

@ -1,6 +1,64 @@
#lang s-exp "../mlish.rkt"
(require "rackunit-typechecking.rkt")
(define (recf [x : Int] Int) (recf x))
;; tests more or less copied from infer-tests.rkt ------------------------------
;; top-level defines
(define (f [x : Int] Int) x)
(check-type f : ( Int Int))
(check-type (f 1) : Int 1)
(typecheck-fail (f (λ ([x : Int]) x)))
(define (g [x : X] X) x)
(check-type g : ( X X))
; (inferred) polymorpic instantiation
(check-type (g 1) : Int 1)
(check-type (g #f) : Bool #f) ; different instantiation
(check-type (g add1) : ( Int Int))
(check-type (g +) : ( Int Int Int))
; function polymorphic in list element
(define-type (List X)
(Nil)
(Cons X (List X)))
(define (g2 [lst : (List X)] (List X)) lst)
(check-type g2 : ( (List X) (List X)))
(typecheck-fail (g2 1) #:with-msg "Expected.+arguments with type.+(List X)")
;(check-type (g2 (Nil {Int})) : (List Int) ⇒ (Nil {Int}))
;(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))}))
;(check-type (g2 (Cons 1 Nil)) : (List Int) ⇒ (Cons 1 Nil))
;(check-type (g2 (Cons "1" Nil)) : (List String) ⇒ (Cons "1" Nil))
;(define (g3 [lst : (List X)] → X) (hd lst)) ; cant type this fn (what to put for nil case)
;(check-type g3 : (→ {X} (List X) X))
;(check-type g3 : (→ {A} (List A) A))
;(check-not-type g3 : (→ {A B} (List A) B))
;(typecheck-fail (g3) #:with-msg "Expected.+arguments with type.+List") ; TODO: more precise err msg
;(check-type (g3 (nil {Int})) : Int) ; runtime fail
;(check-type (g3 (nil {Bool})) : Bool) ; runtime fail
;(check-type (g3 (cons 1 nil)) : Int ⇒ 1)
;(check-type (g3 (cons "1" nil)) : String ⇒ "1")
; recursive fn
;(define (recf [x : Int] → Int) (recf x))
;(check-type recf : (→ Int Int))
;
;(define (countdown [x : Int] → Int)
; (if (zero? x)
; 0
; (countdown (sub1 x))))
;(check-type (countdown 0) : Int ⇒ 0)
;(check-type (countdown 10) : Int ⇒ 0)
;(typecheck-fail (countdown "10") #:with-msg "Arguments.+have wrong type")
;; end infer.rkt tests --------------------------------------------------
;; algebraic data types
(define-type IntList
INil
(ConsI Int IntList))
@ -17,9 +75,6 @@
[ConsI x xs -> 2]) : Int 2)
(typecheck-fail (match 1 with [INil -> 1]))
(define-type (List X)
(Nil)
(Cons X (List X)))
;; annotated
(check-type (Nil {Int}) : (List Int))
(check-type (Cons {Int} 1 (Nil {Int})) : (List Int))
@ -236,7 +291,7 @@
"Arguments to function \\+ have wrong type.+Given:.+(→ Int Int).+Expected: 2 arguments with type.+Int\\, Int")
(typecheck-fail
((λ ([x : Int] [y : Int]) y) 1)
#:with-msg "Arguments to function.+have.+wrong number of arguments")
#:with-msg "Wrong number of arguments given to function")
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)