#lang turnstile/lang (extends "stlc+lit.rkt" #:except #%datum) ;; Simply-Typed Lambda Calculus, plus extensions (TAPL ch11) ;; Types: ;; - types from stlc+lit.rkt ;; - Bool, String ;; - Unit ;; Terms: ;; - terms from stlc+lit.rkt ;; - literals: bool, string ;; - boolean prims, numeric prims ;; - if ;; - prim void : (→ Unit) ;; - begin ;; - let, let*, letrec ;; Top-level: ;; - define (values and functions) ;; - define-type-alias (provide define-type-alias (for-syntax current-join) ⊔ (type-out Bool String Float Char Unit) zero? = (rename-out [typed- -] [typed* *]) (typed-out [add1 (→ Int Int)] [sub1 : (→ Int Int)] [[not- (→ Bool Bool)] not] [[void- : (→ Unit)] void]) define #%datum and or if begin let let* letrec) (define-base-types Bool String Float Char Unit) ;; test all variations of define-primop and typed-out (define-primop zero? (→ Int Bool)) (define-primop = : (→ Int Int Bool)) (define-primop typed- - (→ Int Int Int)) (define-primop typed* * : (→ Int Int Int)) ;; τ.norm in 1st case causes "not valid type" error when file is compiled (define-syntax define-type-alias (syntax-parser [(_ alias:id τ:any-type) #'(define-syntax- alias (make-variable-like-transformer #'τ))] [(_ (f:id x:id ...) ty) #'(define-syntax- (f stx) (syntax-parse stx [(_ x ...) #:with τ:any-type #'ty #'τ.norm]))])) (define-typed-syntax define [(_ x:id (~datum :) τ:type e:expr) ≫ ;[⊢ e ≫ e- ⇐ τ.norm] #:with y (generate-temporary #'x) -------- [≻ (begin- (define-syntax x (make-rename-transformer (⊢ y : τ.norm))) (define- y (ann e : τ.norm)))]] [(_ x:id e) ≫ ;This won't work with mutually recursive definitions [⊢ e ≫ e- ⇒ τ] #:with y (generate-temporary #'x) #:with y+props (transfer-props #'e- (assign-type #'y #'τ)) -------- [≻ (begin- (define-syntax x (make-rename-transformer #'y+props)) (define- y e-))]] [(_ (f [x (~datum :) ty] ... (~or (~datum →) (~datum ->)) ty_out) e ...+) ≫ #:with f- (add-orig (generate-temporary #'f) #'f) -------- [≻ (begin- (define-syntax- f (make-rename-transformer (⊢ f- : (→ ty ... ty_out)))) (define- f- (stlc+lit:λ ([x : ty] ...) (stlc+lit:ann (begin e ...) : ty_out))))]]) (define-typed-syntax #%datum [(_ . b:boolean) ≫ -------- [⊢ (#%datum- . b) ⇒ Bool]] [(_ . s:str) ≫ -------- [⊢ (#%datum- . s) ⇒ String]] [(_ . f) ≫ #:when (flonum? (syntax-e #'f)) -------- [⊢ (#%datum- . f) ⇒ Float]] [(_ . c:char) ≫ -------- [⊢ (#%datum- . c) ⇒ Char]] [(_ . x) ≫ -------- [≻ (stlc+lit:#%datum . x)]]) (define-typed-syntax (and e ...) ≫ [⊢ e ≫ e- ⇐ Bool] ... -------- [⊢ (and- e- ...) ⇒ Bool]) (define-typed-syntax (or e ...) ≫ [⊢ e ≫ e- ⇐ Bool] ... -------- [⊢ (or- e- ...) ⇒ Bool]) (begin-for-syntax (define current-join (make-parameter (λ (x y) (unless (typecheck? x y) (type-error #:src x #:msg "branches have incompatible types: ~a and ~a" x y)) x)))) (define-syntax ⊔ (syntax-parser [(⊔ τ1 τ2 ...) (for/fold ([τ ((current-type-eval) #'τ1)]) ([τ2 (in-list (stx-map (current-type-eval) #'[τ2 ...]))]) ((current-join) τ τ2))])) (define-typed-syntax if [(_ e_tst e1 e2) ⇐ τ-expected ≫ [⊢ e_tst ≫ e_tst- ⇒ _] ; Any non-false value is truthy. [⊢ e1 ≫ e1- ⇐ τ-expected] [⊢ e2 ≫ e2- ⇐ τ-expected] -------- [⊢ (if- e_tst- e1- e2-)]] [(_ e_tst e1 e2) ≫ [⊢ e_tst ≫ e_tst- ⇒ _] ; Any non-false value is truthy. [⊢ e1 ≫ e1- ⇒ τ1] [⊢ e2 ≫ e2- ⇒ τ2] -------- [⊢ (if- e_tst- e1- e2-) ⇒ (⊔ τ1 τ2)]]) (define-typed-syntax begin [(_ e_unit ... e) ⇐ τ_expected ≫ [⊢ e_unit ≫ e_unit- ⇒ _] ... [⊢ e ≫ e- ⇐ τ_expected] -------- [⊢ (begin- e_unit- ... e-)]] [(_ e_unit ... e) ≫ [⊢ e_unit ≫ e_unit- ⇒ _] ... [⊢ e ≫ e- ⇒ τ_e] -------- [⊢ (begin- e_unit- ... e-) ⇒ τ_e]]) (define-typed-syntax let [(_ ([x e] ...) e_body ...) ⇐ τ_expected ≫ [⊢ e ≫ e- ⇒ : τ_x] ... [[x ≫ x- : τ_x] ... ⊢ (begin e_body ...) ≫ e_body- ⇐ τ_expected] -------- [⊢ (let- ([x- e-] ...) e_body-)]] [(_ ([x e] ...) e_body ...) ≫ [⊢ e ≫ e- ⇒ : τ_x] ... [[x ≫ x- : τ_x] ... ⊢ (begin e_body ...) ≫ e_body- ⇒ τ_body] -------- [⊢ (let- ([x- e-] ...) e_body-) ⇒ τ_body]]) ; dont need to manually transfer expected type ; result template automatically propagates properties ; - only need to transfer expected type when local expanding an expression ; - see let/tc (define-typed-syntax let* [(_ () e_body ...) ≫ -------- [≻ (begin e_body ...)]] [(_ ([x e] [x_rst e_rst] ...) e_body ...) ≫ -------- [≻ (let ([x e]) (let* ([x_rst e_rst] ...) e_body ...))]]) (define-typed-syntax letrec [(_ ([b:type-bind e] ...) e_body ...) ⇐ τ_expected ≫ [[b.x ≫ x- : b.type] ... ⊢ [e ≫ e- ⇐ b.type] ... [(begin e_body ...) ≫ e_body- ⇐ τ_expected]] -------- [⊢ (letrec- ([x- e-] ...) e_body-)]] [(_ ([b:type-bind e] ...) e_body ...) ≫ [[b.x ≫ x- : b.type] ... ⊢ [e ≫ e- ⇐ b.type] ... [(begin e_body ...) ≫ e_body- ⇒ τ_body]] -------- [⊢ (letrec- ([x- e-] ...) e_body-) ⇒ τ_body]])