split def-kinded-stx from def-typed-stx; split any-type? from type?

- the former prevents using current-typecheck-relation at type/kind level
- the latter differentiates "well-formed" types (star)
  from valid types (any kind)
- move define-*ed-syntax and *-eval into define-syntax-category
- turnstile must wrap define-stx-category to define new define-*ed-syntax
- add any-*? pred and any-* stx class in def-stx-category
- fixes #44
- fixes #45
This commit is contained in:
Stephen Chang 2016-10-14 14:16:49 -04:00
parent 34b149e248
commit 28fa5dd033
16 changed files with 185 additions and 115 deletions

View File

@ -42,14 +42,14 @@
;; τ.norm in 1st case causes "not valid type" error when file is compiled
(define-syntax define-type-alias
(syntax-parser
[(_ alias:id τ:type)
[(_ 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 τ:type #'ty
#:with τ:any-type #'ty
#'τ.norm]))]))
(define-typed-syntax define

View File

@ -50,8 +50,8 @@
#:with-msg "ann: type mismatch: expected Bool, given Int\n *expression: 1")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : 1) #:with-msg "not a well-formed type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a well-formed type")
(typecheck-fail (ann Int : Int)
#:with-msg "ann: type mismatch: expected Int, given #%type\n *expression: Int")

View File

@ -244,8 +244,8 @@
(typecheck-fail (ann 1 : Bool) #:with-msg "expected Bool, given Int\n *expression: 1")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : 1) #:with-msg "not a well-formed type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a well-formed type")
(typecheck-fail (ann Int : Int) #:with-msg "expected Int, given #%type\n *expression: Int")
; let

View File

@ -673,8 +673,8 @@
(typecheck-fail (ann 1 : Bool) #:with-msg "expected Bool, given Int\n *expression: 1")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : 1) #:with-msg "not a well-formed type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a well-formed type")
(typecheck-fail (ann Int : Int) #:with-msg "expected Int, given #%type\n *expression: Int")
; let

View File

@ -55,7 +55,7 @@
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)
(typecheck-fail (λ ([x : ( 1 2)]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : 1]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : (+ 1 2)]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : (λ ([y : Int]) y)]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : ( 1 2)]) x) #:with-msg "not a well-formed type")
(typecheck-fail (λ ([x : 1]) x) #:with-msg "not a well-formed type")
(typecheck-fail (λ ([x : (+ 1 2)]) x) #:with-msg "not a well-formed type")
(typecheck-fail (λ ([x : (λ ([y : Int]) y)]) x) #:with-msg "not a well-formed type")

View File

@ -237,16 +237,16 @@
(typecheck-fail
(λ ([x : ( Int Boolean)])
(test (1 ? x) #t #f))
#:with-msg "not a valid type")
#:with-msg "not a well-formed type")
(typecheck-fail
(test (1 ? 1) #t #f)
#:with-msg "not a valid type")
#:with-msg "not a well-formed type")
(typecheck-fail
(test (1 ? 1) #t #f)
#:with-msg "not a valid type")
#:with-msg "not a well-formed type")
(typecheck-fail
(test (#f ? #t) #t #f)
#:with-msg "not a valid type")
#:with-msg "not a well-formed type")
;; -----------------------------------------------------------------------------
;; --- Subtypes should not be collapsed

View File

@ -64,6 +64,7 @@
(define (mk-- id) (format-id id "~a-" id))
(define (mk-~ id) (format-id id "~~~a" id))
(define (mk-#% id) (format-id id "#%~a" id))
(define (mk-param id) (format-id id "current-~a" id))
(define-for-syntax (mk-? id) (format-id id "~a?" id))
(define-for-syntax (mk-~ id) (format-id id "~~~a" id))
;; drop-file-ext : String -> String
@ -401,21 +402,6 @@
(syntax-parse (infer es #:tvctx ctx)
[(tvs+ _ es+ tys) (list #'tvs+ #'es+ #'tys)]))
; extra indirection, enables easily overriding type=? with sub?
; to add subtyping, without changing any other definitions
; - must be here (instead of stlc) due to rackunit-typechecking
(define current-typecheck-relation (make-parameter #f))
;; convenience fns for current-typecheck-relation
(define (typecheck? t1 t2)
((current-typecheck-relation) t1 t2))
(define (typechecks? τs1 τs2)
(and (= (stx-length τs1) (stx-length τs2))
(stx-andmap typecheck? τs1 τs2)))
(define current-type-eval (make-parameter #f))
(define (type-evals τs) #`#,(stx-map (current-type-eval) τs))
(define current-promote (make-parameter (λ (t) t)))
;; term expansion
@ -429,22 +415,6 @@
(define (expand/df e)
(local-expand e 'expression null))
;; type eval
;; - default-type-eval == full expansion == canonical type representation
;; - must expand because:
;; - checks for unbound identifiers (ie, undefined types)
;; - checks for valid types, ow can't distinguish types and terms
;; - could parse types but separate parser leads to duplicate code
;; - later, expanding enables reuse of same mechanisms for kind checking
;; and type application
(define (default-type-eval τ)
; TODO: optimization: don't expand if expanded
; currently, this causes problems when
; combining unexpanded and expanded types to create new types
(add-orig (expand/df τ) τ))
(current-type-eval default-type-eval)
;; typecheck-fail-msg/1 : Type Type Stx -> String
(define (typecheck-fail-msg/1 τ_expected τ_given expression)
(format "type mismatch: expected ~a, given ~a\n expression: ~s"
@ -862,12 +832,21 @@
(syntax-parse stx
[(_ name:id)
#:with names (format-id #'name "~as" #'name)
#:with #%tag (format-id #'name "#%~a" #'name)
#:with #%tag (mk-#% #'name)
#:with #%tag? (mk-? #'#%tag)
#:with is-name? (mk-? #'name)
#:with any-name (format-id #'name "any-~a" #'name)
#:with any-name? (mk-? #'any-name)
#:with name-ctx (format-id #'name "~a-ctx" #'name)
#:with name-bind (format-id #'name "~a-bind" #'name)
#:with current-is-name? (format-id #'is-name? "current-~a" #'is-name?)
#:with current-is-name? (mk-param #'is-name?)
#:with current-any-name? (mk-param #'any-name?)
#:with current-namecheck-relation (format-id #'name "current-~acheck-relation" #'name)
#:with namecheck? (format-id #'name "~acheck?" #'name)
#:with namechecks? (format-id #'name "~achecks?" #'name)
#:with current-name-eval (format-id #'name "current-~a-eval" #'name)
#:with default-name-eval (format-id #'name "default-~a-eval" #'name)
#:with name-evals (format-id #'name "~a-evals" #'name)
#:with mk-name (format-id #'name "mk-~a" #'name)
#:with define-base-name (format-id #'name "define-base-~a" #'name)
#:with define-base-names (format-id #'name "define-base-~as" #'name)
@ -878,15 +857,19 @@
#:with name-ann (format-id #'name "~a-ann" #'name)
#:with name=? (format-id #'name "~a=?" #'name)
#:with names=? (format-id #'names "~a=?" #'names)
#:with current-name=? (format-id #'name=? "current-~a" #'name=?)
#:with current-name=? (mk-param #'name=?)
#:with same-names? (format-id #'name "same-~as?" #'name)
#:with name-out (format-id #'name "~a-out" #'name)
#'(begin
(define #%tag void)
(begin-for-syntax
(define (#%tag? t) (and (identifier? t) (free-identifier=? t #'#%tag)))
;; is-name?, eg type?, corresponds to "well-formed" types
(define (is-name? t) (#%tag? (typeof t)))
(define current-is-name? (make-parameter is-name?))
;; any-name? corresponds to any type and defaults to is-name?
(define (any-name? t) (is-name? t))
(define current-any-name? (make-parameter any-name?))
(define (mk-name t) (assign-type t #'#%tag))
(define-syntax-class name
#:attributes (norm)
@ -894,6 +877,15 @@
#:with norm ((current-type-eval) #'τ)
#:with k (typeof #'norm)
#:fail-unless ((current-is-name?) #'norm)
(format "~a (~a:~a) not a well-formed ~a: ~a"
(syntax-source #'τ) (syntax-line #'τ) (syntax-column #'τ)
'name (type->str #'τ))))
(define-syntax-class any-name
#:attributes (norm)
(pattern τ
#:with norm ((current-type-eval) #'τ)
#:with k (typeof #'norm)
#:fail-unless ((current-any-name?) #'norm)
(format "~a (~a:~a) not a valid ~a: ~a"
(syntax-source #'τ) (syntax-line #'τ) (syntax-column #'τ)
'name (type->str #'τ))))
@ -943,14 +935,37 @@
[_ (and (stx-pair? t1) (stx-pair? t2)
(names=? t1 t2))])))
(define current-name=? (make-parameter name=?))
(current-typecheck-relation name=?)
(define (names=? τs1 τs2)
(and (stx-length=? τs1 τs2)
(stx-andmap (current-name=?) τs1 τs2)))
; extra indirection, enables easily overriding type=? with sub?
; to add subtyping, without changing any other definitions
(define current-namecheck-relation (make-parameter name=?))
;; convenience fns for current-typecheck-relation
(define (namecheck? t1 t2)
((current-namecheck-relation) t1 t2))
(define (namechecks? τs1 τs2)
(and (= (stx-length τs1) (stx-length τs2))
(stx-andmap namecheck? τs1 τs2)))
(define (same-names? τs)
(define τs-lst (syntax->list τs))
(or (null? τs-lst)
(andmap (λ (τ) ((current-name=?) (car τs-lst) τ)) (cdr τs-lst)))))
(andmap (λ (τ) ((current-name=?) (car τs-lst) τ)) (cdr τs-lst))))
;; type eval
;; - default-type-eval == full expansion == canonical type representation
;; - must expand because:
;; - checks for unbound identifiers (ie, undefined types)
;; - checks for valid types, ow can't distinguish types and terms
;; - could parse types but separate parser leads to duplicate code
;; - later, expanding enables reuse of same mechanisms for kind checking
;; and type application
(define (default-name-eval τ)
; TODO: optimization: don't expand if expanded
; currently, this causes problems when
; combining unexpanded and expanded types to create new types
(add-orig (expand/df τ) τ))
(define current-name-eval (make-parameter default-name-eval))
(define (name-evals τs) #`#,(stx-map (current-name-eval) τs)))
;; helps with providing defined types
(define-syntax name-out
(make-provide-transformer

View File

@ -41,14 +41,14 @@
;; τ.norm in 1st case causes "not valid type" error when file is compiled
(define-syntax define-type-alias
(syntax-parser
[(_ alias:id τ:type)
[(_ 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 τ:type #'ty
#:with τ:any-type #'ty
#'τ.norm]))]))
(define-typed-syntax define

View File

@ -15,12 +15,17 @@
(define-syntax-category kind)
;; redefine:
;; - current-type? - recognizes types with ★ kind only
;; - current-type-eval - reduce tylams and tyapps
;; - current-type=? - must check for any kind annotations
;; - current-type?: well-formed types have kind ★
;; - current-any-type?: valid types have any valid kind
;; - current-type-eval: reduce tylams and tyapps
;; - current-type=?: must compare kind annotations as well
(begin-for-syntax
;; need this for define-primop (which still uses type stx-class)
;; well-formed types have kind ★
;; (need this for define-primop, which still uses type stx-class)
(current-type? (λ (t) (★? (typeof t))))
;; o.w., a valid type is one with any valid kind
(current-any-type? (λ (t) ((current-kind?) (typeof t))))
;; TODO: I think this can be simplified
(define (normalize τ)
@ -47,11 +52,12 @@
; ty=? == syntax eq and syntax prop eq
(define (type=? t1 t2)
(let ([k1 (typeof t1)][k2 (typeof t2)])
(and (or (and (not k1) (not k2)) ; need this bc there's no current-kindcheck-rel
; the extra `and` and `or` clauses are bc type=? is a structural
; traversal on stx objs, so not all sub stx objs will have a "type"-stx
(and (or (and (not k1) (not k2))
(and k1 k2 ((current-kind=?) k1 k2)))
(old-type=? t1 t2))))
(current-type=? type=?)
;; TODO: add current-kindcheck-rel
(current-typecheck-relation (current-type=?)))
;; kinds ----------------------------------------------------------------------
@ -64,13 +70,10 @@
(define-kind-constructor #:arity >= 1)
;; types ----------------------------------------------------------------------
(define-typed-syntax (define-type-alias alias:id τ)
[ τ τ- k_τ]
#:fail-unless ((current-kind?) #'k_τ)
(format "not a valid type: ~a\n" (type->str #'τ))
(define-kinded-syntax (define-type-alias alias:id τ:any-type)
------------------
[ (define-syntax- alias
(make-variable-like-transformer #'τ-))])
(make-variable-like-transformer #'τ.norm))])
(define-base-type Int : )
(define-base-type Bool : )
@ -79,26 +82,26 @@
(define-base-type Char : )
(define-internal-type-constructor ) ; defines →-
(define-typed-syntax ( ty ...+)
(define-kinded-syntax ( ty ...+)
[ ty ty- (~★ . _)] ...
--------
[ (→- ty- ...) ])
(define-internal-binding-type ) ; defines ∀-
(define-typed-syntax #:datum-literals (:)
(define-kinded-syntax #:datum-literals (:)
[(_ ([tv:id : k_in:kind] ...) ty)
[[tv tv- : k_in.norm] ... ty ty- (~★ . _)]
-------
[ (∀- (tv- ...) ty-) ( k_in.norm ...)]])
(define-typed-syntax (tyλ bvs:kind-ctx τ_body)
(define-kinded-syntax (tyλ bvs:kind-ctx τ_body)
[[bvs.x tv- : bvs.kind] ... τ_body τ_body- k_body]
#:fail-unless ((current-kind?) #'k_body)
(format "not a valid type: ~a\n" (type->str #'τ_body))
--------
[ (λ- (tv- ...) τ_body-) ( bvs.kind ... k_body)])
(define-typed-syntax (tyapp τ_fn τ_arg ...)
(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 ...])
@ -156,9 +159,15 @@
--------
[ e- ( ([tv- : bvs.kind] ...) τ_e)])
;; TODO: what to do when a def-typed-stx needs both
;; current-typecheck-relation and current-kindcheck-relation
(define-typed-syntax (inst e τ ...)
[ e e- (~∀ (tv ...) τ_body) ( (~★ k ...))]
[ τ τ- k] ...
; [⊢ τ ≫ τ- ⇐ k] ...
;; want to use kindchecks? instead of typechecks?
[ τ τ- k_τ] ...
#:fail-unless (kindchecks? #'(k_τ ...) #'(k ...))
(typecheck-fail-msg/multi #'(k ...) #'(k_τ ...) #'(τ ...))
#:with τ-inst (substs #'(τ- ...) #'(tv ...) #'τ_body)
--------
[ e- τ-inst])

View File

@ -31,7 +31,7 @@
(define-syntax define-named-type-alias
(syntax-parser
[(define-named-type-alias Name:id τ:type)
[(define-named-type-alias Name:id τ:any-type)
#'(define-syntax Name
(make-variable-like-transformer (add-orig #'τ #'Name)))]
[(define-named-type-alias (f:id x:id ...) ty) ; dont expand yet

View File

@ -50,8 +50,8 @@
#:with-msg "ann: type mismatch: expected Bool, given Int\n *expression: 1")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : 1) #:with-msg "not a well-formed type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a well-formed type")
(typecheck-fail (ann Int : Int)
#:with-msg "ann: type mismatch: expected Int, given #%type\n *expression: Int")

View File

@ -54,7 +54,7 @@
: ( ( Int String) ( Int String)))
(typecheck-fail
(inst (Λ ([X : ]) (λ ([x : X]) x)) 1)
#:with-msg "inst: type mismatch: expected ★, given Int\n *expression: 1")
#:with-msg "inst: type mismatch.*expected:.*★.*given:.*Int.*expressions: 1")
(typecheck-fail
(Λ ([tyf : ( )]) (λ ([f : (tyapp tyf String)]) (f 1)))

View File

@ -669,8 +669,8 @@
(typecheck-fail (ann 1 : Bool) #:with-msg "expected Bool, given Int\n *expression: 1")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : 1) #:with-msg "not a well-formed type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a well-formed type")
(typecheck-fail (ann Int : Int) #:with-msg "expected Int, given #%type\n *expression: Int")
; let

View File

@ -54,10 +54,10 @@
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)
(typecheck-fail (λ ([x : ( 1 2)]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : 1]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : (+ 1 2)]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : (λ ([y : Int]) y)]) x) #:with-msg "not a valid type")
(typecheck-fail (λ ([x : ( 1 2)]) x) #:with-msg "not a well-formed type")
(typecheck-fail (λ ([x : 1]) x) #:with-msg "not a well-formed type")
(typecheck-fail (λ ([x : (+ 1 2)]) x) #:with-msg "not a well-formed type")
(typecheck-fail (λ ([x : (λ ([y : Int]) y)]) x) #:with-msg "not a well-formed type")
(typecheck-fail
(ann (ann 5 : Int) : ( Int))

View File

@ -112,8 +112,6 @@ Dually, one may write @racket[[⊢ e ≫ e- ⇐ τ]] to check that @racket[e] ha
@racket[τ]. Here, both @racket[e] and @racket[τ] are inputs (templates) and only
@racket[e-] is an output (pattern).}
@defform[(define-typerule ....)]{An alias for @racket[define-typed-syntax].}
@defform*[((define-primop typed-op-id τ)
(define-primop typed-op-id : τ)
(define-primop typed-op-id op-id τ)
@ -135,11 +133,18 @@ Turnstile pre-declares @racket[(define-syntax-category type)], which in turn
use any forms other than @racket[define-base-type] and
@racket[define-type-constructor] in conjunction with @racket[define-typed-syntax]. The other forms are considered "low-level" and are automatically used by @racket[define-typed-syntax].
@itemlist[
@item{@defform[(define-base-type base-type-name-id)]{
Defines a base type. A @racket[(define-base-type τ)] additionally defines:
@item{@racket[define-typed-syntax], as described above.
Uses @racket[current-typecheck-relation] for typechecking.}
@item{@defform[(define-typerule ....)]{An alias for @racket[define-typed-syntax].}}
@item{@defform*[((define-base-type base-type-name-id)
(define-base-type base-type-name-id : kind))]{
Defines a base type. @racket[(define-base-type τ)] in turn defines:
@itemlist[@item{@racket[τ], an identifier macro representing type @racket[τ].}
@item{@racket[τ?], a phase 1 predicate recognizing type @racket[τ].}
@item{@racket[~τ], a phase 1 @tech:pat-expander recognizing type @racket[τ].}]}}
@item{@racket[~τ], a phase 1 @tech:pat-expander recognizing type @racket[τ].}]}
The second form is useful when implementing your own kind system.
@racket[#%type] is used as the @tt{kind} when it's not specified.}
@item{@defform[(define-base-types base-type-name-id ...)]{Defines multiple base types.}}
@item{
@defform[(define-type-constructor name-id option ...)
@ -235,6 +240,33 @@ Turnstile pre-declares @racket[(define-syntax-category type)], which in turn
A @racket[provide]-spec that, given @racket[ty-id], provides @racket[ty-id],
and provides @racket[for-syntax] a predicate @racket[ty-id?] and a @tech:pat-expander @racket[~ty-id].}}
@item{@defparam[current-type-eval type-eval type-eval]{
A phase 1 parameter for controlling "type evaluation". A @racket[type-eval]
function consumes and produces syntax. It is typically used to convert a type
into a canonical representation. The @racket[(current-type-eval)] is called
immediately before attacing a type to a syntax object, i.e., by
@racket[assign-type].
It defaults to full expansion, i.e., @racket[(lambda (stx) (local-expand stx 'expression null))], and also stores extra surface syntax information used for error reporting.
One should extend @racket[current-type-eval] if canonicalization of types
depends on combinations of different types, e.g., type lambdas and type application in F-omega. }}
@item{@defparam[current-typecheck-relation type-pred type-pred]{
A phase 1 parameter for controlling type checking, used by @racket[define-type-syntax].
A @racket[type-pred] function consumes
two types and returns true if they "type check". It defaults to @racket[type=?] though it does not have to be a symmetric relation.
Useful for reusing rules that differ only in the type check operation, e.g.,
equality vs subtyping.}}
@item{@defproc[(typecheck? [τ1 type?] [τ2 type?]) boolean?]{A phase 1 function that calls
@racket[current-typecheck-relation].}}
@item{@defproc[(typechecks? [τs1 (or/c (list/c type?) (stx-list/c type?))]
[τs2 (or/c (list/c type?) (stx-list/c type?))]) boolean?]{
Phase 1 function mapping @racket[typecheck?] over lists of types, pairwise. @racket[τs1] and @racket[τs2]
must have the same length.}}
@item{@defproc[(type=? [τ1 type?] [τ2 type?]) boolean?]{A phase 1 equality
predicate for types that computes structural, @racket[free-identifier=?]
equality, but includes alpha-equivalence.
@ -270,15 +302,28 @@ equality, but includes alpha-equivalence.
A phase 1 parameter for computing type equality. Is initialized to @racket[type=?].}}
@item{@defidform[#%type]{The default "kind" used to validate types.}}
@item{@defproc[(#%type? [x Any]) boolean?]{Phase 1 predicate recognizing @racket[#%type].}}
@item{@defproc[(type? [x Any]) boolean?]{A phase 1 predicate used to validate types.
@item{@defproc[(type? [x Any]) boolean?]{A phase 1 predicate recognizing well-formed types.
Checks that @racket[x] is a syntax object and has syntax propety @racket[#%type].}}
@item{@defparam[current-type? type-pred type-pred]{A phase 1 parameter, initialized to @racket[type?],
used to validate types. Useful when reusing type rules in different languages.}}
used to recognize well-formed types.
Useful when reusing type rules in different languages. For example,
System F-omega may define this to recognize types with "star" kinds.}}
@item{@defproc[(any-type? [x Any]) boolean?]{A phase 1 predicate recognizing valid types.
Defaults to @racket[type?].}}
@item{@defparam[current-any-type? type-pred type-pred]{A phase 1 parameter,
initialized to @racket[any-type?],
used to validate (not necessarily well-formed) types.
Useful when reusing type rules in different languages. For example,
System F-omega may define this to recognize types with a any valid kind,
whereas @racket[current-type?] would recognize types with only the "star" kind.}}
@item{@defproc[(mk-type [stx syntax?]) type?]{
Phase 1 function that marks a syntax object as a type by attaching @racket[#%type].
Useful for defining your own type with arbitrary syntax that does not fit with
@racket[define-base-type] or @racket[define-type-constructor].}}
@item{@defthing[type stx-class]{A syntax class that calls @racket[current-type?]
to validate well-formed types.
Binds a @racket[norm] attribute to the type's expanded representation.}}
@item{@defthing[any-type stx-class]{A syntax class that calls @racket[current-any-type?]
to validate types.
Binds a @racket[norm] attribute to the type's expanded representation.}}
@item{@defthing[type-bind stx-class]{A syntax class recognizing
@ -335,32 +380,6 @@ necessary to call these directly, since @racket[define-typed-syntax] and other
forms already do so, but some type systems may require extending some
functionality.
@defparam[current-type-eval type-eval type-eval]{
A phase 1 parameter for controlling "type evaluation". A @racket[type-eval]
function consumes and produces syntax. It is typically used to convert a type
into a canonical representation. The @racket[(current-type-eval)] is called
immediately before attacing a type to a syntax object, i.e., by
@racket[assign-type].
It defaults to full expansion, i.e., @racket[(lambda (stx) (local-expand stx 'expression null))], and also stores extra surface syntax information used for error reporting.
One should extend @racket[current-type-eval] if canonicalization of types
depends on combinations of different types, e.g., type lambdas and type application in F-omega. }
@defparam[current-typecheck-relation type-pred type-pred]{
A phase 1 parameter for controlling type checking. A @racket[type-pred] function consumes
two types and returns true if they "type check". It defaults to @racket[type=?] though it does not have to be a symmetric relation.
Useful for reusing rules that differ only in the type check operation, e.g.,
equality vs subtyping.}
@defproc[(typecheck? [τ1 type?] [τ2 type?]) boolean?]{A phase 1 function that calls
@racket[current-typecheck-relation].}
@defproc[(typechecks? [τs1 (or/c (list/c type?) (stx-list/c type?))]
[τs2 (or/c (list/c type?) (stx-list/c type?))]) boolean?]{
Phase 1 function mapping @racket[typecheck?] over lists of types, pairwise. @racket[τs1] and @racket[τs2]
must have the same length.}
@defproc[(assign-type [e syntax?] [τ syntax?]) syntax?]{
Phase 1 function that calls @racket[current-type-eval] on @racket[τ] and attaches it to @racket[e]}

View File

@ -1,13 +1,15 @@
#lang racket/base
(provide (except-out (all-from-out macrotypes/typecheck) -define-typed-syntax)
define-typed-syntax
(provide (except-out (all-from-out macrotypes/typecheck)
-define-typed-syntax -define-syntax-category)
define-typed-syntax define-syntax-category
(rename-out [define-typed-syntax define-typerule])
(for-syntax syntax-parse/typed-syntax))
(require (except-in (rename-in
macrotypes/typecheck
[define-typed-syntax -define-typed-syntax]
[define-syntax-category -define-syntax-category]
)
#%module-begin))
@ -418,3 +420,28 @@
rule.norm
...)]))))
(define-syntax define-syntax-category
(lambda (stx)
(syntax-parse stx
[(_ name:id)
#:with def-named-syntax (format-id #'name "define-~aed-syntax" #'name)
#:with check-relation (format-id #'name "current-~acheck-relation" #'name)
#'(begin
(-define-syntax-category name)
(define-syntax (def-named-syntax stx)
(syntax-parse stx
;; single-clause def
[(_ (rulename:id . pats) . rst)
;; cannot bind name as pat var, eg #%app, so replace with _
#:with r #'[(_ . pats) . rst]
#'(define-syntax (rulename stxx)
(parameterize ([current-typecheck-relation (check-relation)])
(syntax-parse/typed-syntax stxx r)))]
;; multi-clause def
[(_ rulename:id
(~and (~seq kw-stuff (... ...)) :stxparse-kws)
rule:rule (... ...+))
#'(define-syntax (rulename stxx)
(parameterize ([current-typecheck-relation (check-relation)])
(syntax-parse/typed-syntax stxx kw-stuff (... ...)
rule (... ...))))])))])))