macrotypes/turnstile/examples/ext-stlc.rkt
2017-03-10 17:03:30 -05:00

188 lines
5.5 KiB
Racket

#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]])