macrotypes/turnstile/examples/fomega.rkt
Stephen Chang 115aae8e73 completely separate type and kind api, etc; generalize type environment
Previously, "type" functions were reused a lot to manipulate kinds, and other
metadata defined via `define-syntax-category`, but this meant it was impossible
to define separate behavior for some type and kind operations, e.g., type=? and
kind=?. This commit defines a separate api for each `define-syntax-category`
declaration.

Also, every `define-syntax-category` defines a new `define-NAMEd-syntax` form,
which implicitly uses the proper parameters, e.g., `define-kinded-syntax` uses
`kindcheck?`, `current-kind-eval`, and the ':: kind key by default (whereas
before, it was using typecheck?, type-eval, etc).

This commit breaks backwards compatibility. The most likely breakage results
from using a different default key for kinds. It used to be ':, the same as
types, but now the default is '::.

This commit also generalizes the contexts used with `define-NAMEd-syntax` and
`infer`.
- all contexts now accept arbitrary key-values associated with a variable
- all contexts use let* semantics, where a binding is in scope for subsequent
  bindings; this means that one environment is sufficient in most scenarioes,
  e.g., type and term vars can be mixed (if properly ordered)
- environments allow lone identifiers, which are treated as type variables by
  default
2017-02-08 13:07:24 -05:00

101 lines
3.8 KiB
Racket

#lang turnstile/lang
(reuse λ #%app Int + #:from "sysf.rkt")
(reuse define-type-alias #%datum String #:from "ext-stlc.rkt")
;; System F_omega
;; Types:
;; - redefine ∀
;; - extend sysf with tyλ and tyapp
;; Terms:
;; - extend sysf with Λ inst
(provide (type-out ) (kind-out ∀★) Λ inst tyλ tyapp)
(define-syntax-category :: kind)
;; want #%type to be equiv to ★
;; => extend current-kind? to recognize #%type
;; <= define ★ as rename-transformer expanding to #%type
(begin-for-syntax
(current-kind? (λ (k) (or (#%type? k) (kind? k))))
;; any valid type (includes ⇒-kinded types)
(current-any-type? (λ (t) (define k (kindof t))
(and k ((current-kind?) k))))
;; well-formed types, ie not types with ⇒ kind
(current-type? (λ (t) (and ((current-any-type?) t)
(not (⇒? (kindof t)))))))
(begin-for-syntax
(define ★? #%type?)
(define-syntax ~★ (λ _ (error "~★ not implemented")))) ; placeholder
(define-syntax (make-rename-transformer #'#%type))
(define-kind-constructor #:arity >= 1)
(define-kind-constructor ∀★ #:arity >= 0)
(define-binding-type #:arr ∀★)
;; alternative: normalize before type=?
;; but then also need to normalize in current-promote
(begin-for-syntax
(define (normalize τ)
(syntax-parse τ #:literals (#%plain-app #%plain-lambda)
[x:id #'x]
[(#%plain-app
(#%plain-lambda (tv ...) τ_body) τ_arg ...)
(normalize (substs #'(τ_arg ...) #'(tv ...) #'τ_body))]
[(#%plain-lambda (x ...) . bodys)
#:with bodys_norm (stx-map normalize #'bodys)
(transfer-stx-props #'(#%plain-lambda (x ...) . bodys_norm) τ #:ctx τ)]
[(#%plain-app x:id . args)
#:with args_norm (stx-map normalize #'args)
(transfer-stx-props #'(#%plain-app x . args_norm) τ #:ctx τ)]
[(#%plain-app . args)
#:with args_norm (stx-map normalize #'args)
#:with res (normalize #'(#%plain-app . args_norm))
(transfer-stx-props #'res τ #:ctx τ)]
[_ τ]))
(define old-eval (current-type-eval))
(define (new-type-eval τ) (normalize (old-eval τ)))
(current-type-eval new-type-eval)
(define old-type=? (current-type=?))
;; need to also compare kinds of types
(define (new-type=? t1 t2)
(let ([k1 (kindof t1)][k2 (kindof t2)])
;; need these `not` checks bc type= does a structural stx traversal
;; and may compare non-type ids (like #%plain-app)
(and (or (and (not k1) (not k2))
(and k1 k2 ((current-kind=?) k1 k2)))
(old-type=? t1 t2))))
(current-typecheck-relation new-type=?))
(define-typed-syntax (Λ bvs:kind-ctx e)
[[bvs.x tv- :: bvs.kind] ... e e- τ_e]
--------
[ e- ( ([tv- :: bvs.kind] ...) τ_e)])
;; τ.norm invokes current-type-eval while "≫ τ-" uses only local-expand
;; (via infer fn)
(define-typed-syntax (inst e τ:any-type ...)
[ e e- (~∀ tvs τ_body) ( :: (~∀★ k ...))]
[ τ τ- :: k] ...
--------
[ e- #,(substs #'(τ.norm ...) #'tvs #'τ_body)])
;; - see fomega2.rkt for example with no explicit tyλ and tyapp
(define-kinded-syntax (tyλ bvs:kind-ctx τ_body)
[[bvs.x tv- :: bvs.kind] ... τ_body τ_body- k_body]
#:fail-unless ((current-kind?) #'k_body) ; better err, in terms of τ_body
(format "not a valid type: ~a\n" (type->str #'τ_body))
--------
[ (λ- (tv- ...) τ_body-) ( bvs.kind ... k_body)])
(define-kinded-syntax (tyapp τ_fn τ_arg ...)
[ τ_fn τ_fn- (~⇒ k_in ... k_out)]
#:fail-unless (stx-length=? #'[k_in ...] #'[τ_arg ...])
(num-args-fail-msg #'τ_fn #'[k_in ...] #'[τ_arg ...])
[ τ_arg τ_arg- k_in] ...
--------
[ (#%app- τ_fn- τ_arg- ...) k_out])