macrotypes/tapl/exist.rkt
Stephen Chang bf126449b4 define-type-constructor supports annotated bound vars
- use this to impl forall in fomega
- define-type-constructor default arity is =1
- define explicit normalize fn in fomega -- speeds up tests 15%
- move same-type to define-syntax-category
2015-10-09 16:59:48 -04:00

74 lines
2.7 KiB
Racket

#lang s-exp "typecheck.rkt"
(extends "stlc+reco+var.rkt")
(reuse #:from "stlc+rec-iso.rkt") ; want type=?, but only need to load current-type=?
;; existential types
;; Types:
;; - types from stlc+reco+var.rkt
;; - ∃
;; Terms:
;; - terms from stlc+reco+var.rkt
;; - pack and open
;; Other: type=? from stlc+rec-iso.rkt
(define-type-constructor #:bvs = 1)
(define-typed-syntax pack
[(_ (τ:type e) as ∃τ:type)
#:with (~∃* (τ_abstract) τ_body) #'∃τ.norm
#:with [e- τ_e] (infer+erase #'e)
#:when (typecheck? #'τ_e (subst #'τ.norm #'τ_abstract #'τ_body))
( e- : ∃τ.norm)])
(define-typed-syntax open #:datum-literals (<=)
[(_ ([(tv:id x:id) <= e_packed]) e)
#:with [e_packed- ((τ_abstract) (τ_body))] ( e_packed as )
;; The subst below appears to be a hack, but it's not really.
;; It's the (TaPL) type rule itself that is fast and loose.
;; Leveraging the macro system's management of binding reveals this.
;;
;; Specifically, here is the TaPL Unpack type rule, fig24-1, p366:
;; Γ ⊢ t_1 : {∃X,T_12}
;; Γ,X,x:T_12 ⊢ t_2 : T_2
;; ------------------------------
;; Γ ⊢ let {X,x}=t_1 in t_2 : T_2
;;
;; There's *two* separate binders, the ∃ and the let,
;; which the rule conflates.
;;
;; Here's the rule rewritten to distinguish the two binding positions:
;; Γ ⊢ t_1 : {∃X_1,T_12}
;; Γ,X_???,x:T_12 ⊢ t_2 : T_2
;; ------------------------------
;; Γ ⊢ let {X_2,x}=t_1 in t_2 : T_2
;;
;; The X_1 binds references to X in T_12.
;; The X_2 binds references to X in t_2.
;; What should the X_??? be?
;;
;; A first guess might be to replace X_??? with both X_1 and X_2,
;; so all the potentially referenced type vars are bound.
;; Γ ⊢ t_1 : {∃X_1,T_12}
;; Γ,X_1,X_2,x:T_12 ⊢ t_2 : T_2
;; ------------------------------
;; Γ ⊢ let {X_2,x}=t_1 in t_2 : T_2
;;
;; But this example demonstrates that the rule above doesnt work:
;; (open ([x : X_2 (pack (Int 0) as (∃ (X_1) X_1))])
;; ((λ ([y : X_2]) y) x)
;; Here, x has type X_1, y has type X_2, but they should be the same thing,
;; so we need to replace all X_1's with X_2
;;
;; Here's the fixed rule, which is implemented here
;;
;; Γ ⊢ t_1 : {∃X_1,T_12}
;; Γ,X_2,x:[X_2/X_1]T_12 ⊢ t_2 : T_2
;; ------------------------------
;; Γ ⊢ let {X_2,x}=t_1 in t_2 : T_2
;;
#:with [_ (x-) (e-) (τ_e)]
(infer #'(e)
#:tvctx #'([tv : #%type])
#:ctx #`([x : #,(subst #'tv #'τ_abstract #'τ_body)]))
( (let ([x- e_packed-]) e-) : τ_e)])