www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 115aae8e737ff6616aeaaad4f8e4891355e8aada
parent f8cb9959cd53372c160f66ee671be2f073560c88
Author: Stephen Chang <stchang@ccs.neu.edu>
Date:   Mon, 30 Jan 2017 10:28:22 -0500

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

Diffstat:
Minfo.rkt | 1+
Mmacrotypes/examples/exist.rkt | 5+----
Mmacrotypes/examples/fomega.rkt | 91+++++++++++++++++++++++++++++++------------------------------------------------
Mmacrotypes/examples/fomega2.rkt | 138++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mmacrotypes/examples/fomega3.rkt | 2++
Mmacrotypes/examples/fsub.rkt | 9++++-----
Mmacrotypes/examples/infer.rkt | 2+-
Mmacrotypes/examples/mlish+adhoc.rkt | 6+++---
Mmacrotypes/examples/mlish.rkt | 2+-
Mmacrotypes/examples/stlc+overloading.rkt | 2+-
Mmacrotypes/examples/sysf.rkt | 7+++----
Mmacrotypes/examples/tests/ext-stlc-tests.rkt | 4++--
Mmacrotypes/examples/tests/fomega-tests.rkt | 207++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mmacrotypes/examples/tests/fomega2-tests.rkt | 167++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mmacrotypes/examples/tests/general-tests.rkt | 14++++++++++----
Mmacrotypes/examples/tests/infer-tests.rkt | 2+-
Mmacrotypes/examples/tests/mlish-tests.rkt | 4+++-
Amacrotypes/examples/tests/rackunit-kindchecking.rkt | 16++++++++++++++++
Mmacrotypes/info.rkt | 5++++-
Mmacrotypes/stx-utils.rkt | 30+++++++++++++++++++++++++++++-
Mmacrotypes/type-constraints.rkt | 2+-
Mmacrotypes/typecheck.rkt | 1228+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mturnstile/examples/exist.rkt | 3+--
Aturnstile/examples/fomega-no-reuse-old.rkt | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mturnstile/examples/fomega-no-reuse.rkt | 64++++++++++++++++++++++++++++++----------------------------------
Mturnstile/examples/fomega.rkt | 104++++++++++++++++++++++++++++++++-----------------------------------------------
Mturnstile/examples/fomega2.rkt | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mturnstile/examples/fomega3.rkt | 2++
Mturnstile/examples/fsub.rkt | 6+++---
Mturnstile/examples/infer.rkt | 2+-
Mturnstile/examples/mlish+adhoc.rkt | 66++++++++++++++++++++++++++++++++++--------------------------------
Mturnstile/examples/mlish.rkt | 48+++++++++++++++++++++++-------------------------
Mturnstile/examples/stlc+effect.rkt | 6+++---
Mturnstile/examples/stlc+rec-iso.rkt | 3+--
Mturnstile/examples/stlc+reco+sub.rkt | 2+-
Mturnstile/examples/stlc+reco+var.rkt | 2+-
Mturnstile/examples/stlc+sub.rkt | 1+
Mturnstile/examples/stlc+union+case.rkt | 4+---
Mturnstile/examples/sysf.rkt | 9+++------
Mturnstile/examples/tests/ext-stlc-tests.rkt | 4++--
Aturnstile/examples/tests/fomega-no-reuse-tests-old.rkt | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mturnstile/examples/tests/fomega-no-reuse-tests.rkt | 182++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mturnstile/examples/tests/fomega-tests.rkt | 178++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mturnstile/examples/tests/fomega2-tests.rkt | 174++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mturnstile/examples/tests/mlish-tests.rkt | 12++++++++++--
Aturnstile/examples/tests/rackunit-kindchecking.rkt | 18++++++++++++++++++
Mturnstile/examples/tests/rackunit-typechecking.rkt | 11++++++-----
Mturnstile/examples/trivial.rkt | 6+++---
Mturnstile/info.rkt | 5++++-
Mturnstile/scribblings/reference.scrbl | 238++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mturnstile/turnstile.rkt | 229+++++++++++++++++++++++++++++++++++++++++--------------------------------------
51 files changed, 2275 insertions(+), 1528 deletions(-)

diff --git a/info.rkt b/info.rkt @@ -15,3 +15,4 @@ "racket-doc" )) +(define version "0.1") diff --git a/macrotypes/examples/exist.rkt b/macrotypes/examples/exist.rkt @@ -67,8 +67,5 @@ ;; #:with [e_packed- (~∃ (Y) τ_body)] (infer+erase #'e_packed) #:with τ_x (subst #'X #'Y #'τ_body) - #:with [(X-) (x-) (e-) (τ_e)] - (infer #'(e) - #:tvctx #'([X : #%type]) - #:ctx #`([x : τ_x])) + #:with [(_ x-) e- τ_e] (infer/ctx+erase #'(X [x : τ_x]) #'e) (⊢ (let- ([x- e_packed-]) e-) : τ_e)]) diff --git a/macrotypes/examples/fomega.rkt b/macrotypes/examples/fomega.rkt @@ -1,50 +1,30 @@ #lang s-exp macrotypes/typecheck -(extends "sysf.rkt" #:except #%datum ∀ ~∀ ∀? Λ inst) -(reuse String #%datum #:from "stlc+reco+var.rkt") +(reuse λ #%app Int → + #:from "sysf.rkt") +(reuse define-type-alias String #%datum #:from "stlc+reco+var.rkt") ;; System F_omega -;; Type relation: ;; Types: -;; - types from sysf.rkt -;; - String from stlc+reco+var +;; - redefine ∀ +;; - extend kind? and kind=? to include #%type +;; - extend sysf with tyλ and tyapp ;; Terms: -;; - extend ∀ Λ inst from sysf -;; - add tyλ and tyapp -;; - #%datum from stlc+reco+var +;; - extend sysf with Λ inst -(provide (for-syntax current-kind?) - define-type-alias - (type-out ★ ⇒ ∀★ ∀) - Λ inst tyλ tyapp) +(provide (type-out ∀) (kind-out ★ ⇒ ∀★ ∀) Λ inst tyλ tyapp) -(define-syntax-category kind) +(define-syntax-category :: kind) -; want #%type to be equiv to★ -; => edit current-kind? so existing #%type annotations (with no #%kind tag) -; are treated as kinds -; <= define ★ as rename-transformer expanding to #%type +;; 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)))) - ;; Try to keep "type?" backward compatible with its uses so far, - ;; eg in the definition of λ or previous type constuctors. - ;; (However, this is not completely possible, eg define-type-alias) - ;; So now "type?" no longer validates types, rather it's a subset. - ;; But we no longer need type? to validate types, instead we can use - ;; (kind? (typeof t)) - (current-type? (λ (t) - (define k (typeof t)) - #;(or (type? t) (★? (typeof t)) (∀★? (typeof t))) - (and ((current-kind?) k) (not (⇒? k)))))) - -; must override, to handle kinds -(define-syntax define-type-alias - (syntax-parser - [(_ alias:id τ) - #:with (τ- k_τ) (infer+erase #'τ) - #:fail-unless ((current-kind?) #'k_τ) - (format "not a valid type: ~a\n" (type->str #'τ)) - #'(define-syntax alias - (syntax-parser [x:id #'τ-][(_ . rst) #'(τ- . rst)]))])) + ;; well-formed types, ie not types with ⇒ kind + (current-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k) (not (⇒? k))))) + ;; any valid type (includes ⇒-kinded types) + (current-any-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k))))) (begin-for-syntax (define ★? #%type?) @@ -53,10 +33,10 @@ (define-kind-constructor ⇒ #:arity >= 1) (define-kind-constructor ∀★ #:arity >= 0) -(define-binding-type ∀ #:bvs >= 0 #:arr ∀★) +(define-binding-type ∀ #:arr ∀★) ;; alternative: normalize before type=? -; but then also need to normalize in current-promote +;; but then also need to normalize in current-promote? (begin-for-syntax (define (normalize τ) (syntax-parse τ #:literals (#%plain-app #%plain-lambda) @@ -83,44 +63,45 @@ (define old-type=? (current-type=?)) ; ty=? == syntax eq and syntax prop eq (define (type=? t1 t2) - (let ([k1 (typeof t1)][k2 (typeof t2)]) + (let ([k1 (kindof t1)][k2 (kindof t2)]) (and (or (and (not k1) (not k2)) - (and k1 k2 ((current-type=?) k1 k2))) + (and k1 k2 ((current-kind=?) k1 k2))) (old-type=? t1 t2)))) - (current-type=? type=?) - (current-typecheck-relation (current-type=?))) + (current-typecheck-relation type=?)) (define-typed-syntax Λ [(_ bvs:kind-ctx e) #:with ((tv- ...) e- τ_e) (infer/ctx+erase #'bvs #'e) - (⊢ e- : (∀ ([tv- : bvs.kind] ...) τ_e))]) + (⊢ e- : (∀ ([tv- :: bvs.kind] ...) τ_e))]) (define-typed-syntax inst - [(_ e τ ...) + [(_ e τ:any-type ...) #:with [e- τ_e] (infer+erase #'e) #:with (~∀ (tv ...) τ_body) #'τ_e - #:with (~∀★ k ...) (typeof #'τ_e) - #:with ([τ- k_τ] ...) (infers+erase #'(τ ...)) - #:fail-unless (typechecks? #'(k_τ ...) #'(k ...)) + #:with (~∀★ k ...) (kindof #'τ_e) +; #:with ([τ- k_τ] ...) (infers+erase #'(τ ...) #:tag '::) + #:with (k_τ ...) (stx-map kindof #'(τ.norm ...)) + #:fail-unless (kindchecks? #'(k_τ ...) #'(k ...)) (typecheck-fail-msg/multi #'(k ...) #'(k_τ ...) #'(τ ...)) - #:with τ_inst (substs #'(τ- ...) #'(tv ...) #'τ_body) + #:with τ_inst (substs #'(τ.norm ...) #'(tv ...) #'τ_body) (⊢ e- : τ_inst)]) ;; TODO: merge with regular λ and app? ;; - see fomega2.rkt (define-typed-syntax tyλ [(_ bvs:kind-ctx τ_body) - #:with (tvs- τ_body- k_body) (infer/ctx+erase #'bvs #'τ_body) + #:with (tvs- τ_body- k_body) (infer/ctx+erase #'bvs #'τ_body #:tag '::) #:fail-unless ((current-kind?) #'k_body) (format "not a valid type: ~a\n" (type->str #'τ_body)) - (⊢ (λ- tvs- τ_body-) : (⇒ bvs.kind ... k_body))]) + (assign-kind #'(λ- tvs- τ_body-) #'(⇒ bvs.kind ... k_body))]) (define-typed-syntax tyapp [(_ τ_fn τ_arg ...) - #:with [τ_fn- (k_in ... k_out)] (⇑ τ_fn as ⇒) - #:with ([τ_arg- k_arg] ...) (infers+erase #'(τ_arg ...)) - #:fail-unless (typechecks? #'(k_arg ...) #'(k_in ...)) +; #:with [τ_fn- (k_in ... k_out)] (⇑ τ_fn as ⇒) + #:with [τ_fn- (~⇒ k_in ... k_out)] (infer+erase #'τ_fn #:tag '::) + #:with ([τ_arg- k_arg] ...) (infers+erase #'(τ_arg ...) #:tag '::) + #:fail-unless (kindchecks? #'(k_arg ...) #'(k_in ...)) (string-append (format "~a (~a:~a) Arguments to function ~a have wrong kinds(s), " @@ -135,4 +116,4 @@ (format "Expected: ~a arguments with type(s): " (stx-length #'(k_in ...))) (string-join (stx-map type->str #'(k_in ...)) ", ")) - (⊢ (#%app- τ_fn- τ_arg- ...) : k_out)]) + (assign-kind #'(#%app- τ_fn- τ_arg- ...) #'k_out)]) diff --git a/macrotypes/examples/fomega2.rkt b/macrotypes/examples/fomega2.rkt @@ -1,8 +1,10 @@ #lang s-exp macrotypes/typecheck -(extends "sysf.rkt" #:except #%datum ∀ ~∀ ∀? Λ inst) -(reuse String #%datum #:from "stlc+reco+var.rkt") +(reuse Int + #:from "sysf.rkt") +(require (prefix-in sysf: (only-in "sysf.rkt" →- → #%app λ)) + (only-in "sysf.rkt" ~→ →?)) +(reuse define-type-alias String #%datum #:from "stlc+reco+var.rkt") -; same as fomega.rkt except here λ and #%app works as both type and terms +; same as fomega.rkt except λ and #%app works as both type and terms, ; - uses definition from stlc, but tweaks type? and kind? predicates ;; → is also both type and kind @@ -15,36 +17,35 @@ ;; - extend ∀ Λ inst from sysf ;; - #%datum from stlc+reco+var -(provide define-type-alias - ★ ∀★ ∀ - Λ inst) +(provide (kind-out ★ ∀★) (type-out ∀) → λ #%app Λ inst + (for-syntax current-kind-eval kindcheck?)) -(define-syntax-category kind) +(define-syntax-category :: kind) +;; modify predicates to recognize → (function type) as both type and kind (begin-for-syntax - (current-kind? (λ (k) (or (#%type? k) (kind? k) (#%type? (typeof k))))) - ;; Try to keep "type?" backward compatible with its uses so far, - ;; eg in the definition of λ or previous type constuctors. - ;; (However, this is not completely possible, eg define-type-alias) - ;; So now "type?" no longer validates types, rather it's a subset. - ;; But we no longer need type? to validate types, instead we can use - ;;(kind? (typeof t)) - (current-type? (λ (t) (or (type? t) - (let ([k (typeof t)]) - (or (★? k) (∀★? k))) - ((current-kind?) t))))) + (define old-kind? (current-kind?)) + (current-kind? (λ (k) (or (#%type? k) (old-kind? k)))) -; must override -(define-syntax define-type-alias - (syntax-parser - [(_ alias:id τ) - #:with (τ- k_τ) (infer+erase #'τ) - #'(define-syntax alias - (syntax-parser [x:id #'τ-][(_ . rst) #'(τ- . rst)]))])) + ;; well-formed types, eg not types with kind → + ;; must allow kinds as types, for → + (current-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k) (not (→? k))))) + + ;; o.w., a valid type is one with any valid kind + (current-any-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k))))) + +;; extend → to serve as both type and kind +(define-syntax (→ stx) + (syntax-parse stx + [(_ k:kind ...) ; kind + (add-orig (mk-kind #'(sysf:→- k.norm ...)) stx)] + [(_ . tys) #'(sysf:→ . tys)])) ; type (define-base-kind ★) (define-kind-constructor ∀★ #:arity >= 0) -(define-binding-type ∀ #:bvs >= 0 #:arr ∀★) +(define-binding-type ∀ #:arr ∀★) ;; alternative: normalize before type=? ; but then also need to normalize in current-promote @@ -70,32 +71,69 @@ (define (type-eval τ) (normalize (old-eval τ))) (current-type-eval type-eval) - (define old-type=? (current-type=?)) - (define (type=? t1 t2) - (or (and (★? t1) (#%type? t2)) - (and (#%type? t1) (★? t2)) - (and (syntax-parse (list t1 t2) #:datum-literals (:) - [((~∀ ([tv1 : k1]) tbody1) - (~∀ ([tv2 : k2]) tbody2)) - ((current-type=?) #'k1 #'k2)] - [_ #t]) - (old-type=? t1 t2)))) - (current-type=? type=?) - (current-typecheck-relation (current-type=?))) + (define old-typecheck? (current-typecheck-relation)) + (define (new-typecheck? t1 t2) + (and (kindcheck? (kindof t1) (kindof t2)) + (old-typecheck? t1 t2))) + (current-typecheck-relation new-typecheck?) + + ;; must be kind= (and not kindcheck?) since old-kind=? recurs on curr-kind= + (define old-kind=? (current-kind=?)) + (define (new-kind=? k1 k2) + (or (and (★? k1) (#%type? k2)) + (and (#%type? k1) (★? k2)) + (old-kind=? k1 k2))) + (current-kind=? new-kind=?) + (current-kindcheck-relation new-kind=?)) (define-typed-syntax Λ [(_ bvs:kind-ctx e) - #:with ((tv- ...) e- τ_e) - (infer/ctx+erase #'bvs #'e) - (⊢ e- : (∀ ([tv- : bvs.kind] ...) τ_e))]) + #:with ((tv- ...) e- τ_e) (infer/ctx+erase #'bvs #'e) + (⊢ e- : (∀ ([tv- :: bvs.kind] ...) τ_e))]) (define-typed-syntax inst - [(_ e τ ...) - #:with (e- (([tv k] ...) (τ_body))) (⇑ e as ∀) - #:with ([τ- k_τ] ...) (infers+erase #'(τ ...)) - #:when (stx-andmap - (λ (t k) (or ((current-kind?) k) - (type-error #:src t #:msg "not a valid type: ~a" t))) - #'(τ ...) #'(k_τ ...)) - #:when (typechecks? #'(k_τ ...) #'(k ...)) - (⊢ e- : #,(substs #'(τ- ...) #'(tv ...) #'τ_body))]) + [(_ e τ:any-type ...) +; #:with (e- (([tv k] ...) (τ_body))) (⇑ e as ∀) + #:with [e- τ_e] (infer+erase #'e) + #:with (~∀ (tv ...) τ_body) #'τ_e + #:with (~∀★ k ...) (kindof #'τ_e) +; #:with ([τ- k_τ] ...) (infers+erase #'(τ ...)) + #:with (k_τ ...) (stx-map kindof #'(τ.norm ...)) + #:fail-unless (kindchecks? #'(k_τ ...) #'(k ...)) + (typecheck-fail-msg/multi + #'(k ...) #'(k_τ ...) #'(τ ...)) + #:with τ_inst (substs #'(τ.norm ...) #'(tv ...) #'τ_body) + (⊢ e- : τ_inst)]) + +;; extend λ to also work as a type +(define-typed-syntax λ + [(_ bvs:kind-ctx τ) ; type + #:with (Xs- τ- k_res) (infer/ctx+erase #'bvs #'τ #:tag '::) + (assign-kind #'(λ- Xs- τ-) #'(→ bvs.kind ... k_res))] + [(_ . rst) #'(sysf:λ . rst)]) ; term + +;; extend #%app to also work as a type +(define-typed-syntax #%app + [(_ τ_fn τ_arg ...) ; type +; #:with [τ_fn- (k_in ... k_out)] (⇑ τ_fn as ⇒) + #:with [τ_fn- k_fn] (infer+erase #'τ_fn #:tag '::) + #:when (syntax-e #'k_fn) ; non-false + #:with (~→ k_in ... k_out ~!) #'k_fn + #:with ([τ_arg- k_arg] ...) (infers+erase #'(τ_arg ...) #:tag '::) + #:fail-unless (kindchecks? #'(k_arg ...) #'(k_in ...)) + (string-append + (format + "~a (~a:~a) Arguments to function ~a have wrong kinds(s), " + (syntax-source stx) (syntax-line stx) (syntax-column stx) + (syntax->datum #'τ_fn)) + "or wrong number of arguments:\nGiven:\n" + (string-join + (map (λ (e t) (format " ~a : ~a" e t)) ; indent each line + (syntax->datum #'(τ_arg ...)) + (stx-map type->str #'(k_arg ...))) + "\n" #:after-last "\n") + (format "Expected: ~a arguments with type(s): " + (stx-length #'(k_in ...))) + (string-join (stx-map type->str #'(k_in ...)) ", ")) + (assign-kind #'(#%app- τ_fn- τ_arg- ...) #'k_out)] + [(_ . rst) #'(sysf:#%app . rst)]) ; term diff --git a/macrotypes/examples/fomega3.rkt b/macrotypes/examples/fomega3.rkt @@ -1,6 +1,8 @@ #lang s-exp macrotypes/typecheck (extends "fomega.rkt" #:except tyapp tyλ) +;; NOTE 2017-02-03: currently not working + ; same as fomega2.rkt --- λ and #%app works as both regular and type versions, ; → is both type and kind --- but reuses parts of fomega.rkt, ; ie removes the duplication in fomega2.rkt diff --git a/macrotypes/examples/fsub.rkt b/macrotypes/examples/fsub.rkt @@ -26,7 +26,7 @@ (begin-for-syntax (define (expose t) (cond [(identifier? t) - (define sub (typeof t #:tag '<:)) + (define sub (detach t '<:)) (if sub (expose sub) t)] [else t])) (current-promote expose) @@ -75,14 +75,13 @@ #:msg "Expected ∀ type, got: ~a" #'any))))])))) (define-typed-syntax Λ #:datum-literals (<:) - [(_ ([tv:id <: τsub:type] ...) e) + [(_ ([X:id <: τsub:type] ...) e) ;; NOTE: store the subtyping relation of tv and τsub in another ;; "environment", ie, a syntax property with another tag: '<: ;; The "expose" function looks for this tag to enforce the bound, ;; as in TaPL (fig 28-1) - #:with ((tv- ...) _ (e-) (τ_e)) - (infer #'(e) #:tvctx #'([tv : #%type <: τsub] ...)) - (⊢ e- : (∀ ([tv- <: τsub] ...) τ_e))]) + #:with ((X- ...) e- τ_e) (infer/ctx #'([X :: #%type <: τsub] ...) #'e) + (⊢ e- : (∀ ([X- <: τsub] ...) τ_e))]) (define-typed-syntax inst [(_ e τ:type ...) #:with (e- (([tv τ_sub] ...) τ_body)) (⇑ e as ∀) diff --git a/macrotypes/examples/infer.rkt b/macrotypes/examples/infer.rkt @@ -51,7 +51,7 @@ (for/fold ([tv-id #'tv]) ([s (in-list (list 'sep ...))] [k (in-list (list #'tvk ...))]) - (assign-type tv-id k #:tag s)) + (attach tv-id s k)) 'tyvar #t))] ...) (λ- (x ...) (let-syntax diff --git a/macrotypes/examples/mlish+adhoc.rkt b/macrotypes/examples/mlish+adhoc.rkt @@ -383,7 +383,7 @@ (format "Improper use of constructor ~a; expected ~a args, got ~a" (syntax->datum #'Name) (stx-length #'(X ...)) (stx-length (stx-cdr #'stx))))])] - [X (make-rename-transformer (⊢ X #%type))] ...) + [X (make-rename-transformer (mk-type #'X))] ...) (void ty_flat ...))))) #:when (or (equal? '(unbound) (syntax->datum #'(ty+ ...))) (stx-map @@ -854,7 +854,7 @@ (expand/df #'(lambda (X ...) (let-syntax - ([X (make-rename-transformer (assign-type #'X #'#%type))] ...) + ([X (make-rename-transformer (mk-type #'X))] ...) (let-syntax ;; must have this inner macro bc body of lambda may require ;; ops defined by TC to be bound @@ -1670,7 +1670,7 @@ (~=> TCsub ... (~TC [generic-op-expected ty-concrete-op-expected] ...))) _) - (infers/tyctx+erase #'([X : #%type] ...) #'(TC ... (Name ty ...))) + (infers/tyctx+erase #'([X :: #%type] ...) #'(TC ... (Name ty ...))) #:when (TCs-exist? #'(TCsub ...) #:ctx stx) ;; simulate as if the declared concrete-op* has TC ... predicates ;; TODO: fix this manual deconstruction and assembly diff --git a/macrotypes/examples/mlish.rkt b/macrotypes/examples/mlish.rkt @@ -447,7 +447,7 @@ (format "Improper use of constructor ~a; expected ~a args, got ~a" (syntax->datum #'Name) (stx-length #'(X ...)) (stx-length (stx-cdr #'stx))))])] - [X (make-rename-transformer (⊢ X #%type))] ...) + [X (make-rename-transformer (mk-type #'X))] ...) (void ty_flat ...))))) #:when (or (equal? '(unbound) (syntax->datum #'(ty+ ...))) (stx-map diff --git a/macrotypes/examples/stlc+overloading.rkt b/macrotypes/examples/stlc+overloading.rkt @@ -106,7 +106,7 @@ (define-typed-syntax signature [(_ (name:id α:id) τ) - #:with ((α+) (~→ τ_α:id τ-cod) _) (infer/tyctx+erase #'([α : #%type]) #'τ) + #:with ((α+) (~→ τ_α:id τ-cod) _) (infer/tyctx+erase #'([α :: #%type]) #'τ) (define ℜ (ℜ-init #'name #'τ-cod)) (⊢ (define-syntax name (syntax-parser diff --git a/macrotypes/examples/sysf.rkt b/macrotypes/examples/sysf.rkt @@ -15,11 +15,10 @@ (define-typed-syntax Λ [(_ (tv:id ...) e) - #:with [(tv- ...) e- τ] (infer/tyctx+erase #'([tv : #%type] ...) #'e) - (⊢ e- : (∀ (tv- ...) τ))]) + #:with [tvs- e- τ-] (infer/ctx #'(tv ...) #'e) + (⊢ e- : (∀ tvs- τ-))]) (define-typed-syntax inst [(_ e τ:type ...) #:with [e- (~∀ tvs τ_body)] (infer+erase #'e) - #:with τ_inst (substs #'(τ.norm ...) #'tvs #'τ_body) - (⊢ e- : τ_inst)] + (⊢ e- : #,(substs #'(τ.norm ...) #'tvs #'τ_body))] [(_ e) #'e]) diff --git a/macrotypes/examples/tests/ext-stlc-tests.rkt b/macrotypes/examples/tests/ext-stlc-tests.rkt @@ -52,8 +52,8 @@ (typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier") (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") +(typecheck-fail (ann Bool : Int) + #:with-msg "ann: type mismatch: expected Int, given an invalid expression\n *expression: Bool") ; let (check-type (let () (+ 1 1)) : Int ⇒ 2) diff --git a/macrotypes/examples/tests/fomega-tests.rkt b/macrotypes/examples/tests/fomega-tests.rkt @@ -1,82 +1,86 @@ #lang s-exp "../fomega.rkt" (require "rackunit-typechecking.rkt") -(check-type Int : ★) -(check-type String : ★) +(check-type Int :: ★) +(check-type String :: ★) (typecheck-fail →) -(check-type (→ Int Int) : ★) +(check-type (→ Int Int) :: ★) (typecheck-fail (→ →)) (typecheck-fail (→ 1)) (check-type 1 : Int) -(typecheck-fail (tyλ ([x : ★]) 1) #:with-msg "not a valid type: 1") +(typecheck-fail (tyλ ([x :: ★]) 1) #:with-msg "not a valid type: 1") -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) -(check-not-type (Λ ([X : ★]) (λ ([x : X]) x)) : - (∀ ([X : (∀★ ★)]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) +(check-not-type (Λ ([X :: ★]) (λ ([x : X]) x)) : + (∀ ([X :: (∀★ ★)]) (→ X X))) ;(check-type (∀ ([t : ★]) (→ t t)) : ★) -(check-type (∀ ([t : ★]) (→ t t)) : (∀★ ★)) -(check-type (→ (∀ ([t : ★]) (→ t t)) (→ Int Int)) : ★) - -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) - -(check-type ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : ★]) (λ ([x : X]) x))) - : (∀ ([X : ★]) (→ X X))) -(typecheck-fail ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : (⇒ ★ ★)]) (λ ([x : X]) x)))) - -(check-type (tyλ ([t : ★]) t) : (⇒ ★ ★)) -(check-type (tyλ ([t : ★] [s : ★]) t) : (⇒ ★ ★ ★)) -(check-type (tyλ ([t : ★]) (tyλ ([s : ★]) t)) : (⇒ ★ (⇒ ★ ★))) -(check-type (tyλ ([t : (⇒ ★ ★)]) t) : (⇒ (⇒ ★ ★) (⇒ ★ ★))) -(check-type (tyλ ([t : (⇒ ★ ★ ★)]) t) : (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) -(check-type (tyλ ([arg : ★] [res : ★]) (→ arg res)) : (⇒ ★ ★ ★)) - -(check-type (tyapp (tyλ ([t : ★]) t) Int) : ★) -(check-type (λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) : (→ Int Int)) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) 1) : Int ⇒ 1) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) -(typecheck-fail ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) "a-string")) +(check-type (∀ ([t :: ★]) (→ t t)) :: (∀★ ★)) +(check-type (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int)) :: ★) + +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) + +(check-type ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) + (Λ ([X :: ★]) (λ ([x : X]) x))) + : (∀ ([X :: ★]) (→ X X))) +(typecheck-fail ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) + (Λ ([X :: (⇒ ★ ★)]) (λ ([x : X]) x)))) + +(check-type (tyλ ([t :: ★]) t) :: (⇒ ★ ★)) +(check-type (tyλ ([t :: ★] [s :: ★]) t) :: (⇒ ★ ★ ★)) +(check-type (tyλ ([t :: ★]) (tyλ ([s :: ★]) t)) :: (⇒ ★ (⇒ ★ ★))) +(check-type (tyλ ([t :: (⇒ ★ ★)]) t) :: (⇒ (⇒ ★ ★) (⇒ ★ ★))) +(check-type (tyλ ([t :: (⇒ ★ ★ ★)]) t) :: (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) +(check-type (tyλ ([arg :: ★] [res :: ★]) (→ arg res)) :: (⇒ ★ ★ ★)) + +(check-type (tyapp (tyλ ([t :: ★]) t) Int) :: ★) +(check-type (λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) x) : (→ Int Int)) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) x) 1) : Int ⇒ 1) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) +(typecheck-fail ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ 1 x)) "a-string")) ;; partial-apply → -(check-type (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int) - : (⇒ ★ ★)) +(check-type (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int) + :: (⇒ ★ ★)) ;; f's type must have kind ★ -(typecheck-fail (λ ([f : (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)]) f)) -(check-type (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : - (∀ ([tyf : (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) +(typecheck-fail (λ ([f :: (tyapp (tyλ([arg : ★]) (tyλ([res :: ★]) (→ arg res))) + Int)]) + f)) +(check-type (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : + (∀ ([tyf :: (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) (check-type (inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) : (→ (→ Int String) (→ Int String))) (typecheck-fail - (inst (Λ ([X : ★]) (λ ([x : X]) x)) 1) - #:with-msg "inst: type mismatch\n *expected: +★\n *given: +Int\n *expressions: 1") + (inst (Λ ([X :: ★]) (λ ([x : X]) x)) 1) + #:with-msg "inst:.*not a valid type: 1") (typecheck-fail - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") ;; applied f too early (typecheck-fail (inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") (check-type ((inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) : (→ Int String)) (check-type (((inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) 1) : String ⇒ "int") ;; tapl examples, p441 (typecheck-fail (define-type-alias tmp 1) #:with-msg "not a valid type: 1") -(define-type-alias Id (tyλ ([X : ★]) X)) +(define-type-alias Id (tyλ ([X :: ★]) X)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int String) Int)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int (tyapp Id String)) Int)) (check-type (λ ([f : (→ Int (tyapp Id String))]) 1) : (→ (→ Int String) Int)) @@ -89,104 +93,125 @@ (check-type (λ ([f : (tyapp Id (→ Int String))]) 1) : (→ (tyapp Id (tyapp Id (→ Int String))) Int)) ;; tapl examples, p451 -(define-type-alias Pair (tyλ ([A : ★] [B : ★]) (∀ ([X : ★]) (→ (→ A B X) X)))) +(define-type-alias Pair (tyλ ([A :: ★] [B :: ★]) (∀ ([X :: ★]) (→ (→ A B X) X)))) ;(check-type Pair : (⇒ ★ ★ ★)) -(check-type Pair : (⇒ ★ ★ (∀★ ★))) +(check-type Pair :: (⇒ ★ ★ (∀★ ★))) -(check-type (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X : ★][Y : ★]) (→ X Y X))) +(check-type (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) x)) + : (∀ ([X :: ★][Y :: ★]) (→ X Y X))) ; parametric pair constructor (check-type - (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) - : (∀ ([X : ★][Y : ★]) (→ X Y (tyapp Pair X Y)))) + (Λ ([X :: ★] [Y :: ★]) + (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) + : (∀ ([X :: ★][Y :: ★]) (→ X Y (tyapp Pair X Y)))) ; concrete Pair Int String constructor (check-type - (inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + (inst (Λ ([X :: ★] [Y :: ★]) + (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) : (→ Int String (tyapp Pair Int String))) ;; Pair Int String value (check-type - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) + (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1") : (tyapp Pair Int String)) ;; fst: parametric (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) - : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) X))) + (Λ ([X :: ★][Y :: ★]) + (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) + ((inst p X) (λ ([x : X][y : Y]) x)))) + : (∀ ([X :: ★][Y :: ★]) (→ (tyapp Pair X Y) X))) ;; fst: concrete Pair Int String accessor (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) + (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) + ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) : (→ (tyapp Pair Int String) Int)) ;; apply fst (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) + (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) + ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) + (λ ([x : X] [y : Y]) + (Λ ([R :: ★]) + (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : Int ⇒ 1) ;; snd (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) - : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) Y))) + (Λ ([X :: ★][Y :: ★]) + (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) + ((inst p Y) (λ ([x : X][y : Y]) y)))) + : (∀ ([X :: ★][Y :: ★]) (→ (tyapp Pair X Y) Y))) (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) + (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) + ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) : (→ (tyapp Pair Int String) String)) (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) + (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) + ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) + (λ ([x : X][y : Y]) + (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : String ⇒ "1") ;; sysf tests wont work, unless augmented with kinds -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X : ★]) (→ X X X))) ; true -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X : ★]) (→ X X X))) ; false -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y : ★]) (→ Y Y Y))) ; false, alpha equiv +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X :: ★]) (→ X X X))) ; true +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X :: ★]) (→ X X X))) ; false +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y :: ★]) (→ Y Y Y))) ; false, alpha equiv -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t1 : ★]) (∀ ([t2 : ★]) (→ t1 (→ t2 t2))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t1 :: ★]) (∀ ([t2 :: ★]) (→ t1 (→ t2 t2))))) -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t3 : ★]) (∀ ([t4 : ★]) (→ t3 (→ t4 t4))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t3 :: ★]) (∀ ([t4 :: ★]) (→ t3 (→ t4 t4))))) -(check-not-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t4 : ★]) (∀ ([t3 : ★]) (→ t3 (→ t4 t4))))) +(check-not-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t4 :: ★]) (∀ ([t3 :: ★]) (→ t3 (→ t4 t4))))) -(check-type (inst (Λ ([t : ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) -(check-type (inst (Λ ([t : ★]) 1) (→ Int Int)) : Int) +(check-type (inst (Λ ([t :: ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) +(check-type (inst (Λ ([t :: ★]) 1) (→ Int Int)) : Int) ; first inst should be discarded -(check-type (inst (inst (Λ ([t : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) +(check-type (inst (inst (Λ ([t :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) ; second inst is discarded -(check-type (inst (inst (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) +(check-type (inst (inst (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) ;; polymorphic arguments -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([t : ★]) (→ t t))) -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([s : ★]) (→ s s))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([s : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([s : ★]) (→ s s)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([u : ★]) (→ u u)))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) x) : (→ (∀ ([s : ★]) (→ s s)) (∀ ([u : ★]) (→ u u)))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([t :: ★]) (→ t t))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([s :: ★]) (→ s s))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([s :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([s :: ★]) (→ s s)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([u :: ★]) (→ u u)))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) x) : (→ (∀ ([s :: ★]) (→ s s)) (∀ ([u :: ★]) (→ u u)))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) (λ ([x : Int]) x))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) 1)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) : (∀ ([u : ★]) (→ u u))) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) : (∀ ([u :: ★]) (→ u u))) (check-type - (inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) + (inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) (check-type - ((inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) 10) + ((inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) 10) : Int ⇒ 10) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t : ★]) (→ t t)) (→ Int Int))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t : ★]) (→ t t)) Int)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) - (Λ ([s : ★]) (λ ([y : s]) y))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t :: ★]) (→ t t)) Int)) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) + (Λ ([s :: ★]) (λ ([y : s]) y))) : Int ⇒ 10) diff --git a/macrotypes/examples/tests/fomega2-tests.rkt b/macrotypes/examples/tests/fomega2-tests.rkt @@ -1,10 +1,11 @@ #lang s-exp "../fomega2.rkt" (require "rackunit-typechecking.rkt") +(require "rackunit-kindchecking.rkt") -(check-type Int : ★) -(check-type String : ★) +(check-kind Int :: ★) +(check-kind String :: ★) (typecheck-fail →) -(check-type (→ Int Int) : ★) +(check-kind (→ Int Int) :: ★) (typecheck-fail (→ →)) (typecheck-fail (→ 1)) (check-type 1 : Int) @@ -12,63 +13,64 @@ ;; this should error but it doesnt #;(λ ([x : ★]) 1) -;(check-type (∀ ([t : ★]) (→ t t)) : ★) -(check-type (∀ ([t : ★]) (→ t t)) : (∀★ ★)) -(check-type (→ (∀ ([t : ★]) (→ t t)) (→ Int Int)) : ★) +;(check-kind (∀ ([t :: ★]) (→ t t)) :: ★) +(check-kind (∀ ([t :: ★]) (→ t t)) :: (∀★ ★)) +(check-kind (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int)) :: ★) -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : ★]) (λ ([x : X]) x))) - : (∀ ([X : ★]) (→ X X))) -(typecheck-fail ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : (→ ★ ★)]) (λ ([x : X]) x)))) +(check-type ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X :: ★]) (λ ([x : X]) x))) + : (∀ ([X :: ★]) (→ X X))) +(typecheck-fail ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X :: (→ ★ ★)]) (λ ([x : X]) x)))) -(check-type (λ ([t : ★]) t) : (→ ★ ★)) -(check-type (λ ([t : ★] [s : ★]) t) : (→ ★ ★ ★)) -(check-type (λ ([t : ★]) (λ ([s : ★]) t)) : (→ ★ (→ ★ ★))) -(check-type (λ ([t : (→ ★ ★)]) t) : (→ (→ ★ ★) (→ ★ ★))) -(check-type (λ ([t : (→ ★ ★ ★)]) t) : (→ (→ ★ ★ ★) (→ ★ ★ ★))) -(check-type (λ ([arg : ★] [res : ★]) (→ arg res)) : (→ ★ ★ ★)) +;; λ as a type +(check-kind (λ ([t :: ★]) t) :: (→ ★ ★)) +(check-kind (λ ([t :: ★] [s :: ★]) t) :: (→ ★ ★ ★)) +(check-kind (λ ([t :: ★]) (λ ([s :: ★]) t)) :: (→ ★ (→ ★ ★))) +(check-kind (λ ([t :: (→ ★ ★)]) t) :: (→ (→ ★ ★) (→ ★ ★))) +(check-kind (λ ([t :: (→ ★ ★ ★)]) t) :: (→ (→ ★ ★ ★) (→ ★ ★ ★))) +(check-kind (λ ([arg :: ★] [res :: ★]) (→ arg res)) :: (→ ★ ★ ★)) -(check-type ((λ ([t : ★]) t) Int) : ★) -(check-type (λ ([x : ((λ ([t : ★]) t) Int)]) x) : (→ Int Int)) -(check-type ((λ ([x : ((λ ([t : ★]) t) Int)]) x) 1) : Int ⇒ 1) -(check-type ((λ ([x : ((λ ([t : ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) -(check-type ((λ ([x : ((λ ([t : ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) -(typecheck-fail ((λ ([x : ((λ ([t : ★]) t) Int)]) (+ 1 x)) "a-string")) +(check-kind ((λ ([t :: ★]) t) Int) :: ★) +(check-type (λ ([x : ((λ ([t :: ★]) t) Int)]) x) : (→ Int Int)) +(check-type ((λ ([x : ((λ ([t :: ★]) t) Int)]) x) 1) : Int ⇒ 1) +(check-type ((λ ([x : ((λ ([t :: ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) +(check-type ((λ ([x : ((λ ([t :: ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) +(typecheck-fail ((λ ([x : ((λ ([t :: ★]) t) Int)]) (+ 1 x)) "a-string")) ;; partial-apply → -(check-type ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int) - : (→ ★ ★)) +(check-kind ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int) + :: (→ ★ ★)) ; f's type must have kind ★ -(typecheck-fail (λ ([f : ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)]) f)) -(check-type (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) : - (∀ ([tyf : (→ ★ ★)]) (→ (tyf String) (tyf String)))) +(typecheck-fail (λ ([f : ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)]) f)) +(check-type (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) : + (∀ ([tyf :: (→ ★ ★)]) (→ (tyf String) (tyf String)))) (check-type (inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)) : (→ (→ Int String) (→ Int String))) (typecheck-fail - (inst (Λ ([X : ★]) (λ ([x : X]) x)) 1)) + (inst (Λ ([X :: ★]) (λ ([x :: X]) x)) 1)) ;#:with-msg "not a valid type: 1") ;; applied f too early (typecheck-fail (inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) (f 1))) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int))) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) (f 1))) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int))) (check-type ((inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) : (→ Int String)) (check-type (((inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) 1) : String ⇒ "int") ;; tapl examples, p441 (typecheck-fail (define-type-alias tmp 1)) ;#:with-msg "not a valid type: 1") -(define-type-alias Id (λ ([X : ★]) X)) +(define-type-alias Id (λ ([X :: ★]) X)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int String) Int)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int (Id String)) Int)) (check-type (λ ([f : (→ Int (Id String))]) 1) : (→ (→ Int String) Int)) @@ -81,104 +83,105 @@ (check-type (λ ([f : (Id (→ Int String))]) 1) : (→ (Id (Id (→ Int String))) Int)) ;; tapl examples, p451 -(define-type-alias Pair (λ ([A : ★] [B : ★]) (∀ ([X : ★]) (→ (→ A B X) X)))) +(define-type-alias Pair (λ ([A :: ★] [B :: ★]) (∀ ([X :: ★]) (→ (→ A B X) X)))) ;(check-type Pair : (→ ★ ★ ★)) -(check-type Pair : (→ ★ ★ (∀★ ★))) +(check-kind Pair :: (→ ★ ★ (∀★ ★))) -(check-type (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X : ★][Y : ★]) (→ X Y X))) +(check-type (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) x)) + : (∀ ([X :: ★][Y :: ★]) (→ X Y X))) ; parametric pair constructor (check-type - (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) - : (∀ ([X : ★][Y : ★]) (→ X Y (Pair X Y)))) + (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) + : (∀ ([X :: ★][Y :: ★]) (→ X Y (Pair X Y)))) ; concrete Pair Int String constructor (check-type - (inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + (inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) : (→ Int String (Pair Int String))) ; Pair Int String value (check-type - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1") : (Pair Int String)) ; fst: parametric (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) - : (∀ ([X : ★][Y : ★]) (→ (Pair X Y) X))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + : (∀ ([X :: ★][Y :: ★]) (→ (Pair X Y) X))) ; fst: concrete Pair Int String accessor (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) : (→ (Pair Int String) Int)) ; apply fst (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : Int ⇒ 1) ; snd (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) - : (∀ ([X : ★][Y : ★]) (→ (Pair X Y) Y))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + : (∀ ([X :: ★][Y :: ★]) (→ (Pair X Y) Y))) (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) : (→ (Pair Int String) String)) (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : String ⇒ "1") ;;; sysf tests wont work, unless augmented with kinds -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X : ★]) (→ X X X))) ; true -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X : ★]) (→ X X X))) ; false -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y : ★]) (→ Y Y Y))) ; false, alpha equiv +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X :: ★]) (→ X X X))) ; true +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X :: ★]) (→ X X X))) ; false +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y :: ★]) (→ Y Y Y))) ; false, alpha equiv -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t1 : ★]) (∀ ([t2 : ★]) (→ t1 (→ t2 t2))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t1 :: ★]) (∀ ([t2 :: ★]) (→ t1 (→ t2 t2))))) -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t3 : ★]) (∀ ([t4 : ★]) (→ t3 (→ t4 t4))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t3 :: ★]) (∀ ([t4 :: ★]) (→ t3 (→ t4 t4))))) -(check-not-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t4 : ★]) (∀ ([t3 : ★]) (→ t3 (→ t4 t4))))) +(check-not-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t4 :: ★]) (∀ ([t3 :: ★]) (→ t3 (→ t4 t4))))) -(check-type (inst (Λ ([t : ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) -(check-type (inst (Λ ([t : ★]) 1) (→ Int Int)) : Int) +(check-type (inst (Λ ([t :: ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) +(check-type (inst (Λ ([t :: ★]) 1) (→ Int Int)) : Int) ; first inst should be discarded -(check-type (inst (inst (Λ ([t : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) +(check-type (inst (inst (Λ ([t :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) ; second inst is discarded -(check-type (inst (inst (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) +(check-type (inst (inst (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) ;; polymorphic arguments -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([t : ★]) (→ t t))) -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([s : ★]) (→ s s))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([s : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([s : ★]) (→ s s)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([u : ★]) (→ u u)))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) x) : (→ (∀ ([s : ★]) (→ s s)) (∀ ([u : ★]) (→ u u)))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([t :: ★]) (→ t t))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([s :: ★]) (→ s s))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([s :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([s :: ★]) (→ s s)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([u :: ★]) (→ u u)))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) x) : (→ (∀ ([s :: ★]) (→ s s)) (∀ ([u :: ★]) (→ u u)))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) (λ ([x : Int]) x))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) 1)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) : (∀ ([u : ★]) (→ u u))) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) : (∀ ([u :: ★]) (→ u u))) (check-type - (inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) + (inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) (check-type - ((inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) 10) + ((inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) 10) : Int ⇒ 10) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t : ★]) (→ t t)) (→ Int Int))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t : ★]) (→ t t)) Int)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) - (Λ ([s : ★]) (λ ([y : s]) y))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t :: ★]) (→ t t)) Int)) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) + (Λ ([s :: ★]) (λ ([y : s]) y))) : Int ⇒ 10) diff --git a/macrotypes/examples/tests/general-tests.rkt b/macrotypes/examples/tests/general-tests.rkt @@ -15,9 +15,9 @@ (define-type-constructor -> #:arity > 0) (define-binding-type mu #:arity = 1 #:bvs = 1) (define-binding-type forall #:bvs = 1 #:arity = 1) - (define-binding-type exist #:no-attach-kind #:bvs = 1 #:arity = 1) - (define-binding-type exist2 #:bvs = 1 #:arity = 1 #:no-attach-kind) - (define-binding-type exist3 #:bvs = 1 #:no-attach-kind #:arity = 1) + (define-binding-type exist #:arr void #:bvs = 1 #:arity = 1) + (define-binding-type exist2 #:bvs = 1 #:arity = 1 #:arr void) + (define-binding-type exist3 #:bvs = 1 #:arr void #:arity = 1) (check-stx-err (define-binding-type exist4 #:bvs = 1 #:no-attach- #:arity = 1) @@ -32,5 +32,11 @@ #:with-msg "expected more terms") (check-stx-err (define-binding-type exist6 #:bvs = 1 #:bvs = 1) - #:with-msg "bad syntax") ; TODO: how to improve this? + #:with-msg "too many occurrences of #:bvs keyword") + (check-stx-err + (define-binding-type exist6 #:arity = 1 #:arity = 1) + #:with-msg "too many occurrences of #:arity keyword") + (check-stx-err + (define-binding-type exist6 #:arr void #:arr void) + #:with-msg "too many occurrences of #:arr keyword") ) diff --git a/macrotypes/examples/tests/infer-tests.rkt b/macrotypes/examples/tests/infer-tests.rkt @@ -246,7 +246,7 @@ (typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier") (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") +(typecheck-fail (ann Bool : Int) #:with-msg "expected Int, given an invalid expression\n *expression: Bool") ; let (check-type (let () (+ 1 1)) : Int ⇒ 2) diff --git a/macrotypes/examples/tests/mlish-tests.rkt b/macrotypes/examples/tests/mlish-tests.rkt @@ -64,6 +64,8 @@ (check-type (g2 Nil) : (List (List Int)) ⇒ Nil) (check-type (g2 Nil) : (List (→ Int Int)) ⇒ Nil) +(check-type (λ ([x : (List Int)]) x) : (→/test (List Int) (List Int))) + (check-type (g2 (Cons 1 Nil)) : (List Int) ⇒ (Cons 1 Nil)) (check-type (g2 (Cons "1" Nil)) : (List String) ⇒ (Cons "1" Nil)) @@ -675,7 +677,7 @@ (typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier") (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") +(typecheck-fail (ann Int : Int) #:with-msg "expected Int, given an invalid expression\n *expression: Int") ; let (check-type (let () (+ 1 1)) : Int ⇒ 2) diff --git a/macrotypes/examples/tests/rackunit-kindchecking.rkt b/macrotypes/examples/tests/rackunit-kindchecking.rkt @@ -0,0 +1,16 @@ +#lang racket/base +(require (for-syntax rackunit syntax/srcloc) rackunit macrotypes/typecheck + (only-in "../fomega2.rkt" current-kind-eval kindcheck?)) +(provide check-kind) + +(define-syntax (check-kind stx) + (syntax-parse stx #:datum-literals (⇒ ->) + [(_ τ tag:id k-expected) + #:with k (detach (expand/df #'(add-expected τ k-expected)) + (stx->datum #'tag)) + #:fail-unless (kindcheck? #'k ((current-kind-eval) #'k-expected)) + (format + "Type ~a [loc ~a:~a] has kind ~a, expected ~a" + (syntax->datum #'τ) (syntax-line #'τ) (syntax-column #'τ) + (type->str #'k) (type->str #'k-expected)) + #'(void)])) diff --git a/macrotypes/info.rkt b/macrotypes/info.rkt @@ -1,13 +1,16 @@ #lang info (define compile-omit-paths - '("examples/tests")) + '("examples/fomega3.rkt" + "examples/tests")) (define test-include-paths '("examples/tests/mlish")) ; to include .mlish files (define test-omit-paths '("examples/tests/mlish/sweet-map.rkt" ; needs sweet-exp + "examples/fomega3.rkt" + "examples/tests/fomega3-tests.rkt" "examples/tests/mlish/bg/README.md")) (define test-timeouts diff --git a/macrotypes/stx-utils.rkt b/macrotypes/stx-utils.rkt @@ -1,11 +1,17 @@ #lang racket/base -(require syntax/stx syntax/parse racket/list racket/format version/utils) +(require syntax/stx syntax/parse syntax/parse/define + racket/list racket/format version/utils) (provide (all-defined-out)) +;; shorthands +(define id? identifier?) +(define free-id=? free-identifier=?) + (define (stx-cadr stx) (stx-car (stx-cdr stx))) (define (stx-caddr stx) (stx-cadr (stx-cdr stx))) (define (stx-cddr stx) (stx-cdr (stx-cdr stx))) +(define datum->stx datum->syntax) (define (stx->datum stx) (if (syntax? stx) (syntax->datum stx) @@ -48,6 +54,7 @@ (define (stx-list-ref stx i) (list-ref (stx->list stx) i)) +(define-simple-macro (in-stx-list stx) (in-list (stx->list stx))) (define (stx-str=? s1 s2) (string=? (syntax-e s1) (syntax-e s2))) @@ -81,6 +88,27 @@ (define (generate-temporariesss stx) (stx-map generate-temporariess stx)) +;; stx prop helpers + +;; ca*r : Any -> Any +(define (ca*r v) + (if (cons? v) (ca*r (car v)) v)) +;; cd*r : Any -> Any +(define (cd*r v) + (if (cons? v) (cd*r (cdr v)) v)) + +;; get-stx-prop/ca*r : Syntax Key -> Val +;; Retrieves Val at Key stx prop on Stx. +;; If Val is a non-empty list, continue down head until non-list. +(define (get-stx-prop/ca*r stx tag) + (ca*r (syntax-property stx tag))) + +;; get-stx-prop/cd*r : Syntax Key -> Val +(define (get-stx-prop/cd*r stx tag) + (cd*r (syntax-property stx tag))) + + + ;; transfers properties and src loc from orig to new (define (transfer-stx-props new orig #:ctx [ctx new]) (datum->syntax ctx (syntax-e new) orig orig)) diff --git a/macrotypes/type-constraints.rkt b/macrotypes/type-constraints.rkt @@ -161,7 +161,7 @@ ;; lookup-Xs/keep-unsolved : (Stx-Listof Id) Constraints -> (Listof Type-Stx) ;; looks up each X in the constraints, returning the X if it's unconstrained (define (lookup-Xs/keep-unsolved Xs cs) - (for/list ([X (in-list (stx->list Xs))]) + (for/list ([X (in-stx-list Xs)]) (or (lookup X cs) X))) ;; instantiate polymorphic types diff --git a/macrotypes/typecheck.rkt b/macrotypes/typecheck.rkt @@ -42,10 +42,11 @@ ;; - To typecheck a surface form, it local-expands each subterm in order to get ;; their types. ;; - With this typechecking strategy, the typechecking implementation machinery -;; is easily inserted into each #%- form +;; is easily inserted into each #%XYZ form ;; - A base type is just a Racket identifier, so type equality, even with ;; aliasing, is just free-identifier=? ;; - type constructors are prefix +;; - use different stx prop keys for different metadata, eg ':: for kinds ;; redefine #%module-begin to add some provides (provide (rename-out [mb #%module-begin])) @@ -54,7 +55,8 @@ [(_ . stuff) (syntax/loc this-syntax (#%module-begin - (provide #%module-begin #%top-interaction #%top require only-in) ; useful racket forms + ; auto-provide some useful racket forms + (provide #%module-begin #%top-interaction #%top require only-in) . stuff))])) (struct exn:fail:type:runtime exn:fail:user ()) @@ -64,6 +66,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 (mkx2 id) (format-id id "~a~a" id 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)) @@ -75,6 +78,9 @@ (path->string (path-replace-suffix (file-name-from-path f) ""))) (define-syntax-parameter stx (syntax-rules ()))) +;; non-Turnstile define-typed-syntax +;; TODO: potentially confusing? get rid of this? +;; - but it will be annoying since the `stx` stx-param is used everywhere (define-syntax (define-typed-syntax stx) (syntax-parse stx [(_ name:id stx-parse-clause ...+) @@ -184,60 +190,603 @@ (set-stx-prop/preserved e 'expected-type ((current-type-eval) ty)) e)) -;; type assignment (begin-for-syntax - ;; Type assignment macro for nicer syntax + ;; Helper functions for attaching/detaching types, kinds, etc. + + ;; A Tag is a Symbol serving as a stx prop key for some kind of metadata. + ;; e.g., ': for types, ':: for kinds, etc. + ;; Define new metadata via `define-syntax-category` + + ;; attach : Stx Tag Val -> Stx + ;; Adds Tag+Val to Stx as stx prop, returns new Stx. + ;; e.g., Stx = expression, Tag = ':, Val = Type stx + (define (attach stx tag v) + (set-stx-prop/preserved stx tag v)) + (define (attachs stx tags vs #:ev [ev (λ (x) x)]) + (for/fold ([stx stx]) ([t (in-list tags)] [v (in-stx-list vs)]) + (attach stx t (ev v)))) + ;; detach : Stx Tag -> Val + ;; Retrieves Val at Tag stx prop on Stx. + ;; If Val is a non-empty list, return first element, otherwise return Val. + ;; e.g., Stx = expression, Tag = ':, Val = Type stx + (define (detach stx tag) + (get-stx-prop/ca*r stx tag))) + +;; ---------------------------------------------------------------------------- +;; ---------------------------------------------------------------------------- +;; define-syntax-category ----------------------------------------------------- +;; ---------------------------------------------------------------------------- +;; ---------------------------------------------------------------------------- + +;; This is a huge macro. +;; Defines a new type of metadata on syntax, e.g. types, and functions +;; and macros for manipulating the metadata, e.g. define-base-type, type=?, etc + +;; A syntax category requires a name and two keys, +;; - one to use when attaching values of this category (eg ': for types) +;; - another for attaching "types" to these values (eg ':: for kinds on types) +;; If key1 is unspecified, the default is ': +;; If key2 is unspecified, the default is "twice" key1 (ie '::) +;; +;; example uses: +;; (define-syntax-category type) +;; (define-syntax-category : type) +;; (define-syntax-category : type ::) +;; (define-syntax-category :: kind :::) +;; +;; CODE NOTE: +;; To make this large macros-defining macro easier to read, +;; I use a `type` pat var corresponding to the category name, +;; and a `kind` pat var for its "type". +;; But `name` could correspond to any kind of metadata, +;; e.g., kinds, src locs, polymorphic bounds +(define-syntax (define-syntax-category stx) + (syntax-parse stx + [(_ name:id) ; default key1 = ': for types + #'(define-syntax-category : name)] + [(_ key:id name:id) ; default key2 = ':: for kinds + #`(define-syntax-category key name #,(mkx2 #'key))] + [(_ key1:id name:id key2:id) + ;; syntax classes + #:with type #'name ; dangerous? check `type` not used in binding pos below + #:with any-type (format-id #'name "any-~a" #'name) + #:with type-ctx (format-id #'name "~a-ctx" #'name) + #:with type-bind (format-id #'name "~a-bind" #'name) + #:with type-ann (format-id #'name "~a-ann" #'name) + ;; type well-formedness + #:with #%tag (mk-#% #'name) ; default "type" for this metadata, e.g. #%type + #:with #%tag? (mk-? #'#%tag) + #:with mk-type (format-id #'name "mk-~a" #'name) + #:with type? (mk-? #'name) + #:with any-type? (mk-? #'any-type) + #:with current-type? (mk-param #'type?) + #:with current-any-type? (mk-param #'any-type?) + ;; assigning and retrieving types + #:with type-key1 (format-id #'name "~a-key1" #'name) + #:with type-key2 (format-id #'name "~a-key2" #'name) + #:with assign-type (format-id #'name "assign-~a" #'name) + #:with fast-assign-type (format-id #'name "fast-assign-~a" #'name) + #:with typeof (format-id #'name "~aof" #'name) + #:with tagoftype (format-id #'name "tagof~a" #'name) + ;; type checking + #:with current-typecheck-relation (format-id #'name "current-~acheck-relation" #'name) + #:with typecheck? (format-id #'name "~acheck?" #'name) + #:with typechecks? (format-id #'name "~achecks?" #'name) + #:with type=? (format-id #'name "~a=?" #'name) + #:with types=? (format-id #'name "~as=?" #'name) + #:with current-type=? (mk-param #'type=?) + #:with same-types? (format-id #'name "same-~as?" #'name) + #:with current-type-eval (format-id #'name "current-~a-eval" #'name) + #:with default-type-eval (format-id #'name "default-~a-eval" #'name) + #:with type-evals (format-id #'name "~a-evals" #'name) + ;; defining types + #:with define-base-type (format-id #'name "define-base-~a" #'name) + #:with define-base-types (format-id #'name "define-base-~as" #'name) + #:with define-internal-type-constructor (format-id #'name "define-internal-~a-constructor" #'name) + #:with define-type-constructor (format-id #'name "define-~a-constructor" #'name) + #:with define-internal-binding-type (format-id #'name "define-internal-binding-~a" #'name) + #:with define-binding-type (format-id #'name "define-binding-~a" #'name) + #:with type-out (format-id #'name "~a-out" #'name) + #'(begin + (define #%tag void) ; TODO: cache expanded #%tag? + (begin-for-syntax + ;; type-wellformedness --------------------------------------------- + (define (#%tag? t) (and (id? t) (free-id=? t #'#%tag))) + (define (mk-type t) (attach t 'key2 #'#%tag)) + ;; type? corresponds to "well-formed" types + (define (type? t) (#%tag? (tagoftype t))) + (define current-type? (make-parameter type?)) + ;; any-type? corresponds to any type, defaults to type? + (define (any-type? t) (type? t)) + (define current-any-type? (make-parameter any-type?)) + ;; assigning and retrieving types ---------------------------------- + (define (type-key1) 'key1) + (define (type-key2) 'key2) + (define (typeof stx) (detach stx 'key1)) + (define (tagoftype stx) (detach stx 'key2)) ; = kindof if kind stx-cat defined + (define (fast-assign-type e τ) ; TODO: does this actually help? + (attach e 'key1 (syntax-local-introduce τ))) + (define (assign-type e τ) + (fast-assign-type e ((current-type-eval) τ))) + ;; helper stx classes ---------------------------------------------- + (define-syntax-class type ;; e.g., well-formed types + #:attributes (norm) + (pattern τ + #:with norm ((current-type-eval) #'τ) + #:fail-unless ((current-type?) #'norm) + (format "~a (~a:~a) not a well-formed ~a: ~a" + (syntax-source #'τ) (syntax-line #'τ) (syntax-column #'τ) + 'name (type->str #'τ)))) + (define-syntax-class any-type ;; e.g., any valid type + #:attributes (norm) + (pattern τ + #:with norm ((current-type-eval) #'τ) + #:fail-unless ((current-any-type?) #'norm) + (format "~a (~a:~a) not a valid ~a: ~a" + (syntax-source #'τ) (syntax-line #'τ) (syntax-column #'τ) + 'name (type->str #'τ)))) + (define-syntax-class type-bind #:datum-literals (key1) + #:attributes (x type) + (pattern [x:id key1 ~! (~var ty type)] + #:attr type #'ty.norm) + (pattern any + #:fail-when #t + (format + (string-append + "Improperly formatted ~a annotation: ~a; should have shape [x ~a τ], " + "where τ is a valid ~a.") + 'name (type->str #'any) 'key1 'name) + #:attr x #f #:attr type #f)) + (define-syntax-class type-ctx + #:attributes ((x 1) (type 1)) + (pattern ((~var || type-bind) (... ...)))) + (define-syntax-class type-ann ; type instantiation + #:attributes (norm) + (pattern (~and (_) + (~fail #:unless (brace? this-syntax)) + ((~var t type) ~!)) + #:attr norm (delay #'t.norm)) + (pattern any + #:fail-when #t + (format + (string-append + "Improperly formatted ~a annotation: ~a; should have shape {τ}, " + "where τ is a valid ~a.") + 'name (type->str #'any) 'name) + #:attr norm #f)) + ;; checking types + (define (type=? t1 t2) + ;; (printf "(τ=) t1 = ~a\n" #;τ1 (stx->datum t1)) + ;; (printf "(τ=) t2 = ~a\n" #;τ2 (stx->datum t2)) + (or (and (id? t1) (id? t2) (free-id=? t1 t2)) + (and (stx-null? t1) (stx-null? t2)) + (syntax-parse (list t1 t2) ; handle binding types + [(((~literal #%plain-lambda) (~and (_:id (... ...)) xs) . ts1) + ((~literal #%plain-lambda) (~and (_:id (... ...)) ys) . ts2)) + (and (stx-length=? #'xs #'ys) + (stx-length=? #'ts1 #'ts2) + (stx-andmap + (λ (ty1 ty2) + ((current-type=?) (substs #'ys #'xs ty1) ty2)) + #'ts1 #'ts2))] + [_ (and (stx-pair? t1) (stx-pair? t2) + (types=? t1 t2))]))) + (define current-type=? (make-parameter type=?)) + (define (types=? τs1 τs2) + (and (stx-length=? τs1 τs2) + (stx-andmap (current-type=?) τs1 τs2))) + ; extra indirection, enables easily overriding type=? with eg sub? + ; to add subtyping, without changing any other definitions + (define current-typecheck-relation (make-parameter type=?)) + ;; 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 (same-types? τs) + (define τs-lst (syntax->list τs)) + (or (null? τs-lst) + (andmap (λ (τ) ((current-type=?) (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-type-eval τ) + ; TODO: optimization: don't expand if expanded + ; - but this causes problems when combining unexpanded and + ; expanded types to create new types + ; - alternative: use syntax-local-expand-expression? + (add-orig (expand/df τ) τ)) + (define current-type-eval (make-parameter default-type-eval)) + (define (type-evals τs) #`#,(stx-map (current-type-eval) τs))) + ;; defining types ---------------------------------------------------- + (define-syntax type-out ;; helps with providing defined types + (make-provide-transformer + (lambda (stx modes) + (syntax-parse stx + ;; cannot write ty:type bc provides might precede type def + [(_ . ts) + #:with t-expanders (stx-map mk-~ #'ts) + #:with t?s (stx-map mk-? #'ts) + #:with t-s (filter identifier-binding (stx-map mk-- #'ts)) + (expand-export + (syntax/loc stx (combine-out + (combine-out . ts) (combine-out . t-s) + (for-syntax (combine-out . t-expanders) . t?s))) + modes)])))) + ;; base types -------------------------------------------------------- + (define-syntax define-base-type + (syntax-parser ; default = 'key2 + #%tag + [(_ (~var τ id)) #'(define-base-type τ key2 #%tag)] + [(_ (~var τ id) new-key2 new-#%tag) + #:with τ? (mk-? #'τ) + #:with τ-expander (mk-~ #'τ) + #:with τ-internal (generate-temporary #'τ) + #`(begin + (begin-for-syntax + (define (τ? t) + (syntax-parse t + [((~literal #%plain-app) (~literal τ-internal)) #t] + [_ #f])) + (define-syntax τ-expander + (pattern-expander + (syntax-parser + [(~var _ id) + #'((~literal #%plain-app) (~literal τ-internal))] + ; - this case used by ⇑, TODO: remove this case? + ; - but it's also needed when matching a list of types, + ; e.g., in stlc+sub (~Nat τ) + [(_ . rst) + #'(((~literal #%plain-app) (~literal τ-internal)) . rst)])))) + (define τ-internal + (λ () (raise (exn:fail:type:runtime + (format "~a: Cannot use ~a at run time" 'τ 'tag) + (current-continuation-marks))))) + (define-syntax τ + (syntax-parser + [(~var _ id) + (add-orig + (attach + (syntax/loc this-syntax (τ-internal)) + 'new-key2 (expand/df #'new-#%tag)) + #'τ)])))])) + (define-syntax define-base-types + (syntax-parser + [(_ (~var x id) (... ...)) + #'(begin (define-base-type x) (... ...))])) + ;; type constructors ------------------------------------------------- + ;; internal-type-constructor defines: + ;; - internal id identifying the constructor + ;; - predicate recognizing the internal id + ;; - expanded shape of type + ;; - pattern expander recognizing the shape + ;; - internal contructor τ- + ;; internal-type-constructor does no checks: + ;; - does not check arity + ;; - does not check that inputs are valid types + ;; - does not attach a kind to itself + (define-syntax define-internal-type-constructor + (syntax-parser + [(_ (~var τ id) + (~or + (~optional ; variances + (~seq #:arg-variances arg-variances-stx:expr) + #:name "#:arg-variances keyword" + #:defaults + ([arg-variances-stx + #`(λ (stx-id) + (for/list ([_ (in-stx-list (stx-cdr stx-id))]) + invariant))])) + (~optional ; extra-info + (~seq #:extra-info extra-info) + #:name "#:extra-info keyword" + #:defaults ([extra-info #'void]))) (... ...)) + #:with τ? (mk-? #'τ) + #:with τ- (mk-- #'τ) + #:with τ-expander (mk-~ #'τ) + #:with τ-internal (generate-temporary #'τ) + #`(begin + (begin-for-syntax + (define (τ? t) + (syntax-parse t + [(~Any (~literal τ-internal) . _) #t] + [_ #f])) + (define-syntax τ-expander + (pattern-expander + (syntax-parser + [(_ . pat) + #:with expanded-τ (generate-temporary) + #'(~and expanded-τ + (~Any + (~literal/else τ-internal + (format "Expected ~a ~a, got: ~a" + 'τ 'name (type->str #'expanded-τ)) + #'expanded-τ) + . pat))]))) + (define arg-vars arg-variances-stx)) + (define τ-internal + (λ _ (raise (exn:fail:type:runtime + (format "~a: Cannot use ~a at run time" 'τ 'name) + (current-continuation-marks))))) + ; τ- is an internal constructor: + ; It does not validate inputs and does not attach a kind, + ; ie, it won't be recognized as a valid type, the programmer + ; must implement their own kind system on top + (define-syntax (τ- stx) + (syntax-parse stx + [(_ . args) + #:with τ* (add-arg-variances #'τ-internal (arg-vars stx)) + (syntax/loc stx + (τ* (λ () (#%expression extra-info) . args)))])))])) + (define-syntax define-type-constructor + (syntax-parser + [(_ (~var τ id) + ;; TODO: allow any order of kws between τ and τ- + (~optional ; arity + (~seq #:arity op n:exact-nonnegative-integer) + #:defaults ([op #'=] [n #'1])) + . (~and other-options (~not (#:arity . _)))) + #:with τ- (mk-- #'τ) + #'(begin + (define-internal-type-constructor τ . other-options) + (define-syntax (τ stx) + (syntax-parse stx + [(_ . args) + #:fail-unless (op (stx-length #'args) n) + (format + "wrong number of arguments, expected ~a ~a" + 'op 'n) + #:with ([arg- _] (... (... ...))) (infers+erase #'args #:tag 'key2) + ;; args are validated on the next line rather than above + ;; to ensure enough stx-parse progress for proper err msg, + ;; ie, "invalid type" instead of "improper tycon usage" + #:with (~! (~var _ type) (... (... ...))) #'(arg- (... (... ...))) + (add-orig (mk-type #'(τ- arg- (... (... ...)))) stx)] + [_ ;; else fail with err msg + (type-error #:src stx + #:msg + (string-append + "Improper usage of type constructor ~a: ~a, expected ~a ~a arguments") + #'τ stx #'op #'n)])))])) + (define-syntax define-internal-binding-type + (syntax-parser + [(_ (~var τ id) + (~or + (~optional ; variances + (~seq #:arg-variances arg-variances-stx:expr) + #:name "#:arg-variances keyword" + #:defaults + ([arg-variances-stx + #`(λ (stx-id) + (for/list ([arg (in-stx-list (stx-cddr stx-id))]) + invariant))])) + (~optional ; extra-info + (~seq #:extra-info extra-info) + #:name "#:extra-info keyword" + #:defaults ([extra-info #'void]))) (... ...)) + #:with τ? (mk-? #'τ) + #:with τ- (mk-- #'τ) + #:with τ-expander (mk-~ #'τ) + #:with τ-internal (generate-temporary #'τ) + #`(begin + (begin-for-syntax + (define (τ? t) + (syntax-parse t + [(~Any/bvs (~literal τ-internal) _ . _) + #t] + [_ #f])) + ;; cannot deal with annotations bc τ- has no knowledge of + ;; its kind + (define-syntax τ-expander + (pattern-expander + (syntax-parser + ; this case used by ⇑, TODO: remove this case? + ;; if has-annotations? + ;; - type has surface shape: + ;; (τ ([tv : k] ...) body ...) + ;; - and parses to pattern: + ;; [([tv k] ...) (body ...)] + ;; if not has-annotations? + ;; - type has surface shape: + ;; (τ (tv ...) body ...) + ;; - and parses to pattern: + ;; [(tv ...) (body ...)] + [(_ . pat:id) + #:with expanded-τ (generate-temporary) +; #:with kindcon-expander (mk-~ #'kindcon) + #'(~and expanded-τ + (~Any/bvs + (~literal/else τ-internal + (format "Expected ~a type, got: ~a" + 'τ (type->str #'expanded-τ)) + #'expanded-τ) + (~and bvs (tv (... (... (... ...))))) + . rst) + ;; #,(if (attribute has-annotations?) + ;; #'(~and + ;; (~parse (kindcon-expander k (... (... ...))) + ;; (detach #'expanded-τ)) + ;; (~parse pat + ;; #'[([tv k] (... (... ...))) rst])) + (~parse pat #'[bvs rst]))] + ;; TODO: fix this to handle has-annotations? + ;; the difference with the first case is that here + ;; the body is ungrouped, ie, + ;; parses to pattern[(tv ...) . (body ...)] + [(_ bvs-pat . pat) + #:with expanded-τ (generate-temporary) + #'(~and expanded-τ + (~Any/bvs + (~literal/else τ-internal + (format "Expected ~a ~a, got: ~a" + 'τ 'name (type->str #'expanded-τ)) + #'expanded-τ) + bvs-pat . pat))]))) + (define arg-vars arg-variances-stx)) + (define τ-internal + (λ _ (raise (exn:fail:type:runtime + (format "~a: Cannot use ~a at run time" 'τ 'name) + (current-continuation-marks))))) + ; τ- is an internal constructor: + ; It does not validate inputs and does not attach a kind, + ; ie, it won't be recognized as a valid type, the programmer + ; must implement their own kind system + (define-syntax (τ- stx) + (syntax-parse stx + [(_ bvs . args) + #:with τ* (add-arg-variances #'τ-internal (arg-vars stx)) + (syntax/loc stx + (τ* (λ bvs (#%expression extra-info) . args)))])))])) + (define-syntax define-binding-type + (syntax-parser + [(_ (~var τ id) + (~or ;; TODO: allow any order of kws between τ and τ- + (~optional ; arity, ie body exprs + (~seq #:arity op n:exact-nonnegative-integer) + #:name "#:arity keyword" + #:defaults ([op #'=] [n #'1])) + (~optional ; bvs, ie num bindings tyvars + (~seq #:bvs bvs-op bvs-n:exact-nonnegative-integer) + #:name "#:bvs keyword" + #:defaults ([bvs-op #'>=][bvs-n #'0])) + (~optional ; arr, ie constructor for kind annotations + (~seq #:arr (~and kindcon (~parse has-annotations? #'#t))) + #:name "#:arr keyword" + #:defaults ([kindcon #'void]))) ; dont use kindcon default + (... ...) + . (~and other-options + (~not ((~or #:arity #:bvs #:arr) . _)))) + #:with τ- (mk-- #'τ) + #`(begin + (define-internal-binding-type τ . other-options) + (define-syntax (τ stx) + (syntax-parse stx + [(_ (~and bvs + (~or (bv:id (... (... ...))) + (~and (~fail #:unless #,(attribute has-annotations?)) + ([_ (~datum key2) _] (... (... ...))) + bvs+ann))) + . args) + #:fail-unless (bvs-op (stx-length #'bvs) bvs-n) + (format "wrong number of type vars, expected ~a ~a" + 'bvs-op 'bvs-n) + #:fail-unless (op (stx-length #'args) n) + (format "wrong number of arguments, expected ~a ~a" + 'op 'n) + #:with bvs+ks (if #,(attribute has-annotations?) + #'bvs+ann + #'([bv key2 #%tag] (... (... ...)))) + #:with (bvs- τs- _) (infers/ctx+erase #'bvs+ks #'args #:tag 'key2) + ;; args are validated on the next line rather than above + ;; to ensure enough stx-parse progress for proper err msg, + ;; ie, "invalid type" instead of "improper tycon usage" + #:with (~! (~var _ type) (... (... ...))) #'τs- + #:with ([tv (~datum key2) k_arg] (... (... ...))) #'bvs+ks + #:with k_result (if #,(attribute has-annotations?) + #'(kindcon k_arg (... (... ...))) + #'#%tag) + (add-orig + (attach #'(τ- bvs- . τs-) 'key2 (default-type-eval #'k_result)) + stx)] + [_ + (type-error #:src stx + #:msg + (string-append + "Improper usage of type constructor ~a: ~a, expected ~a ~a arguments") + #'τ stx #'op #'n)])))])))])) + +;; ---------------------------------------------------------------------------- +;; ---------------------------------------------------------------------------- +;; end of define-syntax-category ---------------------------------------------- +;; ---------------------------------------------------------------------------- +;; ---------------------------------------------------------------------------- + +;; pre-declare all type-related functions and forms +(define-syntax-category type) + +;; TODO: move these into define-syntax-category? +(define-syntax typed-out + (make-provide-pre-transformer + (lambda (stx modes) + (syntax-parse stx #:datum-literals (:) + ;; cannot write ty:type bc provides might precede type def + [(_ (~and (~or (~and [out-x:id (~optional :) ty] (~parse x #'out-x)) + [[x:id (~optional :) ty] out-x:id])) ...) + #:with (x/tc ...) (generate-temporaries #'(x ...)) + #:when (stx-map + syntax-local-lift-module-end-declaration + ;; use define-primop to validate type + #'((define-primop x/tc x ty) ...)) + (pre-expand-export (syntax/loc stx (rename-out [x/tc out-x] ...)) + modes)])))) + +;; colon is optional to make it easier to use define-primop in macros +(define-syntax define-primop + (syntax-parser #:datum-literals (:) + [(define-primop op:id (~optional :) τ) + #:with op- (format-id #'op "~a-" #'op) + #'(define-primop op op- τ)] + [(define-primop op/tc:id (~optional #:as) op:id (~optional :) τ:type) + ; rename-transformer doesnt seem to expand at the right time + ; - op still has no type in #%app + #'(define-syntax op/tc + (make-variable-like-transformer (assign-type #'op #'τ)))])) + +;; generic, type-agnostic parameters +;; Use in code that is generic over types and kinds, e.g., in #lang Turnstile +(begin-for-syntax + (define current=? (make-parameter (current-type=?))) + (define (=s? xs1 xs2) ; map current=? pairwise over lists + (and (stx-length=? xs1 xs2) (stx-andmap (current=?) xs1 xs2))) + (define (sames? stx) ; check list contains same types + (define xs (stx->list stx)) + (or (null? xs) (andmap (λ (x) ((current=?) (car xs) x)) (cdr xs)))) + (define current-check-relation (make-parameter (current-typecheck-relation))) + (define (check? x1 x2) ((current-check-relation) x1 x2)) + (define (checks? xs1 xs2) ; map current-check-relation pairwise of lists + (and (stx-length=? xs1 xs2) (stx-andmap check? xs1 xs2))) + (define current-ev (make-parameter (current-type-eval))) + (define current-tag (make-parameter (type-key1)))) + +;; type assignment utilities -------------------------------------------------- +(define-syntax (let*-syntax stx) + (syntax-parse stx + [(_ () . body) #'(let-syntax () . body)] + [(_ (b . bs) . es) #'(let-syntax (b) (let*-syntax bs . es))])) +(begin-for-syntax + ;; Type assignment macro (ie assign-type) for nicer syntax (define-syntax (⊢ stx) - (syntax-parse stx #:datum-literals (:) - [(_ e : τ) #'(assign-type #`e #`τ)] + (syntax-parse stx + [(_ e tag τ) #'(assign-type #`e #`τ)] [(_ e τ) #'(⊢ e : τ)])) (define-syntax (⊢/no-teval stx) - (syntax-parse stx #:datum-literals (:) - [(_ e : τ) #'(fast-assign-type #`e #`τ)] + (syntax-parse stx + [(_ e tag τ) #'(fast-assign-type #`e #`τ)] [(_ e τ) #'(⊢/no-teval e : τ)])) - ;; Actual type assignment function. - ;; assign-type Type -> Syntax - ;; Attaches type τ to (expanded) expression e. - ;; - eval here so all types are stored in canonical form - ;; - syntax-local-introduce fixes marks on types - ;; which didnt get marked bc they were syntax properties - (define (fast-assign-type e τ #:tag [tag ':]) - (set-stx-prop/preserved e tag (syntax-local-introduce τ))) - (define (assign-type e τ #:tag [tag ':]) - (fast-assign-type e ((current-type-eval) τ) #:tag tag)) - - (define (add-expected-type e τ) + ;; functions for manipulating "expected type" + (define (add-expected-type e τ) (if (and (syntax? τ) (syntax-e τ)) (set-stx-prop/preserved e 'expected-type τ) ; dont type-eval?, ie expand? e)) (define (get-expected-type e) (get-stx-prop/cd*r e 'expected-type)) + + ;; TODO: remove? only used by macrotypes/examples/infer.rkt (define (add-env e env) (set-stx-prop/preserved e 'env env)) (define (get-env e) (syntax-property e 'env)) - ;; typeof : Syntax -> Type or #f - ;; Retrieves type of given stx, or #f if input has not been assigned a type. - (define (typeof stx #:tag [tag ':]) - (get-stx-prop/car stx tag)) - - ;; get-stx-prop/car : Syntax Any -> Any - (define (get-stx-prop/car stx tag) - (define v (syntax-property stx tag)) - (if (cons? v) (car v) v)) - - ;; get-stx-prop/cd*r : Syntax Any -> Any - (define (get-stx-prop/cd*r stx tag) - (cd*r (syntax-property stx tag))) - - ;; cd*r : Any -> Any - (define (cd*r v) - (if (cons? v) (cd*r (cdr v)) v)) - + (define (mk-tyvar X) (attach X 'tyvar #t)) (define (tyvar? X) (syntax-property X 'tyvar)) (define type-pat "[A-Za-z]+") + ;; TODO: remove this? only benefit is single control point for current-promote ;; - infers type of e ;; - checks that type of e matches the specified type ;; - erases types in e @@ -309,54 +858,79 @@ #'(τ_e (... ...))) #'res])])) + ;; -------------------------------------------------------------------------- + ;; "infer" function for expanding/computing type of an expression + + ;; matches arbitrary number of nexted (expanded) let-stxs + (define-syntax ~let*-syntax + (pattern-expander + (syntax-parser + [(_ . pat) + #:with the-stx (generate-temporary) + #'(~and the-stx + (~parse pat (let L ([stxs #'(the-stx)]) + (syntax-parse stxs + [(((~literal let-values) () + ((~literal let-values) () + . rst))) + (L #'rst)] + [es #'es]))))]))) + ;; basic infer function with no context: ;; infers the type and erases types in an expression - (define (infer+erase e) - (define e+ (expand/df e)) - (list e+ (typeof e+))) + (define (infer+erase e #:tag [tag (current-tag)] #:expa [expa expand/df]) + (define e+ (expa e)) + (list e+ (detach e+ tag))) ;; infers the types and erases types in multiple expressions - (define (infers+erase es) - (stx-map infer+erase es)) + (define (infers+erase es #:tag [tag (current-tag)] #:expa [expa expand/df]) + (stx-map (λ (e) (infer+erase e #:tag tag #:expa expa)) es)) - ;; This is the main "infer" function. All others are defined in terms of this. + ;; This is the main "infer" function. Most others are defined in terms of this. ;; It should be named infer+erase but leaving it for now for backward compat. - ;; ctx = vars and their types (or other props, denoted with "sep") - ;; tvctx = tyvars and their kinds - (define (infer es #:ctx [ctx null] #:tvctx [tvctx null]) - (syntax-parse ctx #:datum-literals (:) - [([x : τ] ...) ; dont expand yet bc τ may have references to tvs - #:with ([tv (~seq sep:id tvk) ...] ...) tvctx + ;; ctx = vars and their types (or or any props, denoted with any "sep") + ;; - each x in ctx is in scope for subsequent xs + ;; - ie, dont need separate ctx and tvctx + ;; - keep tvctx bc it's often useful to separate the returned Xs- + ;; TODO: infer currently tries to be generic over types and kinds + ;; but I'm not sure it properly generalizes + ;; eg, what if I need separate type-eval and kind-eval fns? + ;; - should infer be moved into define-syntax-category? + (define (infer es #:ctx [ctx null] #:tvctx [tvctx null] + #:tag [tag (current-tag)] ; the "type" to return from es + #:expa [expa expand/df] ; used to expand e + #:tev [tev #'(current-type-eval)] ; type-eval (τ in ctx) + #:key [kev #'(current-type-eval)]) ; kind-eval (tvk in tvctx) + (syntax-parse ctx + [((~or X:id [x:id (~seq sep:id τ) ...]) ...) ; dont expand; τ may reference to tv + #:with (~or (~and (tv:id ...) + (~parse ([(tvsep ...) (tvk ...)] ...) + (stx-map (λ _ #'[(::) (#%type)]) #'(tv ...)))) + ([tv (~seq tvsep:id tvk) ...] ...)) + tvctx #:with (e ...) es - #:with - ; old expander pattern (leave commented out) - #;((~literal #%plain-lambda) tvs+ - ((~literal #%expression) - ((~literal #%plain-lambda) xs+ - ((~literal letrec-syntaxes+values) stxs1 () - ((~literal letrec-syntaxes+values) stxs2 () - ((~literal #%expression) e+) ...))))) - ; new expander pattern - ((~literal #%plain-lambda) tvs+ - ((~literal let-values) () ((~literal let-values) () - ((~literal #%expression) - ((~literal #%plain-lambda) xs+ - ((~literal let-values) () ((~literal let-values) () - ((~literal #%expression) e+) ... (~literal void)))))))) - (expand/df + #:with ((~literal #%plain-lambda) tvs+ + (~let*-syntax + ((~literal #%expression) + ((~literal #%plain-lambda) xs+ + (~let*-syntax + ((~literal #%expression) e+) ... (~literal void)))))) + (expa #`(λ (tv ...) - (let-syntax ([tv (make-rename-transformer - (set-stx-prop/preserved - (for/fold ([tv-id #'tv]) - ([s (in-list (list 'sep ...))] - [k (in-list (list #'tvk ...))]) - (assign-type tv-id k #:tag s)) - 'tyvar #t))] ...) - (λ (x ...) - (let-syntax - ([x (make-variable-like-transformer (assign-type #'x #'τ))] ...) - (#%expression e) ... void))))) - (list #'tvs+ #'xs+ #'(e+ ...) (stx-map typeof #'(e+ ...)))] - [([x τ] ...) (infer es #:ctx #'([x : τ] ...) #:tvctx tvctx)])) + (let*-syntax ([tv (make-rename-transformer + (mk-tyvar + (attachs #'tv '(tvsep ...) #'(tvk ...) + #:ev #,kev)))] ...) + (λ (X ... x ...) + (let*-syntax ([X (make-variable-like-transformer + (mk-tyvar (attach #'X ':: (#,kev #'#%type))))] ... + [x (make-variable-like-transformer + (attachs #'x '(sep ...) #'(τ ...) + #:ev #,tev))] ...) + (#%expression e) ... void))))) + (list #'tvs+ #'xs+ + #'(e+ ...) + (stx-map (λ (e) (detach e tag)) #'(e+ ...)))] + [([x τ] ...) (infer es #:ctx #`([x #,tag τ] ...) #:tvctx tvctx #:tag tag)])) ;; fns derived from infer --------------------------------------------------- ;; some are syntactic shortcuts, some are for backwards compat @@ -364,18 +938,20 @@ ;; shorter names ; ctx = type env for bound vars in term e, etc ; can also use for bound tyvars in type e - (define (infer/ctx+erase ctx e) - (syntax-parse (infer (list e) #:ctx ctx) + (define (infer/ctx+erase ctx e #:tag [tag (current-tag)]) + (syntax-parse (infer (list e) #:ctx ctx #:tag tag) [(_ xs (e+) (τ)) (list #'xs #'e+ #'τ)])) - (define (infers/ctx+erase ctx es) - (stx-cdr (infer es #:ctx ctx))) + (define (infers/ctx+erase ctx es #:tag [tag (current-tag)]) + (stx-cdr (infer es #:ctx ctx #:tag tag))) ; tyctx = kind env for bound type vars in term e - (define (infer/tyctx+erase ctx e) - (syntax-parse (infer (list e) #:tvctx ctx) + (define (infer/tyctx+erase ctx e #:tag [tag (current-tag)]) + (syntax-parse (infer (list e) #:tvctx ctx #:tag tag) [(tvs _ (e+) (τ)) (list #'tvs #'e+ #'τ)])) - (define (infers/tyctx+erase ctx es) - (syntax-parse (infer es #:tvctx ctx) + (define (infers/tyctx+erase ctx es #:tag [tag (current-tag)]) + (syntax-parse (infer es #:tvctx ctx #:tag tag) [(tvs+ _ es+ tys) (list #'tvs+ #'es+ #'tys)])) + (define infer/tyctx infer/tyctx+erase) + (define infer/ctx infer/ctx+erase) (define current-promote (make-parameter (λ (t) t))) @@ -390,11 +966,13 @@ (define (expand/df e) (local-expand e 'expression null)) + ;; TODO: move these into define-syntax-category? ;; 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" (type->str τ_expected) - (type->str τ_given) + (or (and (syntax-e τ_given) (type->str τ_given)) + "an invalid expression") (syntax->datum (get-orig expression)))) ;; typecheck-fail-msg/1/no-expr : Type Type Stx -> String @@ -438,6 +1016,7 @@ (struct exn:fail:type:check exn:fail:user ()) (struct exn:fail:type:infer exn:fail:user ()) + ;; TODO: deprecate this? can we rely on stx-parse instead? ;; type-error #:src Syntax #:msg String Syntax ... ;; usage: ;; type-error #:src src-stx @@ -541,477 +1120,6 @@ (define (get-type-tags ts) (stx-map get-type-tag ts))) -(define-syntax define-basic-checked-id-stx - (syntax-parser #:datum-literals (:) - [(_ τ:id : kind) - #:with τ? (mk-? #'τ) - #:with τ-expander (mk-~ #'τ) - #:with τ-internal (generate-temporary #'τ) - #`(begin - (begin-for-syntax - (define (τ? t) - (syntax-parse t - [((~literal #%plain-app) (~literal τ-internal)) #t] - [_ #f])) - (define-syntax τ-expander - (pattern-expander - (syntax-parser - [:id #'((~literal #%plain-app) (~literal τ-internal))] - ; - this case used by ⇑, TODO: remove this case? - ; - but it's also needed when matching a list of types, - ; e.g., in stlc+sub (~Nat τ) - [(_ . rst) - #'(((~literal #%plain-app) (~literal τ-internal)) . rst)])))) - (define τ-internal - (λ () (raise (exn:fail:type:runtime - (format "~a: Cannot use ~a at run time" 'τ 'kind) - (current-continuation-marks))))) - (define-syntax τ - (syntax-parser - [:id - (add-orig - (assign-type - (syntax/loc this-syntax (τ-internal)) - #'kind) - #'τ)])))])) - -;; The def uses pattern vars "τ" and "kind" but this form is not restricted to -;; only types and kinds, eg, τ can be #'★ and kind can be #'#%kind (★'s type) -(define-syntax (define-basic-checked-stx stx) - (syntax-parse stx #:datum-literals (:) - [(_ τ:id : kind - (~or - (~optional (~and #:no-attach-kind (~parse no-attach-kind? #'#t))) - (~optional - (~seq #:arity op n:exact-nonnegative-integer) - #:defaults ([op #'=] [n #'1])) - (~optional (~seq #:arg-variances arg-variances-stx:expr) - #:defaults ([arg-variances-stx - #`(λ (stx-id) - (for/list ([arg (in-list (stx->list (stx-cdr stx-id)))]) - invariant))])) - (~optional (~seq #:extra-info extra-info) - #:defaults ([extra-info #'void]))) ...) - #:with #%kind (mk-#% #'kind) - #:with τ? (mk-? #'τ) - #:with τ- (mk-- #'τ) - #:with τ-expander (mk-~ #'τ) - #:with τ-internal (generate-temporary #'τ) - #`(begin - (begin-for-syntax - (define-syntax τ-expander - (pattern-expander - (syntax-parser - [(_ . pat) - #:with expanded-τ (generate-temporary) - #'(~and expanded-τ - (~Any - (~literal/else τ-internal - (format "Expected ~a type, got: ~a" - 'τ (type->str #'expanded-τ)) - #'expanded-τ) - . pat))]))) - (define arg-variances arg-variances-stx) - (define (τ? t) - (syntax-parse t - [(~Any (~literal τ-internal) . _) #t] - [_ #f]))) - (define τ-internal - (λ _ (raise (exn:fail:type:runtime - (format "~a: Cannot use ~a at run time" 'τ 'kind) - (current-continuation-marks))))) - ; τ- is an internal constructor: - ; - it does not validate inputs and does not attach a kind, - ; ie, it won't be recognized as a valid type unless a kind - ; system is implemented on top - ; - the τ constructor implements a default kind system but τ- - ; is available if the programmer wants to implement their own - (define-syntax (τ- stx) - (syntax-parse stx - [(_ . args) - #:with τ-internal* (add-arg-variances #'τ-internal (arg-variances #'(τ . args))) - (syntax/loc stx - (τ-internal* (λ () (#%expression extra-info) . args)))])) - ; this is the actual constructor - #,@(if (attribute no-attach-kind?) - #'() - #'((define-syntax (τ stx) - (syntax-parse stx - [(_ . args) - #:fail-unless (op (stx-length #'args) n) - (format "wrong number of arguments, expected ~a ~a" - 'op 'n) - #:with ([arg- _] (... ...)) (infers+erase #'args) - ;; the args are validated on the next line, rather than above - ;; to ensure enough stx-parse progress so we get a proper err msg, - ;; ie, "invalid type" instead of "improper tycon usage" - #:with (~! (~var _ kind) (... ...)) #'(arg- (... ...)) - (add-orig - (assign-type #'(τ- arg- (... ...)) #'#%kind) - stx)] - [_ ;; else fail with err msg - (type-error - #:src stx - #:msg - (string-append - "Improper usage of type constructor ~a: ~a, expected ~a ~a arguments") - #'τ stx #'op #'n)])))))])) - -;; Form for defining *binding* types, kinds, etc. -;; The def uses pattern vars "τ" and "kind" but this form is not restricted to -;; only types and kinds, eg, τ can be #'★ and kind can be #'#%kind (★'s type) -(define-syntax (define-binding-checked-stx stx) - (syntax-parse stx #:datum-literals (:) - [(_ τ:id : kind - (~or - (~optional (~and #:no-attach-kind (~parse no-attach-kind? #'#t))) - (~optional - (~seq #:arity op n:exact-nonnegative-integer) - #:defaults ([op #'=] [n #'1])) - (~optional - (~seq #:bvs bvs-op bvs-n:exact-nonnegative-integer) - #:defaults ([bvs-op #'>=][bvs-n #'0])) - (~optional - (~seq #:arr (~and kindcon (~parse has-annotations? #'#t))) - #:defaults ([kindcon #'void])) ; default kindcon should never be used - (~optional - (~seq #:arg-variances arg-variances-stx:expr) - #:defaults ([arg-variances-stx - #`(λ (stx-id) - (for/list ([arg (in-list (stx->list (stx-cdr stx-id)))]) - invariant))])) - (~optional - (~seq #:extra-info extra-info) - #:defaults ([extra-info #'void]))) ...) - #:with #%kind (mk-#% #'kind) - #:with τ? (mk-? #'τ) - #:with τ- (mk-- #'τ) - #:with τ-expander (mk-~ #'τ) - #:with τ-internal (generate-temporary #'τ) - #`(begin - (begin-for-syntax - (define-syntax τ-expander - (pattern-expander - (syntax-parser - ; this case used by ⇑, TODO: remove this case? - ;; if has-annotations? - ;; - type has surface shape - ;; (τ ([tv : k] ...) body ...) - ;; - this case parses to pattern - ;; [([tv k] ...) (body ...)] - ;; if not has-annotations? - ;; - type has surface shape - ;; (τ (tv ...) body ...) - ;; - this case parses to pattern - ;; [(tv ...) (body ...)] - [(_ . pat:id) - #:with expanded-τ (generate-temporary) - #:with kindcon-expander (mk-~ #'kindcon) - #'(~and expanded-τ - (~Any/bvs - (~literal/else τ-internal - (format "Expected ~a type, got: ~a" - 'τ (type->str #'expanded-τ)) - #'expanded-τ) - (~and bvs (tv (... (... ...)))) - . rst) - #,(if (attribute has-annotations?) - #'(~and - (~parse (kindcon-expander k (... (... ...))) - (typeof #'expanded-τ)) - (~parse pat - #'[([tv k] (... (... ...))) rst])) - #'(~parse - pat - #'[bvs rst])) - )] - ;; TODO: fix this to handle has-annotations? - ;; the difference with the first case is that here - ;; the body is ungrouped, ie, - ;; parses to pattern[(tv ...) . (body ...)] - [(_ bvs-pat . pat) - #:with expanded-τ (generate-temporary) - #'(~and expanded-τ - (~Any/bvs - (~literal/else τ-internal - (format "Expected ~a type, got: ~a" - 'τ (type->str #'expanded-τ)) - #'expanded-τ) - bvs-pat - . pat))]))) - (define arg-variances arg-variances-stx) - (define (τ? t) - (syntax-parse t - [(~Any/bvs (~literal τ-internal) _ . _) - #t] - [_ #f]))) - (define τ-internal - (λ _ (raise (exn:fail:type:runtime - (format "~a: Cannot use ~a at run time" 'τ 'kind) - (current-continuation-marks))))) - ; τ- is an internal constructor: - ; - it does not validate inputs and does not attach a kind, - ; ie, it won't be recognized as a valid type unless a kind - ; system is implemented on top - ; - the τ constructor implements a default kind system but τ- - ; is available if the programmer wants to implement their own - (define-syntax (τ- stx) - (syntax-parse stx - [(_ bvs . args) - #:with τ-internal* (add-arg-variances - #'τ-internal - (arg-variances #'(τ- . args))) ; drop bvs - (syntax/loc stx - (τ-internal* (λ bvs (#%expression extra-info) . args)))])) - ; this is the actual constructor - #,@(if (attribute no-attach-kind?) - #'() - #`((define-syntax (τ stx) - (syntax-parse stx - [(_ (~or (bv:id (... ...)) - (~and (~fail #:unless #,(attribute has-annotations?)) - bvs+ann)) - . args) - #:with bvs+ks (if #,(attribute has-annotations?) - #'bvs+ann - #'([bv : #%kind] (... ...))) - #:fail-unless (bvs-op (stx-length #'bvs+ks) bvs-n) - (format "wrong number of type vars, expected ~a ~a" - 'bvs-op 'bvs-n) - #:fail-unless (op (stx-length #'args) n) - (format "wrong number of arguments, expected ~a ~a" - 'op 'n) - #:with (bvs- τs- _) (infers/ctx+erase #'bvs+ks #'args) - ;; the args are validated on the next line, rather than above - ;; to ensure enough stx-parse progress so we get a proper err msg, - ;; ie, "invalid type" instead of "improper tycon usage" - #:with (~! (~var _ kind) (... ...)) #'τs- - #:with ([tv (~datum :) k_arg] (... ...)) #'bvs+ks - #:with k_result (if #,(attribute has-annotations?) - #'(kindcon k_arg (... ...)) - #'#%kind) - (add-orig - (assign-type #'(τ- bvs- . τs-) #'k_result) - stx)] - ;; else fail with err msg - [_ - (type-error #:src stx - #:msg (string-append - "Improper usage of type constructor ~a: ~a, expected ~a ~a arguments") - #'τ stx #'op #'n)])))))])) - -; examples: -; (define-syntax-category type) -; (define-syntax-category kind) -(define-syntax (define-syntax-category stx) - (syntax-parse stx - [(_ name:id) - #:with names (format-id #'name "~as" #'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? (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) - #:with define-name-cons (format-id #'name "define-~a-constructor" #'name) - #:with define-binding-name (format-id #'name "define-binding-~a" #'name) - #:with define-internal-name-cons (format-id #'name "define-internal-~a-constructor" #'name) - #:with define-internal-binding-name (format-id #'name "define-internal-binding-~a" #'name) - #: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=? (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) - (pattern τ - #: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 #'τ)))) - (define-syntax-class name-bind #:datum-literals (:) - #:attributes (x name) - (pattern [x:id : ~! (~var ty name)] - #:attr name #'ty.norm) - (pattern any - #:fail-when #t - (format - (string-append - "Improperly formatted ~a annotation: ~a; should have shape [x : τ], " - "where τ is a valid ~a.") - 'name (type->str #'any) 'name) - #:attr x #f #:attr name #f)) - (define-syntax-class name-ctx - #:attributes ((x 1) (name 1)) - (pattern ((~var || name-bind) (... ...)))) - (define-syntax-class name-ann ; type instantiation - #:attributes (norm) - (pattern (~and (_) - (~fail #:unless (brace? this-syntax)) - ((~var t name) ~!)) - #:attr norm (delay #'t.norm)) - (pattern any - #:fail-when #t - (format - (string-append - "Improperly formatted ~a annotation: ~a; should have shape {τ}, " - "where τ is a valid ~a.") - 'name (type->str #'any) 'name) - #:attr norm #f)) - (define (name=? t1 t2) - ;(printf "(τ=) t1 = ~a\n" #;τ1 (syntax->datum t1)) - ;(printf "(τ=) t2 = ~a\n" #;τ2 (syntax->datum t2)) - (or (and (identifier? t1) (identifier? t2) (free-identifier=? t1 t2)) - (and (stx-null? t1) (stx-null? t2)) - (syntax-parse (list t1 t2) - [(((~literal #%plain-lambda) (~and (_:id (... ...)) xs) . ts1) - ((~literal #%plain-lambda) (~and (_:id (... ...)) ys) . ts2)) - (and (stx-length=? #'xs #'ys) - (stx-length=? #'ts1 #'ts2) - (stx-andmap - (λ (ty1 ty2) - ((current-name=?) (substs #'ys #'xs ty1) ty2)) - #'ts1 #'ts2))] - [_ (and (stx-pair? t1) (stx-pair? t2) - (names=? t1 t2))]))) - (define current-name=? (make-parameter 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)))) - ;; 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 - (lambda (stx modes) - (syntax-parse stx - ;; cannot write ty:type bc provides might precede type def - [(_ . ts) - #:with t-expanders (stx-map mk-~ #'ts) - #:with t?s (stx-map mk-? #'ts) - (expand-export - (syntax/loc stx (combine-out - (combine-out . ts) - (for-syntax (combine-out . t-expanders) . t?s))) - modes)])))) - (define-syntax define-base-name - (syntax-parser - [(_ (~var x id) (~datum :) k) - #'(define-basic-checked-id-stx x : k)] - [(_ (~var x id)) - #'(define-basic-checked-id-stx x : #%tag)])) - (define-syntax define-base-names - (syntax-parser - [(_ (~var x id) (... ...)) - #'(begin (define-base-name x) (... ...))])) - (define-syntax define-internal-name-cons - (syntax-parser - [(_ (~var x id) . rst) - #'(define-basic-checked-stx x : name #:no-attach-kind . rst)])) - (define-syntax define-internal-binding-name - (syntax-parser - [(_ (~var x id) . rst) - #'(define-binding-checked-stx x : name #:no-attach-kind . rst)])) - (define-syntax define-name-cons - (syntax-parser - [(_ (~var x id) . rst) - #'(define-basic-checked-stx x : name . rst)])) - (define-syntax define-binding-name - (syntax-parser - [(_ (~var x id) . rst) - #'(define-binding-checked-stx x : name . rst)])))])) - -;; pre-declare all type-related functions and forms -(define-syntax-category type) - -(define-syntax typed-out - (make-provide-pre-transformer - (lambda (stx modes) - (syntax-parse stx #:datum-literals (:) - ;; cannot write ty:type bc provides might precede type def - [(_ (~and (~or (~and [out-x:id (~optional :) ty] (~parse x #'out-x)) - [[x:id (~optional :) ty] out-x:id])) ...) - #:with (x/tc ...) (generate-temporaries #'(x ...)) - #:when (stx-map - syntax-local-lift-module-end-declaration - ;; use define-primop to validate type - #'((define-primop x/tc x ty) ...)) - (pre-expand-export (syntax/loc stx (rename-out [x/tc out-x] ...)) - modes)])))) - -;; colon is optional to make it easier to use define-primop in macros -(define-syntax define-primop - (syntax-parser #:datum-literals (:) - [(define-primop op:id (~optional :) τ) - #:with op- (format-id #'op "~a-" #'op) - #'(define-primop op op- τ)] - [(define-primop op/tc:id (~optional #:as) op:id (~optional :) τ:type) - ; rename-transformer doesnt seem to expand at the right time - ; - op still has no type in #%app - #'(define-syntax op/tc - (make-variable-like-transformer (assign-type #'op #'τ)))])) - ; substitution (begin-for-syntax (define-syntax ~Any/bvs ; matches any tycon @@ -1039,7 +1147,7 @@ (free-identifier=? #'actual #'lit)) fail-msg) stx))]))) - (define (merge-type-tags stx) + (define (merge-type-tags stx) ;; TODO: merge other tags? (define t (syntax-property stx ':)) (or (and (pair? t) (identifier? (car t)) (identifier? (cdr t)) diff --git a/turnstile/examples/exist.rkt b/turnstile/examples/exist.rkt @@ -65,7 +65,6 @@ ;; Γ ⊢ (open [x <= e_packed with X_2] e) : τ_e ;; [⊢ e_packed ≫ e_packed- ⇒ (~∃ (Y) τ_body)] - #:with τ_x (subst #'X #'Y #'τ_body) - [([X ≫ X- : #%type]) ([x ≫ x- : τ_x]) ⊢ e ≫ e- ⇒ τ_e] + [X [x ≫ x- : #,(subst #'X #'Y #'τ_body)] ⊢ e ≫ e- ⇒ τ_e] -------- [⊢ (let- ([x- e_packed-]) e-) ⇒ τ_e]) diff --git a/turnstile/examples/fomega-no-reuse-old.rkt b/turnstile/examples/fomega-no-reuse-old.rkt @@ -0,0 +1,175 @@ +#lang turnstile/lang + +;; System F_omega, without reusing rules from other languages +;; - try to avoid using built-in "kind" system (ie #%type) +;; - not quite there: define-primop and typed-out still use current-type? +;; - use define-internal- forms instead + +;; example suggested by Alexis King + +;; this version still uses ': key for kinds + +;; tyλ and λ are separate forms + +(provide define-type-alias + ★ ⇒ Int Bool String Float Char → ∀ tyλ tyapp + (typed-out [+ : (→ Int Int Int)]) + λ #%app #%datum Λ inst ann) + +(define-syntax-category kind) + +;; redefine: +;; - 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 + + ;; well-formed types have kind ★ + ;; (need this for define-primop, which still uses type stx-class) + (current-type? (λ (t) (★? (kindof t)))) + ;; o.w., a valid type is one with any valid kind + (current-any-type? (λ (t) ((current-kind?) (kindof t)))) + + ;; TODO: I think this can be simplified + (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)) + (current-type-eval (lambda (τ) (normalize (old-eval τ)))) + + (define old-type=? (current-type=?)) + ; ty=? == syntax eq and syntax prop eq + (define (type=? t1 t2) + (let ([k1 (kindof t1)][k2 (kindof t2)]) + ; 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=?) + (current-typecheck-relation type=?)) + +;; kinds ---------------------------------------------------------------------- +(define-internal-kind-constructor ★) ; defines ★- +(define-syntax (★ stx) + (syntax-parse stx + [:id (mk-kind #'(★-))] + [(_ k:kind ...) (mk-kind #'(★- k.norm ...))])) + +(define-kind-constructor ⇒ #:arity >= 1) + +;; types ---------------------------------------------------------------------- +(define-kinded-syntax (define-type-alias alias:id τ:any-type) ≫ + ------------------ + [≻ (define-syntax- alias + (make-variable-like-transformer #'τ.norm))]) + +(define-base-type Int : ★) +(define-base-type Bool : ★) +(define-base-type String : ★) +(define-base-type Float : ★) +(define-base-type Char : ★) + +(define-internal-type-constructor →) ; defines →- +(define-kinded-syntax (→ ty ...+) ≫ + [⊢ ty ≫ ty- ⇒ (~★ . _)] ... + -------- + [⊢ (→- ty- ...) ⇒ ★]) + +(define-internal-binding-type ∀) ; defines ∀- +(define-kinded-syntax ∀ #:datum-literals (:) + [(_ ([tv:id : k_in:kind] ...) ty) ≫ + [[tv ≫ tv- : k_in.norm] ... ⊢ ty ≫ ty- ⇒ (~★ . _)] + ------- + [⊢ (∀- (tv- ...) ty-) ⇒ (★ k_in.norm ...)]]) + +(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-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]) + +;; terms ---------------------------------------------------------------------- +(define-typed-syntax λ #:datum-literals (:) + [(_ ([x:id : τ_in:type] ...) e) ≫ + [[x ≫ x- : τ_in.norm] ... ⊢ e ≫ e- ⇒ τ_out] + ------- + [⊢ (λ- (x- ...) e-) ⇒ (→ τ_in.norm ... τ_out)]] + [(_ (x:id ...) e) ⇐ (~→ τ_in ... τ_out) ≫ + [[x ≫ x- : τ_in] ... ⊢ e ≫ e- ⇐ τ_out] + --------- + [⊢ (λ- (x- ...) e-)]]) + +(define-typed-syntax (#%app e_fn e_arg ...) ≫ + [⊢ e_fn ≫ e_fn- ⇒ (~→ τ_in ... τ_out)] + #:fail-unless (stx-length=? #'[τ_in ...] #'[e_arg ...]) + (num-args-fail-msg #'e_fn #'[τ_in ...] #'[e_arg ...]) + [⊢ e_arg ≫ e_arg- ⇐ τ_in] ... + -------- + [⊢ (#%app- e_fn- e_arg- ...) ⇒ τ_out]) + +(define-typed-syntax (ann e (~datum :) τ:type) ≫ + [⊢ e ≫ e- ⇐ τ.norm] + -------- + [⊢ e- ⇒ τ.norm]) + +(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]] + [(_ . n:integer) ≫ + -------- + [⊢ (#%datum- . n) ⇒ Int]] + [(_ . x) ≫ + -------- + [_ #:error (type-error #:src #'x #:msg "Unsupported literal: ~v" #'x)]]) + +(define-typed-syntax (Λ bvs:kind-ctx e) ≫ + [([bvs.x ≫ tv- : bvs.kind] ...) () ⊢ e ≫ e- ⇒ τ_e] + -------- + [⊢ e- ⇒ (∀ ([tv- : bvs.kind] ...) τ_e)]) + +(define-typed-syntax (inst e τ ...) ≫ + [⊢ e ≫ e- ⇒ (~∀ (tv ...) τ_body) (⇒ (~★ k ...))] +; [⊢ τ ≫ τ- ⇐ k] ... ; ⇐ would use typechecks? + [⊢ τ ≫ τ- ⇒ k_τ] ... ; so use ⇒ and kindchecks? + #:fail-unless (kindchecks? #'(k_τ ...) #'(k ...)) + (typecheck-fail-msg/multi #'(k ...) #'(k_τ ...) #'(τ ...)) + #:with τ-inst (substs #'(τ- ...) #'(tv ...) #'τ_body) + -------- + [⊢ e- ⇒ τ-inst]) + diff --git a/turnstile/examples/fomega-no-reuse.rkt b/turnstile/examples/fomega-no-reuse.rkt @@ -12,7 +12,7 @@ (typed-out [+ : (→ Int Int Int)]) λ #%app #%datum Λ inst ann) -(define-syntax-category kind) +(define-syntax-category :: kind) ;; redefine: ;; - current-type?: well-formed types have kind ★ @@ -23,9 +23,10 @@ ;; well-formed types have kind ★ ;; (need this for define-primop, which still uses type stx-class) - (current-type? (λ (t) (★? (typeof t)))) + (current-type? (λ (t) (★? (kindof t)))) ;; o.w., a valid type is one with any valid kind - (current-any-type? (λ (t) ((current-kind?) (typeof t)))) + (current-any-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k)))) ;; TODO: I think this can be simplified (define (normalize τ) @@ -48,20 +49,19 @@ (define old-eval (current-type-eval)) (current-type-eval (lambda (τ) (normalize (old-eval τ)))) - (define old-type=? (current-type=?)) + (define old-typecheck? (current-typecheck-relation)) ; ty=? == syntax eq and syntax prop eq - (define (type=? t1 t2) - (let ([k1 (typeof t1)][k2 (typeof t2)]) + (define (new-typecheck? t1 t2) + (let ([k1 (kindof t1)][k2 (kindof t2)]) ; 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=?) - (current-typecheck-relation (current-type=?))) + (and k1 k2 (kindcheck? k1 k2))) + (old-typecheck? t1 t2)))) + (current-typecheck-relation new-typecheck?)) ;; kinds ---------------------------------------------------------------------- -(define-internal-kind-constructor ★ #:arity >= 0) ; defines ★- +(define-internal-kind-constructor ★) ; defines ★- (define-syntax (★ stx) (syntax-parse stx [:id (mk-kind #'(★-))] @@ -75,11 +75,11 @@ [≻ (define-syntax- alias (make-variable-like-transformer #'τ.norm))]) -(define-base-type Int : ★) -(define-base-type Bool : ★) -(define-base-type String : ★) -(define-base-type Float : ★) -(define-base-type Char : ★) +(define-base-type Int :: ★) +(define-base-type Bool :: ★) +(define-base-type String :: ★) +(define-base-type Float :: ★) +(define-base-type Char :: ★) (define-internal-type-constructor →) ; defines →- (define-kinded-syntax (→ ty ...+) ≫ @@ -88,15 +88,15 @@ [⊢ (→- ty- ...) ⇒ ★]) (define-internal-binding-type ∀) ; defines ∀- -(define-kinded-syntax ∀ #:datum-literals (:) - [(_ ([tv:id : k_in:kind] ...) ty) ≫ - [[tv ≫ tv- : k_in.norm] ... ⊢ ty ≫ ty- ⇒ (~★ . _)] +(define-kinded-syntax ∀ + [(_ ctx:kind-ctx ty) ≫ + [[ctx.x ≫ tv- :: ctx.kind] ... ⊢ ty ≫ ty- ⇒ (~★ . _)] ------- - [⊢ (∀- (tv- ...) ty-) ⇒ (★ k_in.norm ...)]]) + [⊢ (∀- (tv- ...) ty-) ⇒ (★ ctx.kind ...)]]) (define-kinded-syntax (tyλ bvs:kind-ctx τ_body) ≫ - [[bvs.x ≫ tv- : bvs.kind] ... ⊢ τ_body ≫ τ_body- ⇒ k_body] - #:fail-unless ((current-kind?) #'k_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)]) @@ -155,20 +155,16 @@ [_ #:error (type-error #:src #'x #:msg "Unsupported literal: ~v" #'x)]]) (define-typed-syntax (Λ bvs:kind-ctx e) ≫ - [([bvs.x ≫ tv- : bvs.kind] ...) () ⊢ e ≫ e- ⇒ τ_e] + [[bvs.x ≫ tv- :: bvs.kind] ... ⊢ e ≫ e- ⇒ τ_e] -------- - [⊢ 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] ... - ;; want to use kindchecks? instead of typechecks? - [⊢ τ ≫ τ- ⇒ k_τ] ... + [⊢ e- ⇒ (∀ ([tv- :: bvs.kind] ...) τ_e)]) + +(define-typed-syntax (inst e τ:any-type ...) ≫ + [⊢ e ≫ e- ⇒ (~∀ (tv ...) τ_body) (⇒ :: (~★ k ...))] +; [⊢ τ ≫ τ- ⇐ k] ... ; ⇐ would use typechecks? + [⊢ τ ≫ τ- ⇒ :: k_τ] ... ; so use ⇒ and kindchecks? #:fail-unless (kindchecks? #'(k_τ ...) #'(k ...)) (typecheck-fail-msg/multi #'(k ...) #'(k_τ ...) #'(τ ...)) - #:with τ-inst (substs #'(τ- ...) #'(tv ...) #'τ_body) -------- - [⊢ e- ⇒ τ-inst]) + [⊢ e- ⇒ #,(substs #'(τ.norm ...) #'(tv ...) #'τ_body)]) diff --git a/turnstile/examples/fomega.rkt b/turnstile/examples/fomega.rkt @@ -1,54 +1,33 @@ #lang turnstile/lang -(extends "sysf.rkt" #:except #%datum ∀ ~∀ ∀? Λ inst) -(reuse String #%datum #:from "stlc+reco+var.rkt") +(reuse λ #%app Int → + #:from "sysf.rkt") +(reuse define-type-alias #%datum String #:from "ext-stlc.rkt") ;; System F_omega -;; Type relation: ;; Types: -;; - types from sysf.rkt -;; - String from stlc+reco+var +;; - redefine ∀ +;; - extend sysf with tyλ and tyapp ;; Terms: -;; - extend ∀ Λ inst from sysf -;; - add tyλ and tyapp -;; - #%datum from stlc+reco+var +;; - extend sysf with Λ inst -(provide (for-syntax current-kind?) - define-type-alias - (type-out ★ ⇒ ∀★ ∀) - Λ inst tyλ tyapp) +(provide (type-out ∀) (kind-out ★ ⇒ ∀★) Λ inst tyλ tyapp) -(define-syntax-category kind) +(define-syntax-category :: kind) -; want #%type to be equiv to★ -; => edit current-kind? so existing #%type annotations (with no #%kind tag) -; are treated as kinds -; <= define ★ as rename-transformer expanding to #%type +;; 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)))) - ;; Try to keep "type?" backward compatible with its uses so far, - ;; eg in the definition of λ or previous type constuctors. - ;; (However, this is not completely possible, eg define-type-alias) - ;; So now "type?" no longer validates types, rather it's a subset. - ;; But we no longer need type? to validate types, instead we can use - ;; (kind? (typeof t)) - (current-type? (λ (t) - (define k (typeof t)) - #;(or (type? t) (★? (typeof t)) (∀★? (typeof t))) - (and ((current-kind?) k) (not (⇒? k)))))) - -; must override, to handle kinds -(define-syntax define-type-alias - (syntax-parser - [(define-type-alias alias:id τ) - #:with (τ- k_τ) (infer+erase #'τ) - #:fail-unless ((current-kind?) #'k_τ) - (format "not a valid type: ~a\n" (type->str #'τ)) - #'(define-syntax alias - (syntax-parser [x:id #'τ-] [(_ . rst) #'(τ- . rst)]))])) + ;; 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 ~★ (lambda _ (error "~★ not implemented")))) ; placeholder + (define-syntax ~★ (λ _ (error "~★ not implemented")))) ; placeholder (define-syntax ★ (make-rename-transformer #'#%type)) (define-kind-constructor ⇒ #:arity >= 1) (define-kind-constructor ∀★ #:arity >= 0) @@ -56,7 +35,7 @@ (define-binding-type ∀ #:arr ∀★) ;; alternative: normalize before type=? -; but then also need to normalize in current-promote +;; but then also need to normalize in current-promote (begin-for-syntax (define (normalize τ) (syntax-parse τ #:literals (#%plain-app #%plain-lambda) @@ -77,44 +56,45 @@ [_ τ])) (define old-eval (current-type-eval)) - (define (type-eval τ) (normalize (old-eval τ))) - (current-type-eval type-eval) + (define (new-type-eval τ) (normalize (old-eval τ))) + (current-type-eval new-type-eval) (define old-type=? (current-type=?)) - ; ty=? == syntax eq and syntax prop eq - (define (type=? t1 t2) - (let ([k1 (typeof t1)][k2 (typeof t2)]) + ;; 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-type=?) k1 k2))) + (and k1 k2 ((current-kind=?) k1 k2))) (old-type=? t1 t2)))) - (current-type=? type=?) - (current-typecheck-relation (current-type=?))) + (current-typecheck-relation new-type=?)) (define-typed-syntax (Λ bvs:kind-ctx e) ≫ - [([bvs.x ≫ tv- : bvs.kind] ...) () ⊢ e ≫ e- ⇒ τ_e] + [[bvs.x ≫ tv- :: bvs.kind] ... ⊢ e ≫ e- ⇒ τ_e] -------- - [⊢ e- ⇒ (∀ ([tv- : bvs.kind] ...) τ_e)]) + [⊢ e- ⇒ (∀ ([tv- :: bvs.kind] ...) τ_e)]) -(define-typed-syntax (inst e τ ...) ≫ - [⊢ e ≫ e- ⇒ (~∀ (tv ...) τ_body) (⇒ (~∀★ k ...))] - [⊢ τ ≫ τ- ⇐ k] ... - #:with τ-inst (substs #'(τ- ...) #'(tv ...) #'τ_body) +;; τ.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- ⇒ τ-inst]) + [⊢ e- ⇒ #,(substs #'(τ.norm ...) #'tvs #'τ_body)]) -;; TODO: merge with regular λ and app? -;; - see fomega2.rkt -(define-typed-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)) +;; - 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-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 ...]) + (num-args-fail-msg #'τ_fn #'[k_in ...] #'[τ_arg ...]) [⊢ τ_arg ≫ τ_arg- ⇐ k_in] ... -------- [⊢ (#%app- τ_fn- τ_arg- ...) ⇒ k_out]) diff --git a/turnstile/examples/fomega2.rkt b/turnstile/examples/fomega2.rkt @@ -1,5 +1,5 @@ #lang turnstile/lang -(extends "sysf.rkt" #:except #%datum ∀ ~∀ ∀? Λ inst) +(extends "sysf.rkt" #:except #%datum ∀ ~∀ ∀? Λ inst λ #%app →) (reuse String #%datum #:from "stlc+reco+var.rkt") ; same as fomega.rkt except here λ and #%app works as both type and terms @@ -17,31 +17,42 @@ (provide define-type-alias ★ ∀★ ∀ - Λ inst) + λ #%app → Λ inst + (for-syntax current-kind-eval kindcheck?)) -(define-syntax-category kind) +(define-syntax-category :: kind) (begin-for-syntax - (current-kind? (λ (k) (or (#%type? k) (kind? k) (#%type? (typeof k))))) + (define old-kind? (current-kind?)) + (current-kind? (λ (k) (or (#%type? k) (old-kind? k)))) ;; Try to keep "type?" backward compatible with its uses so far, ;; eg in the definition of λ or previous type constuctors. ;; (However, this is not completely possible, eg define-type-alias) ;; So now "type?" no longer validates types, rather it's a subset. ;; But we no longer need type? to validate types, instead we can use ;; (kind? (typeof t)) - (current-type? (λ (t) (or (type? t) - (let ([k (typeof t)]) - (or (★? k) (∀★? k))) - ((current-kind?) t))))) + (current-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k) (not (→? k))))) + + ;; o.w., a valid type is one with any valid kind + (current-any-type? (λ (t) (define k (kindof t)) + (and k ((current-kind?) k))))) ; must override (define-syntax define-type-alias (syntax-parser [(_ alias:id τ) - #:with (τ- k_τ) (infer+erase #'τ) + #:with (τ- _) (infer+erase #'τ #:tag '::) #'(define-syntax alias (syntax-parser [x:id #'τ-][(_ . rst) #'(τ- . rst)]))])) +;; extend → to serve as both type and kind +(define-syntax (→ stx) + (syntax-parse stx + [(_ k:kind ...) ; kind + (add-orig (mk-kind #'(sysf:→- k.norm ...)) stx)] + [(_ . tys) #'(sysf:→ . tys)])) ; type + (define-base-kind ★) (define-kind-constructor ∀★ #:arity >= 0) (define-binding-type ∀ #:arr ∀★) @@ -70,28 +81,53 @@ (define (type-eval τ) (normalize (old-eval τ))) (current-type-eval type-eval) - (define old-type=? (current-type=?)) - (define (type=? t1 t2) - (or (and (★? t1) (#%type? t2)) - (and (#%type? t1) (★? t2)) - (and (syntax-parse (list t1 t2) #:datum-literals (:) - [((~∀ ([tv1 : k1]) tbody1) - (~∀ ([tv2 : k2]) tbody2)) - ((current-type=?) #'k1 #'k2)] - [_ #t]) - (old-type=? t1 t2)))) - (current-type=? type=?) - (current-typecheck-relation (current-type=?))) + ;; must be kind= (and not kindcheck?) since old-kind=? recurs on curr-kind= + (define old-kind=? (current-kind=?)) + (define (new-kind=? k1 k2) + (or (and (★? k1) (#%type? k2)) ; enables use of existing type defs + (and (#%type? k1) (★? k2)) + (old-kind=? k1 k2))) + (current-kind=? new-kind=?) + (current-kindcheck-relation new-kind=?) + + (define old-typecheck? (current-typecheck-relation)) + (define (new-typecheck? t1 t2) + (syntax-parse (list t1 t2) #:datum-literals (:) + [((~∀ ([tv1 : k1]) tbody1) + (~∀ ([tv2 : k2]) tbody2)) + (and (kindcheck? #'k1 #'k2) (typecheck? #'tbody1 #'tbody2))] + [_ (old-typecheck? t1 t2)])) + (current-typecheck-relation new-typecheck?)) (define-typed-syntax (Λ bvs:kind-ctx e) ≫ - [[bvs.x ≫ tv- : bvs.kind] ... ⊢ e ≫ e- ⇒ τ_e] + [[bvs.x ≫ tv- :: bvs.kind] ... ⊢ e ≫ e- ⇒ τ_e] -------- - [⊢ e- ⇒ (∀ ([tv- : bvs.kind] ...) τ_e)]) + [⊢ e- ⇒ (∀ ([tv- :: bvs.kind] ...) τ_e)]) -(define-typed-syntax (inst e τ ...) ≫ - [⊢ e ≫ e- ⇒ (~∀ (tv ...) τ_body) (⇒ : (~∀★ k ...))] - [⊢ τ ≫ τ- ⇐ k] ... - #:with τ-inst (substs #'(τ- ...) #'(tv ...) #'τ_body) +(define-typed-syntax (inst e τ:any-type ...) ≫ + [⊢ e ≫ e- ⇒ (~∀ (tv ...) τ_body) (⇒ :: (~∀★ k ...))] +; [⊢ τ ≫ τ- ⇐ :: k] ... ; doesnt work since def-typed-s ⇐ not using kindcheck? + #:with (k_τ ...) (stx-map kindof #'(τ.norm ...)) + #:fail-unless (kindchecks? #'(k_τ ...) #'(k ...)) + (typecheck-fail-msg/multi #'(k ...) #'(k_τ ...) #'(τ ...)) -------- - [⊢ e- ⇒ τ-inst]) + [⊢ e- ⇒ #,(substs #'(τ.norm ...) #'(tv ...) #'τ_body)]) + +;; extend λ to also work as a type +(define-kinded-syntax λ + [(_ bvs:kind-ctx τ) ≫ ; type + [[bvs.x ≫ X- :: bvs.kind] ... ⊢ τ ≫ τ- ⇒ k_res] + ------------ + [⊢ (λ- (X- ...) τ-) ⇒ (→ bvs.kind ... k_res)]] + [(_ . rst) ≫ --- [≻ (sysf:λ . rst)]]) ; term +;; extend #%app to also work as a type +(define-kinded-syntax #%app + [(_ τ_fn τ_arg ...) ≫ ; type + [⊢ τ_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]] + [(_ . rst) ≫ --- [≻ (sysf:#%app . rst)]]) ; term diff --git a/turnstile/examples/fomega3.rkt b/turnstile/examples/fomega3.rkt @@ -1,6 +1,8 @@ #lang turnstile/lang (extends "fomega.rkt" #:except tyapp tyλ) +;; not current working 2017-02-03 + ; same as fomega2.rkt --- λ and #%app works as both regular and type versions, ; → is both type and kind --- but reuses parts of fomega.rkt, ; ie removes the duplication in fomega2.rkt diff --git a/turnstile/examples/fsub.rkt b/turnstile/examples/fsub.rkt @@ -26,7 +26,7 @@ (begin-for-syntax (define (expose t) (cond [(identifier? t) - (define sub (typeof t #:tag '<:)) + (define sub (detach t '<:)) (if sub (expose sub) t)] [else t])) (current-promote expose) @@ -34,7 +34,7 @@ (define (sub? t1 t2) (stlc:sub? ((current-promote) t1) t2)) (current-sub? sub?) - (current-typecheck-relation (current-sub?))) + (current-typecheck-relation sub?)) ; quasi-kind, but must be type constructor because its arguments are types (define-type-constructor <: #:arity >= 0) @@ -79,7 +79,7 @@ ;; environment with a syntax property using another tag: '<: ;; The "expose" function looks for this tag to enforce the bound, ;; as in TaPL (fig 28-1) - [([tv ≫ tv- : #%type <: τsub] ...) () ⊢ e ≫ e- ⇒ τ_e] + [[tv ≫ tv- :: #%type <: τsub] ... ⊢ e ≫ e- ⇒ τ_e] -------- [⊢ e- ⇒ (∀ ([tv- <: τsub] ...) τ_e)]) (define-typed-syntax (inst e τ:type ...) ≫ diff --git a/turnstile/examples/infer.rkt b/turnstile/examples/infer.rkt @@ -159,7 +159,7 @@ #:with [X ...] (for/list ([X (in-list (generate-temporaries #'[x ...]))]) (add-orig X X)) - [([X ≫ X- : #%type] ...) ([x ≫ x- : X] ...) + [([X ≫ X- :: #%type] ...) ([x ≫ x- : X] ...) ⊢ [body ≫ body- ⇒ : τ_body*]] #:with (~?Some [V ...] τ_body (~Cs [id_2 τ_2] ...)) (syntax-local-introduce #'τ_body*) #:with τ_fn (some/inst/generalize #'[X- ... V ...] diff --git a/turnstile/examples/mlish+adhoc.rkt b/turnstile/examples/mlish+adhoc.rkt @@ -338,7 +338,7 @@ -------- [≻ (begin- (define-syntax- f (make-rename-transformer (⊢ g : ty_fn_expected))) - #,(quasisyntax/loc stx + #,(quasisyntax/loc this-syntax (define- g ;(Λ Ys (ext-stlc:λ ([x : τ] ...) (ext-stlc:begin e_body ... e_ann)))))]) (liftedλ {Y ...} ([x : τ] ... #:where TC ...) @@ -387,7 +387,7 @@ (format "Improper use of constructor ~a; expected ~a args, got ~a" (syntax->datum #'Name) (stx-length #'(X ...)) (stx-length (stx-cdr #'stx))))])] - [X (make-rename-transformer (⊢ X #%type))] ...) + [X (make-rename-transformer (mk-type #'X))] ...) (void ty_flat ...))))) #:when (or (equal? '(unbound) (syntax->datum #'(ty+ ...))) (stx-map @@ -658,10 +658,10 @@ [⊢ e ≫ e- ⇒ τ_e] #:with ([(~seq p ...) (~datum ->) e_body] ...) #'clauses #:with (pat ...) (stx-map ; use brace to indicate root pattern - (lambda (ps) (syntax-parse ps [(pp ...) (syntax/loc stx {pp ...})])) + (lambda (ps) (syntax-parse ps [(pp ...) (syntax/loc this-syntax {pp ...})])) #'((p ...) ...)) #:with ([(~and ctx ([x ty] ...)) pat-] ...) (compile-pats #'(pat ...) #'τ_e) - #:with ty-expected (get-expected-type stx) + #:with ty-expected (get-expected-type this-syntax) [[x ≫ x- : ty] ... ⊢ [(add-expected e_body ty-expected) ≫ e_body- ⇒ ty_body]] ... #:when (check-exhaust #'(pat- ...) #'τ_e) ---- @@ -671,7 +671,7 @@ [(_ e with . clauses) ≫ #:fail-when (null? (syntax->list #'clauses)) "no clauses" [⊢ e ≫ e- ⇒ τ_e] - #:with t_expect (syntax-property stx 'expected-type) ; propagate inferred type + #:with t_expect (syntax-property this-syntax 'expected-type) ; propagate inferred type #:with out (cond [(×? #'τ_e) ;; e is tuple @@ -730,7 +730,7 @@ #:with (_ (_ (_ ConsAll) . _) ...) #'info-body #:fail-unless (set=? (syntax->datum #'(Clause ...)) (syntax->datum #'(ConsAll ...))) - (type-error #:src stx + (type-error #:src this-syntax #:msg (string-append "match: clauses not exhaustive; missing: " (string-join @@ -842,7 +842,7 @@ (expand/df #'(lambda (X ...) (let-syntax - ([X (make-rename-transformer (assign-type #'X #'#%type))] ...) + ([X (make-rename-transformer (mk-type #'X))] ...) (let-syntax ;; must have this inner macro bc body of lambda may require ;; ops defined by TC to be bound @@ -896,7 +896,7 @@ ⇒ (∀ Xs+ (=> TC+ ... (ext-stlc:→ ty+ ... ty-out)))]] [(_ ([x:id (~datum :) ty] ...) body) ≫ ; no TC #:with (X ...) (compute-tyvars #'(ty ...)) - #:with (~∀ () (~ext-stlc:→ _ ... body-ty)) (get-expected-type stx) + #:with (~∀ () (~ext-stlc:→ _ ... body-ty)) (get-expected-type this-syntax) -------- [≻ (Λ (X ...) (ext-stlc:λ ([x : ty] ...) (add-expected body body-ty)))]] [(_ ([x:id (~datum :) ty] ...) body) ≫ ; no TC, ignoring expected-type @@ -904,12 +904,12 @@ -------- [≻ (Λ (X ...) (ext-stlc:λ ([x : ty] ...) body))]] [(_ (x:id ...+) body) ≫ - #:with (~?∀ Xs expected) (get-expected-type stx) + #:with (~?∀ Xs expected) (get-expected-type this-syntax) #:do [(unless (→? #'expected) - (type-error #:src stx #:msg "λ parameters must have type annotations"))] + (type-error #:src this-syntax #:msg "λ parameters must have type annotations"))] #:with (~ext-stlc:→ arg-ty ... body-ty) #'expected #:do [(unless (stx-length=? #'[x ...] #'[arg-ty ...]) - (type-error #:src stx #:msg + (type-error #:src this-syntax #:msg (format "expected a function of ~a arguments, got one with ~a arguments" (stx-length #'[arg-ty ...] #'[x ...]))))] -------- @@ -922,10 +922,9 @@ ;; TODO is there a way to have λs that refer to ids defined after them? #'(Λ Xs (ext-stlc:λ x+tys . body))]) - ;; #%app -------------------------------------------------- (define-typed-syntax mlish:#%app #:export-as #%app - [(_ e_fn . e_args) ≫ + [(~and this-app (_ e_fn . e_args)) ≫ ; #:when (printf "app: ~a\n" (syntax->datum #'(e_fn . e_args))) ;; ) compute fn type (ie ∀ and →) [⊢ e_fn ≫ e_fn- ⇒ (~and ty_fn (~∀ Xs ty_fnX))] @@ -939,7 +938,7 @@ (syntax-parse #'(e_args tyX_args) [((e_arg ...) (τ_inX ... _)) #:fail-unless (stx-length=? #'(e_arg ...) #'(τ_inX ...)) - (mk-app-err-msg stx #:expected #'(τ_inX ...) + (mk-app-err-msg #'this-app #:expected #'(τ_inX ...) #:note "Wrong number of arguments.") #:with e_fn/ty (⊢ e_fn- : (ext-stlc:→ . tyX_args)) #'(ext-stlc:#%app e_fn/ty (add-expected e_arg τ_inX) ...)])] @@ -949,13 +948,13 @@ ;; no typeclasses, duplicate code for now -------------------------------- [(~ext-stlc:→ . tyX_args) ;; ) solve for type variables Xs - (define/with-syntax ((e_arg1- ...) (unsolved-X ...) cs) (solve #'Xs #'tyX_args stx)) + (define/with-syntax ((e_arg1- ...) (unsolved-X ...) cs) (solve #'Xs #'tyX_args #'this-app)) ;; ) instantiate polymorphic function type (syntax-parse (inst-types/cs #'Xs #'cs #'tyX_args) [(τ_in ... τ_out) ; concrete types ;; ) arity check #:fail-unless (stx-length=? #'(τ_in ...) #'e_args) - (mk-app-err-msg stx #:expected #'(τ_in ...) + (mk-app-err-msg #'this-app #:expected #'(τ_in ...) #:note "Wrong number of arguments.") ;; ) compute argument types; re-use args expanded during solve #:with ([e_arg2- τ_arg2] ...) (let ([n (stx-length #'(e_arg1- ...))]) @@ -967,7 +966,7 @@ #:with (e_arg- ...) #'(e_arg1- ... e_arg2- ...) ;; ) typecheck args #:fail-unless (typechecks? #'(τ_arg ...) #'(τ_in ...)) - (mk-app-err-msg stx + (mk-app-err-msg #'this-app #:given #'(τ_arg ...) #:expected (stx-map @@ -986,13 +985,13 @@ (syntax-parse #'τ_out [(~?∀ (Y ...) τ_out) (unless (→? #'τ_out) - (raise-app-poly-infer-error stx #'(τ_in ...) #'(τ_arg ...) #'e_fn)) + (raise-app-poly-infer-error #'this-app #'(τ_in ...) #'(τ_arg ...) #'e_fn)) #'(∀ (unsolved-X ... Y ...) τ_out)])) (⊢ (#%app- e_fn- e_arg- ...) : τ_out*)])] ;; handle type class constraints ---------------------------------------- [(~=> TCX ... (~ext-stlc:→ . tyX_args)) ;; ) solve for type variables Xs - (define/with-syntax ((e_arg1- ...) (unsolved-X ...) cs) (solve #'Xs #'tyX_args stx)) + (define/with-syntax ((e_arg1- ...) (unsolved-X ...) cs) (solve #'Xs #'tyX_args #'this-app)) ;; ) instantiate polymorphic function type (syntax-parse (inst-types/cs #'Xs #'cs #'((TCX ...) tyX_args)) [((TC ...) (τ_in ... τ_out)) ; concrete types @@ -1005,7 +1004,7 @@ (with-handlers ([exn:fail:syntax:unbound? (lambda (e) - (type-error #:src stx + (type-error #:src #'this-app #:msg (format (string-append @@ -1028,9 +1027,12 @@ (stx-map (lambda (X ty-solved) (string-append (type->str X) " : " (type->str ty-solved))) - #'Xs (lookup-Xs/keep-unsolved #'Xs #'cs)) ", "))))]) + #'Xs (lookup-Xs/keep-unsolved #'Xs #'cs)) ", "))))] + [(lambda _ #t) + (lambda (e) (displayln "other exn")(displayln e) + (error 'lookup))]) (lookup-op o tys))) - (stx-map (lambda (o) (format-id stx "~a" o #:source stx)) gen-ops) + (stx-map (lambda (o) (format-id #'this-app "~a" o #:source #'this-app)) gen-ops) (stx-map (syntax-parser [(~∀ _ (~ext-stlc:→ ty_in ... _)) #'(ty_in ...)]) @@ -1038,7 +1040,7 @@ #'((generic-op ...) ...) #'((ty-concrete-op ...) ...) #'(TC ...)) ;; ) arity check #:fail-unless (stx-length=? #'(τ_in ...) #'e_args) - (mk-app-err-msg stx #:expected #'(τ_in ...) + (mk-app-err-msg #'this-app #:expected #'(τ_in ...) #:note "Wrong number of arguments.") ;; ) compute argument types; re-use args expanded during solve #:with ([e_arg2- τ_arg2] ...) (let ([n (stx-length #'(e_arg1- ...))]) @@ -1050,7 +1052,7 @@ #:with (e_arg- ...) #'(e_arg1- ... e_arg2- ...) ;; ) typecheck args #:fail-unless (typechecks? #'(τ_arg ...) #'(τ_in ...)) - (mk-app-err-msg stx + (mk-app-err-msg #'this-app #:given #'(τ_arg ...) #:expected (stx-map @@ -1069,14 +1071,14 @@ (syntax-parse #'τ_out [(~?∀ (Y ...) τ_out) (unless (→? #'τ_out) - (raise-app-poly-infer-error stx #'(τ_in ...) #'(τ_arg ...) #'e_fn)) + (raise-app-poly-infer-error #'this-app #'(τ_in ...) #'(τ_arg ...) #'e_fn)) #'(∀ (unsolved-X ... Y ...) τ_out)])) (⊢ ((#%app- e_fn- op ...) e_arg- ...) : τ_out*)])])])]] [(_ e_fn . e_args) ≫ ; err case; e_fn is not a function [⊢ e_fn ≫ e_fn- ⇒ τ_fn] -------- [#:error - (type-error #:src stx + (type-error #:src #'this-app #:msg (format "Expected expression ~a to have → type, got: ~a" (syntax->datum #'e_fn) (type->str #'τ_fn)))]]) @@ -1658,10 +1660,10 @@ [(_ (Name ty ...) [generic-op concrete-op] ...) ≫ [⊢ (Name ty ...) ≫ (~=> TC ... (~TC [generic-op-expected ty-concrete-op-expected] ...)) ⇒ _] - #:when (TCs-exist? #'(TC ...) #:ctx stx) + #:when (TCs-exist? #'(TC ...) #:ctx this-syntax) #:fail-unless (set=? (syntax->datum #'(generic-op ...)) (syntax->datum #'(generic-op-expected ...))) - (type-error #:src stx + (type-error #:src this-syntax #:msg (format "Type class instance ~a incomplete, missing: ~a" (syntax->datum #'(Name ty ...)) (string-join @@ -1711,15 +1713,15 @@ (~=> TCsub ... (~TC [generic-op-expected ty-concrete-op-expected] ...))) _) - (infers/tyctx+erase #'([X : #%type] ...) #'(TC ... (Name ty ...))) + (infers/tyctx+erase #'(X ...) #'(TC ... (Name ty ...))) ;; this produces #%app bad stx err, so manually call infer for now - ;; [([X ≫ X- : #%type] ...) () ⊢ (TC ... (Name ty ...)) ≫ + ;; [([X ≫ X- :: #%type] ...) () ⊢ (TC ... (Name ty ...)) ≫ ;; (TC+ ... ;; (~=> TCsub ... ;; (~TC [generic-op-expected ty-concrete-op-expected] ...))) ;; ⇒ _] ;; #:with Xs+ #'(X- ...) - #:when (TCs-exist? #'(TCsub ...) #:ctx stx) + #:when (TCs-exist? #'(TCsub ...) #:ctx this-syntax) ;; simulate as if the declared concrete-op* has TC ... predicates ;; TODO: fix this manual deconstruction and assembly #:with ((app fa (lam _ ei ty_fn)) ...) #'(ty-concrete-op-expected ...) @@ -1727,7 +1729,7 @@ (stx-map (current-type-eval) #'((app fa (lam Xs+ ei (=> TC+ ... ty_fn))) ...)) #:fail-unless (set=? (syntax->datum #'(generic-op ...)) (syntax->datum #'(generic-op-expected ...))) - (type-error #:src stx + (type-error #:src this-syntax #:msg (format "Type class instance ~a incomplete, missing: ~a" (syntax->datum #'(Name ty ...)) (string-join diff --git a/turnstile/examples/mlish.rkt b/turnstile/examples/mlish.rkt @@ -86,9 +86,7 @@ ;; find-free-Xs : (Stx-Listof Id) Type -> (Listof Id) ;; finds the free Xs in the type (define (find-free-Xs Xs ty) - (for/list ([X (in-list (stx->list Xs))] - #:when (stx-contains-id? ty X)) - X)) + (for/list ([X (in-stx-list Xs)] #:when (stx-contains-id? ty X)) X)) ;; solve for Xs by unifying quantified fn type with the concrete types of stx's args ;; stx = the application stx = (#%app e_fn e_arg ...) @@ -104,8 +102,9 @@ (syntax-parse tyXs [(τ_inX ... τ_outX) ;; generate initial constraints with expected type and τ_outX - #:with (~?∀ Vs expected-ty) (and (get-expected-type stx) - ((current-type-eval) (get-expected-type stx))) + #:with (~?∀ Vs expected-ty) + (and (get-expected-type stx) + ((current-type-eval) (get-expected-type stx))) (define initial-cs (if (and (syntax-e #'expected-ty) (stx-null? #'Vs)) (add-constraints Xs '() (list (list #'expected-ty #'τ_outX))) @@ -114,8 +113,8 @@ [(_ e_fn . args) (define-values (as- cs) (for/fold ([as- null] [cs initial-cs]) - ([a (in-list (syntax->list #'args))] - [tyXin (in-list (syntax->list #'(τ_inX ...)))]) + ([a (in-stx-list #'args)] + [tyXin (in-stx-list #'(τ_inX ...))]) (define ty_in (inst-type/cs Xs cs tyXin)) (define/with-syntax [a- ty_a] (infer+erase (if (empty? (find-free-Xs Xs ty_in)) @@ -149,7 +148,7 @@ (define (covariant-Xs? ty) (syntax-parse ((current-type-eval) ty) [(~?∀ Xs ty) - (for/and ([X (in-list (syntax->list #'Xs))]) + (for/and ([X (in-stx-list #'Xs)]) (covariant-X? X #'ty))])) ;; find-X-variance : Id Type [Variance] -> Variance @@ -186,7 +185,7 @@ (for/list ([arg-variance (in-list (get-arg-variances #'tycons))]) (variance-compose ctxt-variance arg-variance))) (for/fold ([acc (make-list (length Xs) irrelevant)]) - ([τ (in-list (syntax->list #'[τ ...]))] + ([τ (in-stx-list #'[τ ...])] [τ-ctxt-variance (in-list τ-ctxt-variances)]) (map variance-join acc @@ -422,7 +421,7 @@ (format "Improper use of constructor ~a; expected ~a args, got ~a" (syntax->datum #'Name) (stx-length #'(X ...)) (stx-length (stx-cdr #'stx))))])] - [X (make-rename-transformer (⊢ X #%type))] ...) + [X (make-rename-transformer (mk-type #'X))] ...) (void ty_flat ...))))) #:when (or (equal? '(unbound) (syntax->datum #'(ty+ ...))) (stx-map @@ -701,10 +700,10 @@ [⊢ e ≫ e- ⇒ τ_e] #:with ([(~seq p ...) -> e_body] ...) #'clauses #:with (pat ...) (stx-map ; use brace to indicate root pattern - (lambda (ps) (syntax-parse ps [(pp ...) (syntax/loc stx {pp ...})])) + (lambda (ps) (syntax-parse ps [(pp ...) (syntax/loc this-syntax {pp ...})])) #'((p ...) ...)) #:with ([(~and ctx ([x ty] ...)) pat-] ...) (compile-pats #'(pat ...) #'τ_e) - #:with ty-expected (get-expected-type stx) + #:with ty-expected (get-expected-type this-syntax) [[x ≫ x- : ty] ... ⊢ (add-expected e_body ty-expected) ≫ e_body- ⇒ ty_body] ... #:when (check-exhaust #'(pat- ...) #'τ_e) -------- @@ -716,7 +715,7 @@ #:fail-unless (not (null? (syntax->list #'clauses))) "no clauses" [⊢ e ≫ e- ⇒ τ_e] #:when (×? #'τ_e) - #:with t_expect (get-expected-type stx) ; propagate inferred type + #:with t_expect (get-expected-type this-syntax) ; propagate inferred type #:with ([x ... -> e_body]) #'clauses #:with (~× ty ...) #'τ_e #:fail-unless (stx-length=? #'(ty ...) #'(x ...)) @@ -733,7 +732,7 @@ #:fail-unless (not (null? (syntax->list #'clauses))) "no clauses" [⊢ e ≫ e- ⇒ τ_e] #:when (List? #'τ_e) - #:with t_expect (get-expected-type stx) ; propagate inferred type + #:with t_expect (get-expected-type this-syntax) ; propagate inferred type #:with ([(~or (~and (~and xs [x ...]) (~parse rst (generate-temporary))) (~and (~seq (~seq x ::) ... rst:id) (~parse xs #'()))) -> e_body] ...+) @@ -770,7 +769,7 @@ #:fail-unless (not (null? (syntax->list #'clauses))) "no clauses" [⊢ e ≫ e- ⇒ τ_e] #:when (and (not (×? #'τ_e)) (not (List? #'τ_e))) - #:with t_expect (get-expected-type stx) ; propagate inferred type + #:with t_expect (get-expected-type this-syntax) ; propagate inferred type #:with ([Clause:id x:id ... (~optional (~seq #:when e_guard) #:defaults ([e_guard #'(ext-stlc:#%datum . #t)])) -> e_c_un] ...+) ; un = unannotated with expected ty @@ -780,7 +779,7 @@ #:with (_ (_ (_ ConsAll) . _) ...) #'info-body #:fail-unless (set=? (syntax->datum #'(Clause ...)) (syntax->datum #'(ConsAll ...))) - (type-error #:src stx + (type-error #:src this-syntax #:msg (string-append "match: clauses not exhaustive; missing: " (string-join @@ -851,22 +850,21 @@ #:fail-unless (stx-length=? #'[x ...] #'[τ_in ...]) (format "expected a function of ~a arguments, got one with ~a arguments" (stx-length #'[τ_in ...]) (stx-length #'[x ...])) - [([X ≫ X- : #%type] ...) ([x ≫ x- : τ_in] ...) ⊢ [body ≫ body- ⇐ τ_out]] + [(X ...) ([x ≫ x- : τ_in] ...) ⊢ [body ≫ body- ⇐ τ_out]] -------- [⊢ (λ- (x- ...) body-)]] [(λ ([x : τ_x] ...) body) ⇐ (~?∀ (V ...) (~ext-stlc:→ τ_in ... τ_out)) ≫ #:with [X ...] (compute-tyvars #'(τ_x ...)) - [([X ≫ X- : #%type] ...) () ⊢ [τ_x ≫ τ_x- ⇐ #%type] ...] + [[X ≫ X- :: #%type] ... ⊢ [τ_x ≫ τ_x- ⇐ :: #%type] ...] [τ_in τ⊑ τ_x- #:for x] ... ;; TODO is there a way to have λs that refer to ids defined after them? - [([V ≫ V- : #%type] ... [X- ≫ X-- : #%type] ...) ([x ≫ x- : τ_x-] ...) - ⊢ body ≫ body- ⇐ τ_out] + [(V ... X- ...) ([x ≫ x- : τ_x-] ...) ⊢ body ≫ body- ⇐ τ_out] -------- [⊢ (λ- (x- ...) body-)]] [(λ ([x : τ_x] ...) body) ≫ #:with [X ...] (compute-tyvars #'(τ_x ...)) ;; TODO is there a way to have λs that refer to ids defined after them? - [([X ≫ X- : #%type] ...) ([x ≫ x- : τ_x] ...) ⊢ body ≫ body- ⇒ τ_body] + [([X ≫ X- :: #%type] ...) ([x ≫ x- : τ_x] ...) ⊢ body ≫ body- ⇒ τ_body] #:with [τ_x* ...] (inst-types/cs #'[X ...] #'([X X-] ...) #'[τ_x ...]) #:with τ_fn (add-orig #'(?∀ (X- ...) (ext-stlc:→ τ_x* ... τ_body)) #`(→ #,@(stx-map get-orig #'[τ_x* ...]) #,(get-orig #'τ_body))) @@ -880,7 +878,7 @@ ;; compute fn type (ie ∀ and →) [⊢ e_fn ≫ e_fn- ⇒ (~?∀ Xs (~ext-stlc:→ . tyX_args))] ;; solve for type variables Xs - #:with [[e_arg- ...] Xs* cs] (solve #'Xs #'tyX_args stx) + #:with [[e_arg- ...] Xs* cs] (solve #'Xs #'tyX_args this-syntax) ;; instantiate polymorphic function type #:with [τ_in ... τ_out] (inst-types/cs #'Xs* #'cs #'tyX_args) #:with (unsolved-X ...) (find-free-Xs #'Xs* #'τ_out) @@ -896,13 +894,13 @@ (syntax-parse #'τ_out [(~?∀ (Y ...) τ_out) #:fail-unless (→? #'τ_out) - (mk-app-poly-infer-error stx #'(τ_in ...) #'(τ_arg ...) #'e_fn) + (mk-app-poly-infer-error this-syntax #'(τ_in ...) #'(τ_arg ...) #'e_fn) (for ([X (in-list (syntax->list #'(unsolved-X ...)))]) (unless (covariant-X? X #'τ_out) (raise-syntax-error #f - (mk-app-poly-infer-error stx #'(τ_in ...) #'(τ_arg ...) #'e_fn) - stx))) + (mk-app-poly-infer-error this-syntax #'(τ_in ...) #'(τ_arg ...) #'e_fn) + this-syntax))) #'(∀ (unsolved-X ... Y ...) τ_out)])) -------- [⊢ (#%app- e_fn- e_arg- ...) ⇒ τ_out*]]) diff --git a/turnstile/examples/stlc+effect.rkt b/turnstile/examples/stlc+effect.rkt @@ -80,7 +80,7 @@ -------- [⊢ (#%app- box- e-) (⇒ : (Ref τ)) - (⇒ ν (locs #,(syntax-position stx) ns ...)) + (⇒ ν (locs #,(syntax-position this-syntax) ns ...)) (⇒ := (locs as ...)) (⇒ ! (locs ds ...))]]) (define-typed-syntax deref @@ -95,7 +95,7 @@ (⇒ : ty) (⇒ ν (locs ns ...)) (⇒ := (locs as ...)) - (⇒ ! (locs #,(syntax-position stx) ds ...))]]) + (⇒ ! (locs #,(syntax-position this-syntax) ds ...))]]) (define-typed-syntax := #:literals (:=) [(_ e_ref e) ≫ [⊢ e_ref ≫ e_ref- @@ -112,6 +112,6 @@ [⊢ (#%app- set-box!- e_ref- e-) (⇒ : Unit) (⇒ ν (locs ns1 ... ns2 ...)) - (⇒ := (locs #,(syntax-position stx) as1 ... as2 ...)) + (⇒ := (locs #,(syntax-position this-syntax) as1 ... as2 ...)) (⇒ ! (locs ds1 ... ds2 ...))]]) diff --git a/turnstile/examples/stlc+rec-iso.rkt b/turnstile/examples/stlc+rec-iso.rkt @@ -23,7 +23,6 @@ [⊢ e- ⇒ #,(subst #'τ.norm #'tv #'τ_body)]) (define-typed-syntax (fld τ:type-ann e) ≫ #:with (~μ (tv) τ_body) #'τ.norm - #:with τ_e (subst #'τ.norm #'tv #'τ_body) - [⊢ e ≫ e- ⇐ τ_e] + [⊢ e ≫ e- ⇐ #,(subst #'τ.norm #'tv #'τ_body)] -------- [⊢ e- ⇒ τ.norm]) diff --git a/turnstile/examples/stlc+reco+sub.rkt b/turnstile/examples/stlc+reco+sub.rkt @@ -49,4 +49,4 @@ #'([l τl] ...))] [_ #f]))) (current-sub? sub?) - (current-typecheck-relation (current-sub?))) + (current-typecheck-relation sub?)) diff --git a/turnstile/examples/stlc+reco+var.rkt b/turnstile/examples/stlc+reco+var.rkt @@ -122,7 +122,7 @@ (∨-ref #'τ #'l #:else (λ () (raise-syntax-error #f (format "~a field does not exist" (syntax->datum #'l)) - stx))) + this-syntax))) [⊢ e ≫ e- ⇐ τ_e] -------- [⊢ (list- 'l e)]]) diff --git a/turnstile/examples/stlc+sub.rkt b/turnstile/examples/stlc+sub.rkt @@ -52,6 +52,7 @@ (Top? τ2))) (define current-sub? (make-parameter sub?)) (current-typecheck-relation sub?) + (define (subs? τs1 τs2) (and (stx-length=? τs1 τs2) (stx-andmap (current-sub?) τs1 τs2))) diff --git a/turnstile/examples/stlc+union+case.rkt b/turnstile/examples/stlc+union+case.rkt @@ -99,7 +99,5 @@ (current-typecheck-relation sub?) (define (subs? τs1 τs2) (and (stx-length=? τs1 τs2) - (stx-andmap (current-sub?) τs1 τs2))) - - ) + (stx-andmap (current-sub?) τs1 τs2)))) diff --git a/turnstile/examples/sysf.rkt b/turnstile/examples/sysf.rkt @@ -14,17 +14,14 @@ (define-binding-type ∀) (define-typed-syntax (Λ (tv:id ...) e) ≫ - [([tv ≫ tv- : #%type] ...) () ⊢ e ≫ e- ⇒ τ] + [[tv ≫ tv- :: #%type] ... ⊢ e ≫ e- ⇒ τ] -------- [⊢ e- ⇒ (∀ (tv- ...) τ)]) (define-typed-syntax inst [(_ e τ:type ...) ≫ [⊢ e ≫ e- ⇒ (~∀ tvs τ_body)] - #:with τ_inst (substs #'(τ.norm ...) #'tvs #'τ_body) -------- - [⊢ e- ⇒ τ_inst]] - [(_ e) ≫ - -------- - [≻ e]]) + [⊢ e- ⇒ #,(substs #'(τ.norm ...) #'tvs #'τ_body)]] + [(_ e) ≫ --- [≻ e]]) diff --git a/turnstile/examples/tests/ext-stlc-tests.rkt b/turnstile/examples/tests/ext-stlc-tests.rkt @@ -52,8 +52,8 @@ (typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier") (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") +(typecheck-fail (ann Bool : Int) + #:with-msg "ann: type mismatch: expected Int, given an invalid expression\n *expression: Bool") ; let (check-type (let () (+ 1 1)) : Int ⇒ 2) diff --git a/turnstile/examples/tests/fomega-no-reuse-tests-old.rkt b/turnstile/examples/tests/fomega-no-reuse-tests-old.rkt @@ -0,0 +1,213 @@ +#lang s-exp "../fomega-no-reuse-old.rkt" +(require "rackunit-typechecking.rkt") + +;; similar to fomega-tests.rkt, but with ': kind key + +(check-type Int : ★) +(check-type String : ★) +(typecheck-fail →) +(check-type (→ Int Int) : ★) +(typecheck-fail (→ →)) +(typecheck-fail (→ 1)) +(check-type 1 : Int) + +(typecheck-fail (tyλ ([x : ★]) 1) #:with-msg "not a valid type: 1") + +(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-not-type (Λ ([X : ★]) (λ ([x : X]) x)) : + (∀ ([X : (★ ★)]) (→ X X))) + +;(check-type (∀ ([t : ★]) (→ t t)) : ★) +(check-type (∀ ([t : ★]) (→ t t)) : (★ ★)) +(check-type (→ (∀ ([t : ★]) (→ t t)) (→ Int Int)) : ★) + +(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) + +(check-type ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : ★]) (λ ([x : X]) x))) + : (∀ ([X : ★]) (→ X X))) +(typecheck-fail ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : (⇒ ★ ★)]) (λ ([x : X]) x)))) + +(check-type (tyλ ([t : ★]) t) : (⇒ ★ ★)) +(check-type (tyλ ([t : ★] [s : ★]) t) : (⇒ ★ ★ ★)) +(check-type (tyλ ([t : ★]) (tyλ ([s : ★]) t)) : (⇒ ★ (⇒ ★ ★))) +(check-type (tyλ ([t : (⇒ ★ ★)]) t) : (⇒ (⇒ ★ ★) (⇒ ★ ★))) +(check-type (tyλ ([t : (⇒ ★ ★ ★)]) t) : (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) +(check-type (tyλ ([arg : ★] [res : ★]) (→ arg res)) : (⇒ ★ ★ ★)) + +(check-type (tyapp (tyλ ([t : ★]) t) Int) : ★) +(check-type (λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) : (→ Int Int)) +(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) 1) : Int ⇒ 1) +(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) +(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) +(typecheck-fail ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) "a-string")) + +;; partial-apply → +(check-type (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int) + : (⇒ ★ ★)) +;; f's type must have kind ★ +(typecheck-fail (λ ([f : (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)]) f)) +(check-type (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : + (∀ ([tyf : (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) +(check-type (inst + (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + : (→ (→ Int String) (→ Int String))) +(typecheck-fail + (inst (Λ ([X : ★]) (λ ([x : X]) x)) 1) + #:with-msg "inst: type mismatch.*expected:.*★.*given:.*Int.*expressions: 1") + +(typecheck-fail + (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") +;; applied f too early +(typecheck-fail + (inst + (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") +(check-type ((inst + (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (λ ([x : Int]) "int")) : (→ Int String)) +(check-type (((inst + (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (λ ([x : Int]) "int")) 1) : String ⇒ "int") + +;; tapl examples, p441 +(typecheck-fail + (define-type-alias tmp 1) + #:with-msg "not a valid type: 1") +(define-type-alias Id (tyλ ([X : ★]) X)) +(check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int String) Int)) +(check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int (tyapp Id String)) Int)) +(check-type (λ ([f : (→ Int (tyapp Id String))]) 1) : (→ (→ Int String) Int)) +(check-type (λ ([f : (→ Int (tyapp Id String))]) 1) : (→ (→ Int (tyapp Id String)) Int)) +(check-type (λ ([f : (→ Int String)]) 1) : (→ (→ (tyapp Id Int) (tyapp Id String)) Int)) +(check-type (λ ([f : (→ Int String)]) 1) : (→ (→ (tyapp Id Int) String) Int)) +(check-type (λ ([f : (tyapp Id (→ Int String))]) 1) : (→ (→ Int String) Int)) +(check-type (λ ([f : (→ Int String)]) 1) : (→ (tyapp Id (→ Int String)) Int)) +(check-type (λ ([f : (tyapp Id (→ Int String))]) 1) : (→ (tyapp Id (→ Int String)) Int)) +(check-type (λ ([f : (tyapp Id (→ Int String))]) 1) : (→ (tyapp Id (tyapp Id (→ Int String))) Int)) + +;; tapl examples, p451 +(define-type-alias Pair (tyλ ([A : ★] [B : ★]) (∀ ([X : ★]) (→ (→ A B X) X)))) + +;(check-type Pair : (⇒ ★ ★ ★)) +(check-type Pair : (⇒ ★ ★ (★ ★))) + +(check-type (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X : ★][Y : ★]) (→ X Y X))) +; parametric pair constructor +(check-type + (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + : (∀ ([X : ★][Y : ★]) (→ X Y (tyapp Pair X Y)))) +; concrete Pair Int String constructor +(check-type + (inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + Int String) + : (→ Int String (tyapp Pair Int String))) +;; Pair Int String value +(check-type + ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + Int String) 1 "1") + : (tyapp Pair Int String)) +;; fst: parametric +(check-type + (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) X))) +;; fst: concrete Pair Int String accessor +(check-type + (inst + (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + Int String) + : (→ (tyapp Pair Int String) Int)) +;; apply fst +(check-type + ((inst + (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + Int String) + ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + Int String) 1 "1")) + : Int ⇒ 1) +;; snd +(check-type + (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) Y))) +(check-type + (inst + (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + Int String) + : (→ (tyapp Pair Int String) String)) +(check-type + ((inst + (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + Int String) + ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + Int String) 1 "1")) + : String ⇒ "1") + +;; sysf tests wont work, unless augmented with kinds +(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) + +(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X : ★]) (→ X X X))) ; true +(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X : ★]) (→ X X X))) ; false +(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y : ★]) (→ Y Y Y))) ; false, alpha equiv + +(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t1 : ★]) (∀ ([t2 : ★]) (→ t1 (→ t2 t2))))) + +(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t3 : ★]) (∀ ([t4 : ★]) (→ t3 (→ t4 t4))))) + +(check-not-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t4 : ★]) (∀ ([t3 : ★]) (→ t3 (→ t4 t4))))) + +(check-type (inst (Λ ([t : ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) +(check-type (inst (Λ ([t : ★]) 1) (→ Int Int)) : Int) +; first inst should be discarded +(check-type (inst (inst (Λ ([t : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) +; second inst is discarded +(check-type (inst (inst (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) + +;; polymorphic arguments +(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([t : ★]) (→ t t))) +(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([s : ★]) (→ s s))) +(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([s : ★]) (∀ ([t : ★]) (→ t t)))) +(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([t : ★]) (→ t t)))) +(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([s : ★]) (→ s s)))) +(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([u : ★]) (→ u u)))) +(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) x) : (→ (∀ ([s : ★]) (→ s s)) (∀ ([u : ★]) (→ u u)))) +(typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) (λ ([x : Int]) x))) +(typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) 1)) +(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) : (∀ ([u : ★]) (→ u u))) +(check-type + (inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) +(check-type + ((inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) 10) + : Int ⇒ 10) +(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t : ★]) (→ t t)) (→ Int Int))) +(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t : ★]) (→ t t)) Int)) +(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) + (Λ ([s : ★]) (λ ([y : s]) y))) + : Int ⇒ 10) + + +;; previous tests ------------------------------------------------------------- +(check-type 1 : Int) +(check-not-type 1 : (→ Int Int)) +;(typecheck-fail #f) ; unsupported literal +(check-type (λ ([x : Int] [y : Int]) x) : (→ Int Int Int)) +(check-not-type (λ ([x : Int]) x) : Int) +(check-type (λ ([x : Int]) x) : (→ Int Int)) +(check-type (λ ([f : (→ Int Int)]) 1) : (→ (→ Int Int) Int)) +(check-type ((λ ([x : Int]) x) 1) : Int ⇒ 1) +;(typecheck-fail ((λ ([x : Bool]) x) 1)) ; Bool is not valid type +;(typecheck-fail (λ ([x : Bool]) x)) ; Bool is not valid type +(typecheck-fail (λ ([f : Int]) (f 1 2))) ; applying f with non-fn type +(check-type (λ ([f : (→ Int Int Int)] [x : Int] [y : Int]) (f x y)) + : (→ (→ Int Int Int) Int Int Int)) +(check-type ((λ ([f : (→ Int Int Int)] [x : Int] [y : Int]) (f x y)) + 1 2) : Int ⇒ 3) +(typecheck-fail (+ 1 (λ ([x : Int]) x))) ; adding non-Int +(typecheck-fail (λ ([x : (→ Int Int)]) (+ x x))) ; x should be Int +(typecheck-fail ((λ ([x : Int] [y : Int]) y) 1)) ; wrong number of args +(check-type ((λ ([x : Int]) (+ x x)) 10) : Int ⇒ 20) diff --git a/turnstile/examples/tests/fomega-no-reuse-tests.rkt b/turnstile/examples/tests/fomega-no-reuse-tests.rkt @@ -1,84 +1,84 @@ #lang s-exp "../fomega-no-reuse.rkt" (require "rackunit-typechecking.rkt") -;; identical to fomega-tests.rkt +;; mostly identical to fomega-tests.rkt -(check-type Int : ★) -(check-type String : ★) +(check-type Int :: ★) +(check-type String :: ★) (typecheck-fail →) -(check-type (→ Int Int) : ★) +(check-type (→ Int Int) :: ★) (typecheck-fail (→ →)) (typecheck-fail (→ 1)) (check-type 1 : Int) -(typecheck-fail (tyλ ([x : ★]) 1) #:with-msg "not a valid type: 1") +(typecheck-fail (tyλ ([x :: ★]) 1) #:with-msg "not a valid type: 1") -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) -(check-not-type (Λ ([X : ★]) (λ ([x : X]) x)) : - (∀ ([X : (★ ★)]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) +(check-not-type (Λ ([X :: ★]) (λ ([x : X]) x)) : + (∀ ([X :: (★ ★)]) (→ X X))) -;(check-type (∀ ([t : ★]) (→ t t)) : ★) -(check-type (∀ ([t : ★]) (→ t t)) : (★ ★)) -(check-type (→ (∀ ([t : ★]) (→ t t)) (→ Int Int)) : ★) +;(check-type (∀ ([t :: ★]) (→ t t)) :: ★) +(check-type (∀ ([t :: ★]) (→ t t)) :: (★ ★)) +(check-type (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int)) :: ★) -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : ★]) (λ ([x : X]) x))) - : (∀ ([X : ★]) (→ X X))) -(typecheck-fail ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : (⇒ ★ ★)]) (λ ([x : X]) x)))) +(check-type ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X :: ★]) (λ ([x : X]) x))) + : (∀ ([X :: ★]) (→ X X))) +(typecheck-fail ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X : (⇒ ★ ★)]) (λ ([x : X]) x)))) -(check-type (tyλ ([t : ★]) t) : (⇒ ★ ★)) -(check-type (tyλ ([t : ★] [s : ★]) t) : (⇒ ★ ★ ★)) -(check-type (tyλ ([t : ★]) (tyλ ([s : ★]) t)) : (⇒ ★ (⇒ ★ ★))) -(check-type (tyλ ([t : (⇒ ★ ★)]) t) : (⇒ (⇒ ★ ★) (⇒ ★ ★))) -(check-type (tyλ ([t : (⇒ ★ ★ ★)]) t) : (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) -(check-type (tyλ ([arg : ★] [res : ★]) (→ arg res)) : (⇒ ★ ★ ★)) +(check-type (tyλ ([t :: ★]) t) :: (⇒ ★ ★)) +(check-type (tyλ ([t :: ★] [s :: ★]) t) :: (⇒ ★ ★ ★)) +(check-type (tyλ ([t :: ★]) (tyλ ([s :: ★]) t)) :: (⇒ ★ (⇒ ★ ★))) +(check-type (tyλ ([t :: (⇒ ★ ★)]) t) :: (⇒ (⇒ ★ ★) (⇒ ★ ★))) +(check-type (tyλ ([t :: (⇒ ★ ★ ★)]) t) :: (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) +(check-type (tyλ ([arg :: ★] [res :: ★]) (→ arg res)) :: (⇒ ★ ★ ★)) -(check-type (tyapp (tyλ ([t : ★]) t) Int) : ★) -(check-type (λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) : (→ Int Int)) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) 1) : Int ⇒ 1) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) -(typecheck-fail ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) "a-string")) +(check-type (tyapp (tyλ ([t :: ★]) t) Int) :: ★) +(check-type (λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) x) : (→ Int Int)) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) x) 1) : Int ⇒ 1) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) +(typecheck-fail ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ 1 x)) "a-string")) ;; partial-apply → -(check-type (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int) - : (⇒ ★ ★)) +(check-type (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int) + :: (⇒ ★ ★)) ;; f's type must have kind ★ -(typecheck-fail (λ ([f : (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)]) f)) -(check-type (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : - (∀ ([tyf : (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) +(typecheck-fail (λ ([f : (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)]) f)) +(check-type (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : + (∀ ([tyf :: (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) (check-type (inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) : (→ (→ Int String) (→ Int String))) -(typecheck-fail - (inst (Λ ([X : ★]) (λ ([x : X]) x)) 1) - #:with-msg "inst: type mismatch.*expected:.*★.*given:.*Int.*expressions: 1") +(typecheck-fail ; TODO: fix err msg: "given an invalid expression" + (inst (Λ ([X :: ★]) (λ ([x : X]) x)) 1) + #:with-msg "inst:.*not a valid type: 1") (typecheck-fail - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") ;; applied f too early (typecheck-fail (inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") (check-type ((inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) : (→ Int String)) (check-type (((inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) 1) : String ⇒ "int") ;; tapl examples, p441 (typecheck-fail (define-type-alias tmp 1) #:with-msg "not a valid type: 1") -(define-type-alias Id (tyλ ([X : ★]) X)) +(define-type-alias Id (tyλ ([X :: ★]) X)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int String) Int)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int (tyapp Id String)) Int)) (check-type (λ ([f : (→ Int (tyapp Id String))]) 1) : (→ (→ Int String) Int)) @@ -91,104 +91,104 @@ (check-type (λ ([f : (tyapp Id (→ Int String))]) 1) : (→ (tyapp Id (tyapp Id (→ Int String))) Int)) ;; tapl examples, p451 -(define-type-alias Pair (tyλ ([A : ★] [B : ★]) (∀ ([X : ★]) (→ (→ A B X) X)))) +(define-type-alias Pair (tyλ ([A :: ★] [B :: ★]) (∀ ([X :: ★]) (→ (→ A B X) X)))) -;(check-type Pair : (⇒ ★ ★ ★)) -(check-type Pair : (⇒ ★ ★ (★ ★))) +;(check-type Pair :: (⇒ ★ ★ ★)) +(check-type Pair :: (⇒ ★ ★ (★ ★))) -(check-type (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X : ★][Y : ★]) (→ X Y X))) +(check-type (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X :: ★][Y :: ★]) (→ X Y X))) ; parametric pair constructor (check-type - (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) - : (∀ ([X : ★][Y : ★]) (→ X Y (tyapp Pair X Y)))) + (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) + : (∀ ([X :: ★][Y :: ★]) (→ X Y (tyapp Pair X Y)))) ; concrete Pair Int String constructor (check-type - (inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + (inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) : (→ Int String (tyapp Pair Int String))) ;; Pair Int String value (check-type - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1") : (tyapp Pair Int String)) ;; fst: parametric (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) - : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) X))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + : (∀ ([X :: ★][Y :: ★]) (→ (tyapp Pair X Y) X))) ;; fst: concrete Pair Int String accessor (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) : (→ (tyapp Pair Int String) Int)) ;; apply fst (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : Int ⇒ 1) ;; snd (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) - : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) Y))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + : (∀ ([X :: ★][Y :: ★]) (→ (tyapp Pair X Y) Y))) (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) : (→ (tyapp Pair Int String) String)) (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : String ⇒ "1") ;; sysf tests wont work, unless augmented with kinds -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X : ★]) (→ X X X))) ; true -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X : ★]) (→ X X X))) ; false -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y : ★]) (→ Y Y Y))) ; false, alpha equiv +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X :: ★]) (→ X X X))) ; true +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X :: ★]) (→ X X X))) ; false +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y :: ★]) (→ Y Y Y))) ; false, alpha equiv -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t1 : ★]) (∀ ([t2 : ★]) (→ t1 (→ t2 t2))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t1 :: ★]) (∀ ([t2 :: ★]) (→ t1 (→ t2 t2))))) -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t3 : ★]) (∀ ([t4 : ★]) (→ t3 (→ t4 t4))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t3 :: ★]) (∀ ([t4 :: ★]) (→ t3 (→ t4 t4))))) -(check-not-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t4 : ★]) (∀ ([t3 : ★]) (→ t3 (→ t4 t4))))) +(check-not-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t4 :: ★]) (∀ ([t3 :: ★]) (→ t3 (→ t4 t4))))) -(check-type (inst (Λ ([t : ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) -(check-type (inst (Λ ([t : ★]) 1) (→ Int Int)) : Int) +(check-type (inst (Λ ([t :: ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) +(check-type (inst (Λ ([t :: ★]) 1) (→ Int Int)) : Int) ; first inst should be discarded -(check-type (inst (inst (Λ ([t : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) +(check-type (inst (inst (Λ ([t :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) ; second inst is discarded -(check-type (inst (inst (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) +(check-type (inst (inst (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) ;; polymorphic arguments -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([t : ★]) (→ t t))) -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([s : ★]) (→ s s))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([s : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([s : ★]) (→ s s)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([u : ★]) (→ u u)))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) x) : (→ (∀ ([s : ★]) (→ s s)) (∀ ([u : ★]) (→ u u)))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([t :: ★]) (→ t t))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([s :: ★]) (→ s s))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([s :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([s :: ★]) (→ s s)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([u :: ★]) (→ u u)))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) x) : (→ (∀ ([s :: ★]) (→ s s)) (∀ ([u :: ★]) (→ u u)))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) (λ ([x : Int]) x))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) 1)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) : (∀ ([u : ★]) (→ u u))) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) : (∀ ([u :: ★]) (→ u u))) (check-type - (inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) + (inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) (check-type - ((inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) 10) + ((inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) 10) : Int ⇒ 10) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t : ★]) (→ t t)) (→ Int Int))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t : ★]) (→ t t)) Int)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) - (Λ ([s : ★]) (λ ([y : s]) y))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t :: ★]) (→ t t)) Int)) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) + (Λ ([s :: ★]) (λ ([y : s]) y))) : Int ⇒ 10) diff --git a/turnstile/examples/tests/fomega-tests.rkt b/turnstile/examples/tests/fomega-tests.rkt @@ -1,82 +1,84 @@ #lang s-exp "../fomega.rkt" (require "rackunit-typechecking.rkt") -(check-type Int : ★) -(check-type String : ★) +;; ok to conflate check-kind and check-type bc +;; kindcheck? does not require special cases +(check-type Int :: ★) +(check-type String :: ★) (typecheck-fail →) -(check-type (→ Int Int) : ★) +(check-type (→ Int Int) :: ★) (typecheck-fail (→ →)) (typecheck-fail (→ 1)) (check-type 1 : Int) -(typecheck-fail (tyλ ([x : ★]) 1) #:with-msg "not a valid type: 1") +(typecheck-fail (tyλ ([x :: ★]) 1) #:with-msg "not a valid type: 1") -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) -(check-not-type (Λ ([X : ★]) (λ ([x : X]) x)) : - (∀ ([X : (∀★ ★)]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) +(check-not-type (Λ ([X :: ★]) (λ ([x : X]) x)) : + (∀ ([X :: (∀★ ★)]) (→ X X))) -;(check-type (∀ ([t : ★]) (→ t t)) : ★) -(check-type (∀ ([t : ★]) (→ t t)) : (∀★ ★)) -(check-type (→ (∀ ([t : ★]) (→ t t)) (→ Int Int)) : ★) +;(check-type (∀ ([t :: ★]) (→ t t)) :: ★) +(check-type (∀ ([t :: ★]) (→ t t)) :: (∀★ ★)) +(check-type (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int)) :: ★) -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : ★]) (λ ([x : X]) x))) - : (∀ ([X : ★]) (→ X X))) -(typecheck-fail ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : (⇒ ★ ★)]) (λ ([x : X]) x)))) +(check-type ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X :: ★]) (λ ([x : X]) x))) + : (∀ ([X :: ★]) (→ X X))) +(typecheck-fail ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X : (⇒ ★ ★)]) (λ ([x : X]) x)))) -(check-type (tyλ ([t : ★]) t) : (⇒ ★ ★)) -(check-type (tyλ ([t : ★] [s : ★]) t) : (⇒ ★ ★ ★)) -(check-type (tyλ ([t : ★]) (tyλ ([s : ★]) t)) : (⇒ ★ (⇒ ★ ★))) -(check-type (tyλ ([t : (⇒ ★ ★)]) t) : (⇒ (⇒ ★ ★) (⇒ ★ ★))) -(check-type (tyλ ([t : (⇒ ★ ★ ★)]) t) : (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) -(check-type (tyλ ([arg : ★] [res : ★]) (→ arg res)) : (⇒ ★ ★ ★)) +(check-type (tyλ ([t :: ★]) t) :: (⇒ ★ ★)) +(check-type (tyλ ([t :: ★] [s :: ★]) t) :: (⇒ ★ ★ ★)) +(check-type (tyλ ([t :: ★]) (tyλ ([s :: ★]) t)) :: (⇒ ★ (⇒ ★ ★))) +(check-type (tyλ ([t :: (⇒ ★ ★)]) t) :: (⇒ (⇒ ★ ★) (⇒ ★ ★))) +(check-type (tyλ ([t :: (⇒ ★ ★ ★)]) t) :: (⇒ (⇒ ★ ★ ★) (⇒ ★ ★ ★))) +(check-type (tyλ ([arg :: ★] [res :: ★]) (→ arg res)) :: (⇒ ★ ★ ★)) -(check-type (tyapp (tyλ ([t : ★]) t) Int) : ★) -(check-type (λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) : (→ Int Int)) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) x) 1) : Int ⇒ 1) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) -(check-type ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) -(typecheck-fail ((λ ([x : (tyapp (tyλ ([t : ★]) t) Int)]) (+ 1 x)) "a-string")) +(check-type (tyapp (tyλ ([t :: ★]) t) Int) :: ★) +(check-type (λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) x) : (→ Int Int)) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) x) 1) : Int ⇒ 1) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) +(check-type ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) +(typecheck-fail ((λ ([x : (tyapp (tyλ ([t :: ★]) t) Int)]) (+ 1 x)) "a-string")) ;; partial-apply → -(check-type (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int) - : (⇒ ★ ★)) +(check-type (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int) + :: (⇒ ★ ★)) ;; f's type must have kind ★ -(typecheck-fail (λ ([f : (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)]) f)) -(check-type (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : - (∀ ([tyf : (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) +(typecheck-fail (λ ([f : (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)]) f)) +(check-type (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) : + (∀ ([tyf :: (⇒ ★ ★)]) (→ (tyapp tyf String) (tyapp tyf String)))) (check-type (inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) : (→ (→ Int String) (→ Int String))) (typecheck-fail - (inst (Λ ([X : ★]) (λ ([x : X]) x)) 1) - #:with-msg "inst: type mismatch: expected ★, given Int\n *expression: 1") + (inst (Λ ([X :: ★]) (λ ([x : X]) x)) 1) + #:with-msg "inst:.*not a valid type: 1") (typecheck-fail - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") ;; applied f too early (typecheck-fail (inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) (f 1))) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) #:with-msg "Expected → type, got: \\(tyapp tyf String\\)") (check-type ((inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) : (→ Int String)) (check-type (((inst - (Λ ([tyf : (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) - (tyapp (tyλ ([arg : ★]) (tyλ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (⇒ ★ ★)]) (λ ([f : (tyapp tyf String)]) f)) + (tyapp (tyλ ([arg :: ★]) (tyλ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) 1) : String ⇒ "int") ;; tapl examples, p441 (typecheck-fail (define-type-alias tmp 1) #:with-msg "not a valid type: 1") -(define-type-alias Id (tyλ ([X : ★]) X)) +(define-type-alias Id (tyλ ([X :: ★]) X)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int String) Int)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int (tyapp Id String)) Int)) (check-type (λ ([f : (→ Int (tyapp Id String))]) 1) : (→ (→ Int String) Int)) @@ -89,104 +91,104 @@ (check-type (λ ([f : (tyapp Id (→ Int String))]) 1) : (→ (tyapp Id (tyapp Id (→ Int String))) Int)) ;; tapl examples, p451 -(define-type-alias Pair (tyλ ([A : ★] [B : ★]) (∀ ([X : ★]) (→ (→ A B X) X)))) +(define-type-alias Pair (tyλ ([A :: ★] [B :: ★]) (∀ ([X :: ★]) (→ (→ A B X) X)))) ;(check-type Pair : (⇒ ★ ★ ★)) -(check-type Pair : (⇒ ★ ★ (∀★ ★))) +(check-type Pair :: (⇒ ★ ★ (∀★ ★))) -(check-type (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X : ★][Y : ★]) (→ X Y X))) +(check-type (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X :: ★][Y :: ★]) (→ X Y X))) ; parametric pair constructor (check-type - (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) - : (∀ ([X : ★][Y : ★]) (→ X Y (tyapp Pair X Y)))) + (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) + : (∀ ([X :: ★][Y :: ★]) (→ X Y (tyapp Pair X Y)))) ; concrete Pair Int String constructor (check-type - (inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + (inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) : (→ Int String (tyapp Pair Int String))) ;; Pair Int String value (check-type - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1") : (tyapp Pair Int String)) ;; fst: parametric (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) - : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) X))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + : (∀ ([X :: ★][Y :: ★]) (→ (tyapp Pair X Y) X))) ;; fst: concrete Pair Int String accessor (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) : (→ (tyapp Pair Int String) Int)) ;; apply fst (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : Int ⇒ 1) ;; snd (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) - : (∀ ([X : ★][Y : ★]) (→ (tyapp Pair X Y) Y))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + : (∀ ([X :: ★][Y :: ★]) (→ (tyapp Pair X Y) Y))) (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) : (→ (tyapp Pair Int String) String)) (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : String ⇒ "1") ;; sysf tests wont work, unless augmented with kinds -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X : ★]) (→ X X X))) ; true -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X : ★]) (→ X X X))) ; false -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y : ★]) (→ Y Y Y))) ; false, alpha equiv +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X :: ★]) (→ X X X))) ; true +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X :: ★]) (→ X X X))) ; false +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y :: ★]) (→ Y Y Y))) ; false, alpha equiv -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t1 : ★]) (∀ ([t2 : ★]) (→ t1 (→ t2 t2))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t1 :: ★]) (∀ ([t2 :: ★]) (→ t1 (→ t2 t2))))) -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t3 : ★]) (∀ ([t4 : ★]) (→ t3 (→ t4 t4))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t3 :: ★]) (∀ ([t4 :: ★]) (→ t3 (→ t4 t4))))) -(check-not-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t4 : ★]) (∀ ([t3 : ★]) (→ t3 (→ t4 t4))))) +(check-not-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t4 :: ★]) (∀ ([t3 :: ★]) (→ t3 (→ t4 t4))))) -(check-type (inst (Λ ([t : ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) -(check-type (inst (Λ ([t : ★]) 1) (→ Int Int)) : Int) +(check-type (inst (Λ ([t :: ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) +(check-type (inst (Λ ([t :: ★]) 1) (→ Int Int)) : Int) ; first inst should be discarded -(check-type (inst (inst (Λ ([t : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) +(check-type (inst (inst (Λ ([t :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) ; second inst is discarded -(check-type (inst (inst (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) +(check-type (inst (inst (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) ;; polymorphic arguments -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([t : ★]) (→ t t))) -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([s : ★]) (→ s s))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([s : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([s : ★]) (→ s s)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([u : ★]) (→ u u)))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) x) : (→ (∀ ([s : ★]) (→ s s)) (∀ ([u : ★]) (→ u u)))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([t :: ★]) (→ t t))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([s :: ★]) (→ s s))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([s :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([s :: ★]) (→ s s)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([u :: ★]) (→ u u)))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) x) : (→ (∀ ([s :: ★]) (→ s s)) (∀ ([u :: ★]) (→ u u)))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) (λ ([x : Int]) x))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) 1)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) : (∀ ([u : ★]) (→ u u))) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) : (∀ ([u :: ★]) (→ u u))) (check-type - (inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) + (inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) (check-type - ((inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) 10) + ((inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) 10) : Int ⇒ 10) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t : ★]) (→ t t)) (→ Int Int))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t : ★]) (→ t t)) Int)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) - (Λ ([s : ★]) (λ ([y : s]) y))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t :: ★]) (→ t t)) Int)) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) + (Λ ([s :: ★]) (λ ([y : s]) y))) : Int ⇒ 10) diff --git a/turnstile/examples/tests/fomega2-tests.rkt b/turnstile/examples/tests/fomega2-tests.rkt @@ -1,74 +1,76 @@ #lang s-exp "../fomega2.rkt" (require "rackunit-typechecking.rkt") +(require "rackunit-kindchecking.rkt") -(check-type Int : ★) -(check-type String : ★) +(check-kind Int :: ★) +(check-kind String :: ★) (typecheck-fail →) -(check-type (→ Int Int) : ★) +(check-kind (→ Int Int) :: ★) (typecheck-fail (→ →)) (typecheck-fail (→ 1)) (check-type 1 : Int) ;; this should error but it doesnt -#;(λ ([x : ★]) 1) +#;(λ ([x :: ★]) 1) -;(check-type (∀ ([t : ★]) (→ t t)) : ★) -(check-type (∀ ([t : ★]) (→ t t)) : (∀★ ★)) -(check-type (→ (∀ ([t : ★]) (→ t t)) (→ Int Int)) : ★) +;(check-type (∀ ([t :: ★]) (→ t t)) :: ★) +(check-kind (∀ ([t :: ★]) (→ t t)) :: (∀★ ★)) +(check-kind (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int)) :: ★) -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : ★]) (λ ([x : X]) x))) - : (∀ ([X : ★]) (→ X X))) -(typecheck-fail ((λ ([x : (∀ ([X : ★]) (→ X X))]) x) (Λ ([X : (→ ★ ★)]) (λ ([x : X]) x)))) +(check-type ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X :: ★]) (λ ([x : X]) x))) + : (∀ ([X :: ★]) (→ X X))) +(typecheck-fail ((λ ([x : (∀ ([X :: ★]) (→ X X))]) x) (Λ ([X : (→ ★ ★)]) (λ ([x : X]) x)))) -(check-type (λ ([t : ★]) t) : (→ ★ ★)) -(check-type (λ ([t : ★] [s : ★]) t) : (→ ★ ★ ★)) -(check-type (λ ([t : ★]) (λ ([s : ★]) t)) : (→ ★ (→ ★ ★))) -(check-type (λ ([t : (→ ★ ★)]) t) : (→ (→ ★ ★) (→ ★ ★))) -(check-type (λ ([t : (→ ★ ★ ★)]) t) : (→ (→ ★ ★ ★) (→ ★ ★ ★))) -(check-type (λ ([arg : ★] [res : ★]) (→ arg res)) : (→ ★ ★ ★)) +;; tests for λ as a type +(check-kind (λ ([t :: ★]) t) :: (→ ★ ★)) +(check-kind (λ ([t :: ★] [s :: ★]) t) :: (→ ★ ★ ★)) +(check-kind (λ ([t :: ★]) (λ ([s :: ★]) t)) :: (→ ★ (→ ★ ★))) +(check-kind (λ ([t :: (→ ★ ★)]) t) :: (→ (→ ★ ★) (→ ★ ★))) +(check-kind (λ ([t :: (→ ★ ★ ★)]) t) :: (→ (→ ★ ★ ★) (→ ★ ★ ★))) +(check-kind (λ ([arg :: ★] [res :: ★]) (→ arg res)) :: (→ ★ ★ ★)) -(check-type ((λ ([t : ★]) t) Int) : ★) -(check-type (λ ([x : ((λ ([t : ★]) t) Int)]) x) : (→ Int Int)) -(check-type ((λ ([x : ((λ ([t : ★]) t) Int)]) x) 1) : Int ⇒ 1) -(check-type ((λ ([x : ((λ ([t : ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) -(check-type ((λ ([x : ((λ ([t : ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) -(typecheck-fail ((λ ([x : ((λ ([t : ★]) t) Int)]) (+ 1 x)) "a-string")) +(check-kind ((λ ([t :: ★]) t) Int) :: ★) +(check-type (λ ([x : ((λ ([t :: ★]) t) Int)]) x) : (→ Int Int)) +(check-type ((λ ([x : ((λ ([t :: ★]) t) Int)]) x) 1) : Int ⇒ 1) +(check-type ((λ ([x : ((λ ([t :: ★]) t) Int)]) (+ x 1)) 1) : Int ⇒ 2) +(check-type ((λ ([x : ((λ ([t :: ★]) t) Int)]) (+ 1 x)) 1) : Int ⇒ 2) +(typecheck-fail ((λ ([x : ((λ ([t :: ★]) t) Int)]) (+ 1 x)) "a-string")) ;; partial-apply → -(check-type ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int) - : (→ ★ ★)) +(check-kind ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int) + :: (→ ★ ★)) ; f's type must have kind ★ -(typecheck-fail (λ ([f : ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)]) f)) -(check-type (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) : - (∀ ([tyf : (→ ★ ★)]) (→ (tyf String) (tyf String)))) +(typecheck-fail (λ ([f : ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)]) f)) +(check-type (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) : + (∀ ([tyf :: (→ ★ ★)]) (→ (tyf String) (tyf String)))) (check-type (inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)) : (→ (→ Int String) (→ Int String))) (typecheck-fail - (inst (Λ ([X : ★]) (λ ([x : X]) x)) 1)) - ;#:with-msg "not a valid type: 1") + (inst (Λ ([X :: ★]) (λ ([x : X]) x)) 1) + #:with-msg "not a valid type: 1") ;; applied f too early (typecheck-fail (inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) (f 1))) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int))) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) (f 1))) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int))) (check-type ((inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) : (→ Int String)) (check-type (((inst - (Λ ([tyf : (→ ★ ★)]) (λ ([f : (tyf String)]) f)) - ((λ ([arg : ★]) (λ ([res : ★]) (→ arg res))) Int)) + (Λ ([tyf :: (→ ★ ★)]) (λ ([f : (tyf String)]) f)) + ((λ ([arg :: ★]) (λ ([res :: ★]) (→ arg res))) Int)) (λ ([x : Int]) "int")) 1) : String ⇒ "int") ;; tapl examples, p441 (typecheck-fail (define-type-alias tmp 1)) ;#:with-msg "not a valid type: 1") -(define-type-alias Id (λ ([X : ★]) X)) +(define-type-alias Id (λ ([X :: ★]) X)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int String) Int)) (check-type (λ ([f : (→ Int String)]) 1) : (→ (→ Int (Id String)) Int)) (check-type (λ ([f : (→ Int (Id String))]) 1) : (→ (→ Int String) Int)) @@ -81,104 +83,104 @@ (check-type (λ ([f : (Id (→ Int String))]) 1) : (→ (Id (Id (→ Int String))) Int)) ;; tapl examples, p451 -(define-type-alias Pair (λ ([A : ★] [B : ★]) (∀ ([X : ★]) (→ (→ A B X) X)))) +(define-type-alias Pair (λ ([A :: ★] [B :: ★]) (∀ ([X :: ★]) (→ (→ A B X) X)))) -;(check-type Pair : (→ ★ ★ ★)) -(check-type Pair : (→ ★ ★ (∀★ ★))) +;(check-type Pair :: (→ ★ ★ ★)) +(check-type Pair :: (→ ★ ★ (∀★ ★))) -(check-type (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X : ★][Y : ★]) (→ X Y X))) +(check-type (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) x)) : (∀ ([X :: ★][Y :: ★]) (→ X Y X))) ; parametric pair constructor (check-type - (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) - : (∀ ([X : ★][Y : ★]) (→ X Y (Pair X Y)))) + (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) + : (∀ ([X :: ★][Y :: ★]) (→ X Y (Pair X Y)))) ; concrete Pair Int String constructor (check-type - (inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + (inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) : (→ Int String (Pair Int String))) ; Pair Int String value (check-type - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1") : (Pair Int String)) ; fst: parametric (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) - : (∀ ([X : ★][Y : ★]) (→ (Pair X Y) X))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + : (∀ ([X :: ★][Y :: ★]) (→ (Pair X Y) X))) ; fst: concrete Pair Int String accessor (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) : (→ (Pair Int String) Int)) ; apply fst (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p X) (λ ([x : X][y : Y]) x)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : Int ⇒ 1) ; snd (check-type - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) - : (∀ ([X : ★][Y : ★]) (→ (Pair X Y) Y))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + : (∀ ([X :: ★][Y :: ★]) (→ (Pair X Y) Y))) (check-type (inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) : (→ (Pair Int String) String)) (check-type ((inst - (Λ ([X : ★][Y : ★]) (λ ([p : (∀ ([R : ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) + (Λ ([X :: ★][Y :: ★]) (λ ([p : (∀ ([R :: ★]) (→ (→ X Y R) R))]) ((inst p Y) (λ ([x : X][y : Y]) y)))) Int String) - ((inst (Λ ([X : ★] [Y : ★]) (λ ([x : X][y : Y]) (Λ ([R : ★]) (λ ([p : (→ X Y R)]) (p x y))))) + ((inst (Λ ([X :: ★] [Y :: ★]) (λ ([x : X][y : Y]) (Λ ([R :: ★]) (λ ([p : (→ X Y R)]) (p x y))))) Int String) 1 "1")) : String ⇒ "1") -;;; sysf tests wont work, unless augmented with kinds -(check-type (Λ ([X : ★]) (λ ([x : X]) x)) : (∀ ([X : ★]) (→ X X))) +;; sysf tests wont work, unless augmented with kinds +(check-type (Λ ([X :: ★]) (λ ([x : X]) x)) : (∀ ([X :: ★]) (→ X X))) -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X : ★]) (→ X X X))) ; true -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X : ★]) (→ X X X))) ; false -(check-type (Λ ([X : ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y : ★]) (→ Y Y Y))) ; false, alpha equiv +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) t)) : (∀ ([X :: ★]) (→ X X X))) ; true +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([X :: ★]) (→ X X X))) ; false +(check-type (Λ ([X :: ★]) (λ ([t : X] [f : X]) f)) : (∀ ([Y :: ★]) (→ Y Y Y))) ; false, alpha equiv -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t1 : ★]) (∀ ([t2 : ★]) (→ t1 (→ t2 t2))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t1 :: ★]) (∀ ([t2 :: ★]) (→ t1 (→ t2 t2))))) -(check-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t3 : ★]) (∀ ([t4 : ★]) (→ t3 (→ t4 t4))))) +(check-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t3 :: ★]) (∀ ([t4 :: ★]) (→ t3 (→ t4 t4))))) -(check-not-type (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) - : (∀ ([t4 : ★]) (∀ ([t3 : ★]) (→ t3 (→ t4 t4))))) +(check-not-type (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) (λ ([y : t2]) y)))) + : (∀ ([t4 :: ★]) (∀ ([t3 :: ★]) (→ t3 (→ t4 t4))))) -(check-type (inst (Λ ([t : ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) -(check-type (inst (Λ ([t : ★]) 1) (→ Int Int)) : Int) +(check-type (inst (Λ ([t :: ★]) (λ ([x : t]) x)) Int) : (→ Int Int)) +(check-type (inst (Λ ([t :: ★]) 1) (→ Int Int)) : Int) ; first inst should be discarded -(check-type (inst (inst (Λ ([t : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) +(check-type (inst (inst (Λ ([t :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) (→ Int Int)) Int) : (→ Int Int)) ; second inst is discarded -(check-type (inst (inst (Λ ([t1 : ★]) (Λ ([t2 : ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) +(check-type (inst (inst (Λ ([t1 :: ★]) (Λ ([t2 :: ★]) (λ ([x : t1]) x))) Int) (→ Int Int)) : (→ Int Int)) ;; polymorphic arguments -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([t : ★]) (→ t t))) -(check-type (Λ ([t : ★]) (λ ([x : t]) x)) : (∀ ([s : ★]) (→ s s))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([s : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([t : ★]) (→ t t)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([s : ★]) (→ s s)))) -(check-type (Λ ([s : ★]) (Λ ([t : ★]) (λ ([x : t]) x))) : (∀ ([r : ★]) (∀ ([u : ★]) (→ u u)))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) x) : (→ (∀ ([s : ★]) (→ s s)) (∀ ([u : ★]) (→ u u)))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([t :: ★]) (→ t t))) +(check-type (Λ ([t :: ★]) (λ ([x : t]) x)) : (∀ ([s :: ★]) (→ s s))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([s :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([t :: ★]) (→ t t)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([s :: ★]) (→ s s)))) +(check-type (Λ ([s :: ★]) (Λ ([t :: ★]) (λ ([x : t]) x))) : (∀ ([r :: ★]) (∀ ([u :: ★]) (→ u u)))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) x) : (→ (∀ ([s :: ★]) (→ s s)) (∀ ([u :: ★]) (→ u u)))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) (λ ([x : Int]) x))) (typecheck-fail ((λ ([x : (∀ (t) (→ t t))]) x) 1)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) : (∀ ([u : ★]) (→ u u))) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) : (∀ ([u :: ★]) (→ u u))) (check-type - (inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) + (inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) : (→ Int Int)) (check-type - ((inst ((λ ([x : (∀ ([t : ★]) (→ t t))]) x) (Λ ([s : ★]) (λ ([y : s]) y))) Int) 10) + ((inst ((λ ([x : (∀ ([t :: ★]) (→ t t))]) x) (Λ ([s :: ★]) (λ ([y : s]) y))) Int) 10) : Int ⇒ 10) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t : ★]) (→ t t)) (→ Int Int))) -(check-type (λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t : ★]) (→ t t)) Int)) -(check-type ((λ ([x : (∀ ([t : ★]) (→ t t))]) ((inst x Int) 10)) - (Λ ([s : ★]) (λ ([y : s]) y))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) (inst x Int)) : (→ (∀ ([t :: ★]) (→ t t)) (→ Int Int))) +(check-type (λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) : (→ (∀ ([t :: ★]) (→ t t)) Int)) +(check-type ((λ ([x : (∀ ([t :: ★]) (→ t t))]) ((inst x Int) 10)) + (Λ ([s :: ★]) (λ ([y : s]) y))) : Int ⇒ 10) diff --git a/turnstile/examples/tests/mlish-tests.rkt b/turnstile/examples/tests/mlish-tests.rkt @@ -295,7 +295,7 @@ [Nil -> 3]) : Int ⇒ 6) -;; check expected-type propagation for other match paterns +;; check expected-type propagation for other match patterns (define-type (Option A) (None) @@ -450,6 +450,13 @@ (define (ok [a : A] → (Result A B)) (Ok a)) + +;; Cannot infer concrete type for B in (Result A B). +;; Need expected type (see (inst result-if-1 ...) example below) +(typecheck-fail + (λ ([a : Int]) (ok (Cons a Nil))) + #:with-msg "Could not infer instantiation of polymorphic function ok") + (define (error [b : B] → (Result A B)) (Error b)) @@ -512,6 +519,7 @@ : (→ (→ Int (Result (List Int) (List String))) (→ String (Result (List Int) (List String))) (Result (List Int) (List String)))) + (check-type (((inst result-if-1 Int String (List Int) (List String)) (Ok 1)) (λ ([a : Int]) (ok (Cons a Nil))) (λ ([b : String]) (error (Cons b Nil)))) @@ -671,7 +679,7 @@ (typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier") (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") +(typecheck-fail (ann Int : Int) #:with-msg "expected Int, given an invalid expression\n *expression: Int") ; let (check-type (let () (+ 1 1)) : Int ⇒ 2) diff --git a/turnstile/examples/tests/rackunit-kindchecking.rkt b/turnstile/examples/tests/rackunit-kindchecking.rkt @@ -0,0 +1,18 @@ +#lang racket/base +(require (for-syntax rackunit syntax/srcloc) rackunit macrotypes/typecheck + (only-in "../fomega2.rkt" current-kind-eval kindcheck?)) +(provide check-kind) + +;; Note: this file is separate from macrotypes/examples/test/rackunit-kindcheck +;; because each one uses different defs of current-kind-eval and kindcheck? +(define-syntax (check-kind stx) + (syntax-parse stx #:datum-literals (⇒ ->) + [(_ τ tag:id k-expected) + #:with k (detach (expand/df #'(add-expected τ k-expected)) + (stx->datum #'tag)) + #:fail-unless (kindcheck? #'k ((current-kind-eval) #'k-expected)) + (format + "Type ~a [loc ~a:~a] has kind ~a, expected ~a" + (syntax->datum #'τ) (syntax-line #'τ) (syntax-column #'τ) + (type->str #'k) (type->str #'k-expected)) + #'(void)])) diff --git a/turnstile/examples/tests/rackunit-typechecking.rkt b/turnstile/examples/tests/rackunit-typechecking.rkt @@ -19,19 +19,20 @@ (string-join (map add-escs (string-split givens ", ")) ".*")))) (define-syntax (check-type stx) - (syntax-parse stx #:datum-literals (: ⇒ ->) + (syntax-parse stx #:datum-literals (⇒ ->) ;; duplicate code to avoid redundant expansions - [(_ e : τ-expected (~or ⇒ ->) v) + [(_ e tag:id τ-expected (~or ⇒ ->) v) #:with e+ (expand/df #'(add-expected e τ-expected)) - #:with τ (typeof #'e+) + #:with τ (detach #'e+ (stx->datum #'tag)) #:fail-unless (typecheck? #'τ ((current-type-eval) #'τ-expected)) (format "Expression ~a [loc ~a:~a] has type ~a, expected ~a" (syntax->datum #'e) (syntax-line #'e) (syntax-column #'e) (type->str #'τ) (type->str #'τ-expected)) (syntax/loc stx (check-equal? e+ (add-expected v τ-expected)))] - [(_ e : τ-expected) - #:with τ (typeof (expand/df #'(add-expected e τ-expected))) + [(_ e tag:id τ-expected) + #:with e+ (expand/df #'(add-expected e τ-expected)) + #:with τ (detach #'e+ (stx->datum #'tag)) #:fail-unless (typecheck? #'τ ((current-type-eval) #'τ-expected)) (format diff --git a/turnstile/examples/trivial.rkt b/turnstile/examples/trivial.rkt @@ -120,7 +120,7 @@ ((current-type=?) t1 #'t2*)] [_ #f]))) (current-type=? new-type=?) - (current-typecheck-relation (current-type=?)) + (current-typecheck-relation new-type=?) ;; current-type? ;; TODO: disabling type validation for now @@ -282,7 +282,7 @@ [x:id : ty])) ...) . es) ≫ #:with (X ...) (generate-temporaries #'(x ...)) - [([X ≫ X- : #%type] ...) ([x ≫ x- : X] ...) + [([X ≫ X- :: #%type] ...) ([x ≫ x- : X] ...) ⊢ (begin . es) ≫ e- ⇒ τ_out] ;; TODO: investigate why this extra syntax-local-introduce is needed? #:with τ_out* (syntax-local-introduce #'τ_out) @@ -344,7 +344,7 @@ #:with Bs** (prune-Bs #'Bs*) ; #:when (begin (displayln "checking Cs:") ; (pretty-print (syntax->datum #'Cs*))) - #:with remaining-Cs (check-Cs #'Cs* stx) + #:with remaining-Cs (check-Cs #'Cs* this-syntax) ; #:when (printf "remaining Cs: ~a\n" ; (syntax->datum #'remaining-Cs)) #:with ty-out** diff --git a/turnstile/info.rkt b/turnstile/info.rkt @@ -5,8 +5,9 @@ (define compile-omit-paths '("examples/rosette" + "examples/fomega3.rkt" "examples/tests" - "examples/trivial.rkt")) + "examples/trivial.rkt")) ; needs typed racket (define test-include-paths '("examples/tests/mlish")) ; to include .mlish files @@ -16,6 +17,8 @@ "examples/tests/rosette" ; needs rosette "examples/tests/trivial-test.rkt" ; needs typed/racket "examples/tests/mlish/sweet-map.rkt" ; needs sweet-exp + "examples/fomega3.rkt" + "examples/tests/fomega3-tests.rkt" "examples/tests/mlish/bg/README.md")) (define test-timeouts diff --git a/turnstile/scribblings/reference.scrbl b/turnstile/scribblings/reference.scrbl @@ -34,7 +34,7 @@ and then press Control-@litchar{\}. @; define-typed-syntax--------------------------------------------------------- @defform*[ - #:literals (≫ ⊢ ⇒ ⇐ ≻ : --------) + #:literals (≫ ⊢ ⇒ ⇐ ≻ --------) ((define-typed-syntax (name-id . pattern) ≫ premise ... -------- @@ -50,7 +50,7 @@ and then press Control-@litchar{\}. premise ... -------- ⇐-conclusion] - [expr-pattern ⇐ key type-pattern ≫ + [expr-pattern ⇐ key pattern ≫ premise ... -------- ⇐-conclusion]] @@ -70,48 +70,86 @@ and then press Control-@litchar{\}. type-relation (code:line @#,racket[syntax-parse] @#,tech:pat-directive)] [ctx (ctx-elem ...)] - [ctx-elem (code:line [id ≫ id : type-template] ooo ...)] + [ctx-elem (code:line [id ≫ id key template ... ...] ooo ...) + (code:line id ooo ...)] [tc (code:line tc-elem ooo ...)] [tc-elem [expr-template ≫ expr-pattern ⇒ type-pattern] - [expr-template ≫ expr-pattern ⇒ key type-pattern] - [expr-template ≫ expr-pattern (⇒ key type-pattern) ooo ...] + [expr-template ≫ expr-pattern ⇒ key pattern] + [expr-template ≫ expr-pattern (⇒ key pattern) ooo ...] [expr-template ≫ expr-pattern ⇐ type-template] - [expr-template ≫ expr-pattern ⇐ key type-template] - [expr-template ≫ expr-pattern (⇐ key type-template) ooo ...]] + [expr-template ≫ expr-pattern ⇐ key template] + [expr-template ≫ expr-pattern (⇐ key template) ooo ...]] [type-relation (code:line [type-template τ= type-template] ooo ...) (code:line [type-template τ= type-template #:for expr-template] ooo ...) (code:line [type-template τ⊑ type-template] ooo ...) (code:line [type-template τ⊑ type-template #:for expr-template] ooo ...)] [conclusion [⊢ expr-template ⇒ type-template] - [⊢ expr-template ⇒ key type-template] - [⊢ expr-template (⇒ key type-template) ooo ...] + [⊢ expr-template ⇒ key template] + [⊢ expr-template (⇒ key template) ooo ...] [≻ expr-template] [#:error expr-template]] [⇐-conclusion [⊢ expr-template]] [ooo ...]) ]{ -Defines a macro that additionally performs typechecking. It uses -@racket[syntax-parse] @tech:stx-pats and @tech:pat-directives and - additionally allows writing type-judgement-like clauses that interleave - typechecking and macro expansion. - -Type checking is computed as part of macro expansion and the resulting type is -attached to an expanded expression. In addition, Turnstile supports -bidirectional type checking clauses. For example @racket[[⊢ e ≫ e- ⇒ τ]] -declares that expression @racket[e] expands to @racket[e-] and has type -@racket[τ], where @racket[e] is the input and, @racket[e-] and @racket[τ] -outputs. Syntactically, @racket[e] is a syntax template position and -@racket[e-] @racket[τ] are syntax pattern positions. - -A programmer may use the generalized form @racket[[⊢ e ≫ e- (⇒ key τ) ...]] to -specify propagation of arbitrary values, associated with any number of -keys. For example, a type and effect system may wish to additionally propagate -source locations of allocations and mutations. When no key is specified, -@litchar{:}, i.e., the "type" key, is used. Dually, one may write @racket[[⊢ e +Generates a macro definition that also performs type checking. The macro is +generated from @racket[syntax-parse] @tech:stx-pats and @tech:pat-directives, +along with type-judgement-like clauses that interleave typechecking and macro +expansion. The resulting macro type checks its surface syntax as part of macro +expansion and the resulting type is attached to the expanded expression. + +@; ---------------------------------------------------------------------------- +@bold{Premises} + +@italic{Bidirectional judgements} + +Turnstile @racket[define-typed-syntax] rules use bidirectional type checking +judgements: +@itemlist[ + @item{@racket[[⊢ e ≫ e- ⇒ τ]] declares that expression @racket[e] expands to +@racket[e-] and has type @racket[τ], where @racket[e] is the input and, +@racket[e-] and @racket[τ] outputs. Syntactically, @racket[e] is a syntax +template position and @racket[e-] and @racket[τ] are syntax pattern positions.} + + @item{Dually, one may write @racket[[⊢ e ≫ e- ⇐ τ]] to check that @racket[e] has type @racket[τ]. Here, both @racket[e] and @racket[τ] are inputs (templates) and only @racket[e-] is an -output (pattern). +output (pattern).}] + +Each bidirectional arrow has a generalized form that associates a key with a +value, e.g., @racket[[⊢ e ≫ e- (⇒ key pat) ...]]. A programmer may use this +generalized form to specify propagation of arbitrary values, associated with +any number of keys. For example, a type and effect system may wish to +additionally propagate source locations of allocations and mutations. When no +key is specified, @litchar{:}, i.e., the "type" key, is used. + +@italic{Contexts} + +A context may be specified to the left of the turnstile. A context element has +shape @racket[[⊢ x ≫ x- key pat ... ...]] where @racket[x-] is a pattern +matching the expansion of @racket[x] and the interleaved @racket[key ...] and +@racket[pat ...] are arbitrary key-value pairs, e.g. a @litchar{:} key and type +pattern. + +A context has @racket[let*] semantics where each binding is in scope for +subsequent parts of the context. This means type and term variables may be in +the same context (if properly ordered). In addition, Turnstile allows writing +two separate contexts, grouped by parens, where bindings in first are in scope +for the second. This is often convenient for scenarios that Racket's pattern +language cannot express, e.g., when there two distinct groups of bindings, a +pattern @racket[x ... y ...] will not work as expected. + +For convenience, lone identifiers written to the left of the turnstile are +automatically treated as type variables. + +@; ---------------------------------------------------------------------------- +@bold{Conclusion} + +Bidirectional judgements below the conclusion line invert their inputs and +outputs so that both positions in @racket[[⊢ e- ⇒ τ]] are syntax templates and +means that @racket[e-] is the output of the generated macro and it has type τ +attached. The generalized key-value form of the bidirectional judgements may +also be used in the conclusion. The @racket[≻] conclusion form is useful in many scenarios where the rule being implemented may not want to attach type information. E.g., @@ -180,6 +218,13 @@ attach type information to the top-level @tt{x} identifier, so the ]} +@; ---------------------------------------------------------------------------- +@bold{Note} + +@racket[define-typed-syntax] is generated by +@racket[define-syntax-category]. See @racket[define-syntax-category] for more +information. + @defform[(define-typerule ....)]{Alias for @racket[define-typed-syntax].} @defform[(define-syntax/typecheck ....)]{Alias for @racket[define-typed-syntax].} @@ -203,10 +248,30 @@ When not specified, @racket[op-id] is @racket[typed-op-id] suffixed with @litchar{-} (see @secref{racket-}).} @; define-syntax-category ----------------------------------------------------- -@defform[(define-syntax-category name-id)]{ +@defform*[((define-syntax-category name-id) + (define-syntax-category key1 name-id) + (define-syntax-category key1 name-id key2))]{ + Defines a new "category" of syntax by defining a series of forms and functions. Turnstile pre-declares @racket[(define-syntax-category type)], which in turn - defines the following forms and functions: +defines the forms and functions below. + +Each category of syntax is associated with two keys: @racket[key1] is used when +attaching values in the category to other syntax, e.g., attaching types to +terms, and @racket[key2] is used for attaching an additional syntax property to +values in the category, e.g. using @racket[#%type] to indicate well-formedness. + +If no keys are specified, @racket[key1] is @litchar{:} and @racket[key2] is +@litchar{::}. If only @racket[key1] is given, then @racket[key2] is +@racket[key1] appended to itself. + +Defining another category, e.g., @racket[(define-syntax-category kind)], +defines a separate set of forms and functions, e.g., +@racket[define-kinded-syntax], @racket[define-base-kind], @racket[kind=?], etc. + +@; ---------------------------------------------------------------------------- +@italic{The following forms and functions are automatically defined by a +@racket[(define-syntax-category type)] declaration:} @margin-note{It's not important to immediately understand all these definitions. Some, like @racket[type?] and @racket[mk-type], are @@ -216,35 +281,48 @@ are probably @racket[define-typed-syntax], and the type-defining forms @racket[define-binding-type].} @itemlist[ - @item{@racket[define-typed-syntax], as described above. - Uses @racket[current-typecheck-relation] for typechecking.} + + @item{@racket[define-typed-syntax], as described above. Uses +@racket[current-typecheck-relation] @racket[current-type-eval] for +typechecking, and uses @litchar{:} as the key when an explicit key is not specificed in type judgements.} + @item{@defform*[((define-base-type base-type-name-id) - (define-base-type base-type-name-id : kind))]{ + (define-base-type base-type-name-id key tag))]{ 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[τ].}]} - 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.}} + Types defined with @racket[define-base-type] are automatically tagged with a +@racket[key2]-keyed (as specified in the @racket[define-syntax-category] +declaration) @racket[#%type] syntax property, unless second form above is used, +in which case the specified @tt{tag} is attached to the type using the +specified @tt{key}.} + + @item{@defform[(define-base-types base-type-name-id ...)]{Defines multiple base types, each using the default key.}} + @item{ @defform[(define-type-constructor name-id option ...) #:grammar ([option (code:line #:arity op n) (code:line #:arg-variances expr) (code:line #:extra-info stx)])]{ - Defines a type constructor that does not bind type variables. - Defining a type constructor @racket[τ] defines: - @itemlist[@item{@racket[τ], a macro for constructing an instance of type - @racket[τ], with the specified arity. - Validates inputs and expands to @racket[τ-], attaching kind.} - @item{@racket[τ-], an internal macro that expands to the internal - (i.e., fully expanded) type representation. Does not validate - inputs or attach kinds. This macro is useful when creating - a separate kind system, see @racket[define-internal-type-constructor].} - @item{@racket[τ?], a phase 1 predicate recognizing type @racket[τ].} - @item{@racket[~τ], a phase 1 @tech:pat-expander recognizing type @racket[τ].}] + Defines a type constructor (that does not bind type variables). + Defining a type constructor @racket[τ] subsequently defines: + @itemlist[ + + @item{@racket[τ], a macro for constructing an instance of type @racket[τ], + with the specified arity. Validates inputs and expands to + @racket[τ-], attaching @racket[#%type] at @tt{key2}.} + + @item{@racket[τ-], an internal macro that expands to the internal + (i.e., fully expanded) type representation. Does not validate inputs + or attach any extra properties. This macro is useful when creating a + separate kind system, see @racket[define-internal-type-constructor].} + + @item{@racket[τ?], a phase 1 predicate recognizing type @racket[τ].} + @item{@racket[~τ], a phase 1 @tech:pat-expander recognizing type + @racket[τ].}] The @racket[#:arity] argument specifies the valid shapes for the type. For example @@ -274,16 +352,18 @@ are probably @racket[define-typed-syntax], and the type-defining forms (list covariant))])))] The @racket[#:extra-info] argument is useful for attaching additional - metainformation to types, for example to implement pattern matching.}} + metainformation to types, for example to communicate accessors to a pattern + matching form.}} @item{ @defform[(define-internal-type-constructor name-id option ...) #:grammar - ([option (code:line #:arity op n) - (code:line #:arg-variances expr) + ([option (code:line #:arg-variances expr) (code:line #:extra-info stx)])]{ - Like @racket[define-type-constructor], except the surface macro is not defined. - Use this form, for example, when creating a separate kind system so that - you can still use other Turnstile conveniences like pattern expanders.}} + + Like @racket[define-type-constructor], except the surface macro is not +defined. Use this form, for example, when creating a separate kind system so +that you can still use other Turnstile conveniences like pattern expanders.}} + @item{ @defform[(define-binding-type name-id option ...) #:grammar @@ -311,12 +391,10 @@ are probably @racket[define-typed-syntax], and the type-defining forms @item{ @defform[(define-internal-binding-type name-id option ...) #:grammar - ([option (code:line #:arity op n) - (code:line #:bvs op n) - (code:line #:arr kindcon) + ([option (code:line #:arr kindcon) (code:line #:arg-variances expr) (code:line #:extra-info stx)])]{ - Analogous to @racket[define-internal-type-constructor].}} + Analogous to @racket[define-internal-type-constructor], but for binding types.}} @item{ @defform[(type-out ty-id)]{ A @racket[provide]-spec that, given @racket[ty-id], provides @racket[ty-id], @@ -326,7 +404,7 @@ and provides @racket[for-syntax] a predicate @racket[ty-id?] and a @tech:pat-exp 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 +immediately before attaching 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. @@ -420,6 +498,13 @@ equality, but includes alpha-equivalence. syntax objects with shape @racket[(b:type-bind ...)].}} @item{@defthing[type-ann stx-class]{A syntax class recognizing syntax objects with shape @racket[{τ:type}] where the braces are required.}} + + @item{@defproc[(assign-type [e syntax?] [τ syntax?]) syntax?]{ +Phase 1 function that calls @racket[current-type-eval] on @racket[τ] and attaches it to @racket[e] using @tt{key1}.}} + + @item{@defproc[(typeof [e expr-stx]) type-stx]{ +Phase 1 function returning type of @racket[e], at @tt{key1}.}} + ] } @@ -460,13 +545,14 @@ Reuses @racket[name]s from @racket[base-lang].} To help avoid name conflicts, Turnstile re-provides all Racket bindings with a @litchar{-} suffix. These bindings are automatically used in some cases, e.g., -@racket[define-primop], but in general are useful for avoiding name conflicts. +@racket[define-primop], but in general are useful for avoiding name conflicts, +particularly for commonly used macros like @racket[#%app]. @; Sec: turnstile/lang ---------------------------------------------- @section[#:tag "turnstilelang"]{@hash-lang[] @racketmodname[turnstile]/lang} Languages implemented using @hash-lang[] @racketmodname[turnstile] -must additionally provide @racket[#%module-begin] and other forms required by +must manually provide @racket[#%module-begin] and other forms required by Racket. For convenience, Turnstile additionally supplies @hash-lang[] @@ -482,18 +568,30 @@ 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. -@defproc[(assign-type [e syntax?] [τ syntax?]) syntax?]{ -Phase 1 function that calls @racket[current-type-eval] on @racket[τ] and attaches it to @racket[e]} - -@defproc[(typeof [e expr-stx]) type-stx]{ -Phase 1 function returning type of @racket[e].} - @defproc[(infer [es (listof expr-stx)] [#:ctx ctx (listof bindings) null] - [#:tvctx tvctx (listof tyvar-bindings) null]) (list tvs xs es τs)]{ -Phase 1 function expanding a list of expressions, in the given contexts and computes their types. - Returns the expanded expressions, their types, and expanded identifiers from the - contexts, e.g. @racket[(infer (list #'(+ x 1)) #:ctx ([x : Int]))].} + [#:tvctx tvctx (listof tyvar-bindings) null] + [#:tag tag symbol? ':]) + (list tvs xs es τs)]{ + +Phase 1 function expanding a list of expressions, in the given contexts and +computes their types. Returns the expanded expressions, their types, and +expanded identifiers from the contexts, e.g. + +@racket[(infer (list #'(+ x 1)) #:ctx ([x : Int]))] + +might return + +@racket[(list '() (list #'x-) (list #'(#%plain-app x- 1))(list #'Int))]. + +The context elements have the same shape as in @racket[define-typed-syntax]. +The contexts use @racket[let*] semantics, where each binding is in scope for +subsequent bindings. + +Use the @tt{tag} keyword argument to specify the key for the +returned "type". The default key is @litchar{:}. For example, a programmer may +want to specify a @litchar{::} key when using @racket[infer] to compute the +kinds on types.} @defproc[(subst [τ type-stx] [x id] diff --git a/turnstile/turnstile.rkt b/turnstile/turnstile.rkt @@ -5,9 +5,9 @@ define-typed-syntax define-syntax-category (rename-out [define-typed-syntax define-typerule] [define-typed-syntax define-syntax/typecheck]) - (for-syntax syntax-parse/typed-syntax + (for-syntax syntax-parse/typecheck (rename-out - [syntax-parse/typed-syntax syntax-parse/typecheck]))) + [syntax-parse/typecheck syntax-parse/typed-syntax]))) (require (except-in (rename-in macrotypes/typecheck @@ -25,16 +25,18 @@ ;; xs- ; a stx-list of the expanded versions of variables in the ctx ;; es*- ; a nested list a depth given by the depth argument, with the same structure ;; ; as es*, containing the expanded es*, with the types attached - (define (infer/depth #:ctx ctx #:tvctx tvctx depth es* origs*) + (define (infer/depth #:ctx ctx #:tvctx tvctx depth es* origs* + #:tag [tag (current-tag)]) (define flat (stx-flatten/depth-lens depth)) (define es (lens-view flat es*)) (define origs (lens-view flat origs*)) (define/with-syntax [tvxs- xs- es- _] - (infer #:tvctx tvctx #:ctx ctx (stx-map pass-orig es origs))) - (define es*- (lens-set flat es* #'es-)) + (infer #:tvctx tvctx #:ctx ctx (stx-map pass-orig es origs) #:tag tag)) + (define es*- (lens-set flat es* #`es-)) (list #'tvxs- #'xs- es*-)) ;; infers/depths - (define (infers/depths clause-depth tc-depth tvctxs/ctxs/ess/origss*) + (define (infers/depths clause-depth tc-depth tvctxs/ctxs/ess/origss* + #:tag [tag (current-tag)]) (define flat (stx-flatten/depth-lens clause-depth)) (define tvctxs/ctxs/ess/origss (lens-view flat tvctxs/ctxs/ess/origss*)) @@ -42,7 +44,7 @@ (for/list ([tvctx/ctx/es/origs (in-list (stx->list tvctxs/ctxs/ess/origss))]) (match-define (list tvctx ctx es origs) (stx->list tvctx/ctx/es/origs)) - (infer/depth #:tvctx tvctx #:ctx ctx tc-depth es origs))) + (infer/depth #:tvctx tvctx #:ctx ctx tc-depth es origs #:tag tag))) (define res (lens-set flat tvctxs/ctxs/ess/origss* tcs)) res) @@ -92,44 +94,45 @@ (define-splicing-syntax-class ⇒-prop #:datum-literals (⇒) #:attributes (e-pat) - [pattern (~or (~seq ⇒ tag-pat ; implicit : tag - (~parse tag #':) (tag-prop:⇒-prop) ...) + [pattern (~or (~seq ⇒ tag-pat ; implicit tag + (~parse tag #',(current-tag)) + (tag-prop:⇒-prop) ...) (~seq ⇒ tag:id tag-pat (tag-prop:⇒-prop) ...)) ; explicit tag #:with e-tmp (generate-temporary) #:with e-pat #'(~and e-tmp (~parse (~and tag-prop.e-pat ... tag-pat) - (typeof #'e-tmp #:tag 'tag)))]) + (detach #'e-tmp `tag)))]) (define-splicing-syntax-class ⇒-prop/conclusion #:datum-literals (⇒) #:attributes (tag tag-expr) - [pattern (~or (~seq ⇒ tag-stx - (~parse tag #':) - (~parse (tag-prop.tag ...) #'()) - (~parse (tag-prop.tag-expr ...) #'())) + [pattern (~or (~seq ⇒ tag-stx ; implicit tag + (~parse tag #',(current-tag)) + (~parse (tag-prop.tag ...) #'()) + (~parse (tag-prop.tag-expr ...) #'())) (~seq ⇒ tag:id tag-stx (tag-prop:⇒-prop/conclusion) ...)) #:with tag-expr (for/fold ([tag-expr #'#`tag-stx]) - ([k (in-list (syntax->list #'[tag-prop.tag ...]))] - [v (in-list (syntax->list #'[tag-prop.tag-expr ...]))]) + ([k (in-stx-list #'[tag-prop.tag ...])] + [v (in-stx-list #'[tag-prop.tag-expr ...])]) (with-syntax ([tag-expr tag-expr] [k k] [v v]) - #'(assign-type tag-expr #:tag 'k v)))]) + #'(attach tag-expr `k ((current-ev) v))))]) (define-splicing-syntax-class ⇐-prop - #:datum-literals (⇐ :) + #:datum-literals (⇐) #:attributes (τ-stx e-pat) - [pattern (~or (~seq ⇐ τ-stx) - (~seq ⇐ : τ-stx)) + [pattern (~or (~seq ⇐ τ-stx (~parse tag #',(current-tag))) + (~seq ⇐ tag:id τ-stx)) #:with e-tmp (generate-temporary) #:with τ-tmp (generate-temporary) #:with τ-exp (generate-temporary) #:with e-pat - #'(~and e-tmp + #`(~and e-tmp (~parse τ-exp (get-expected-type #'e-tmp)) - (~parse τ-tmp (typeof #'e-tmp)) + (~parse τ-tmp (detach #'e-tmp `tag)) (~parse (~post - (~fail #:when (and (not (typecheck? #'τ-tmp #'τ-exp)) + (~fail #:when (and (not (check? #'τ-tmp #'τ-exp)) (get-orig #'e-tmp)) (typecheck-fail-msg/1 #'τ-exp #'τ-tmp #'e-tmp))) (get-orig #'e-tmp)))]) @@ -153,6 +156,12 @@ (define-splicing-syntax-class id+props+≫ #:datum-literals (≫) #:attributes ([x- 1] [ctx 1]) + [pattern (~seq (~and X:id (~not _:elipsis))) + #:with [x- ...] #'[_] + #:with [ctx ...] #'[[X :: #%type]]] + [pattern (~seq X:id ooo:elipsis) + #:with [x- ...] #'[_ ooo] + #:with [ctx ...] #'[[X :: #%type] ooo]] [pattern (~seq [x:id ≫ x--:id props:props]) #:with [x- ...] #'[x--] #:with [ctx ...] #'[[x props.stuff ...]]] @@ -165,7 +174,7 @@ #:with [x- ...] #'[ctx1.x- ... ...] #:with [ctx ...] #'[ctx1.ctx ... ...]]) (define-syntax-class tc-elem - #:datum-literals (⊢ ⇒ ⇐ ≫ :) + #:datum-literals (⊢ ⇒ ⇐ ≫) #:attributes (e-stx e-stx-orig e-pat) [pattern [e-stx ≫ e-pat* props:⇒-props] #:with e-stx-orig #'e-stx @@ -198,9 +207,9 @@ (define max-d (apply max 0 ds))] #:with depth (add1 max-d) #:with [[es-stx* es-stx-orig* es-pat*] ...] - (for/list ([tc-es-stx (in-list (syntax->list #'[tc.es-stx ...]))] - [tc-es-stx-orig (in-list (syntax->list #'[tc.es-stx-orig ...]))] - [tc-es-pat (in-list (syntax->list #'[tc.es-pat ...]))] + (for/list ([tc-es-stx (in-stx-list #'[tc.es-stx ...])] + [tc-es-stx-orig (in-stx-list #'[tc.es-stx-orig ...])] + [tc-es-pat (in-stx-list #'[tc.es-pat ...])] [d (in-list ds)]) (list (add-lists tc-es-stx (- max-d d)) @@ -226,58 +235,59 @@ #'[ooo ...]) #:with tvctxs/ctxs/ess/origs (with-depth - #'[(tvctx.ctx ...) (ctx.ctx ...) tc.es-stx tc.es-stx-orig] + #`[(tvctx.ctx ...) (ctx.ctx ...) tc.es-stx tc.es-stx-orig] #'[ooo ...]) #:with pat - #'(~post + #`(~post (~post (~parse tcs-pat - (infers/depths 'clause-depth 'tc.depth #'tvctxs/ctxs/ess/origs))))] + (infers/depths 'clause-depth 'tc.depth #`tvctxs/ctxs/ess/origs + #:tag (current-tag)))))] ) (define-splicing-syntax-class clause #:attributes (pat) - #:datum-literals (τ⊑ τ=) + #:datum-literals (τ⊑ τ=) ; TODO: drop the τ in τ⊑ and τ= [pattern :tc-clause] [pattern [a τ⊑ b] #:with pat #'(~post - (~fail #:unless (typecheck? #'a #'b) + (~fail #:unless (check? #'a #'b) (typecheck-fail-msg/1/no-expr #'b #'a)))] [pattern [a τ⊑ b #:for e] #:with pat #'(~post - (~fail #:unless (typecheck? #'a #'b) + (~fail #:unless (check? #'a #'b) (typecheck-fail-msg/1 #'b #'a #'e)))] [pattern (~seq [a τ⊑ b] ooo:elipsis) #:with pat #'(~post - (~fail #:unless (typechecks? #'[a ooo] #'[b ooo]) + (~fail #:unless (checks? #'[a ooo] #'[b ooo]) (typecheck-fail-msg/multi/no-exprs #'[b ooo] #'[a ooo])))] [pattern (~seq [a τ⊑ b #:for e] ooo:elipsis) #:with pat #'(~post - (~fail #:unless (typechecks? #'[a ooo] #'[b ooo]) + (~fail #:unless (checks? #'[a ooo] #'[b ooo]) (typecheck-fail-msg/multi #'[b ooo] #'[a ooo] #'[e ooo])))] [pattern [a τ= b] #:with pat #'(~post - (~fail #:unless ((current-type=?) #'a #'b) + (~fail #:unless ((current=?) #'a #'b) (typecheck-fail-msg/1/no-expr #'b #'a)))] [pattern [a τ= b #:for e] #:with pat #'(~post - (~fail #:unless ((current-type=?) #'a #'b) + (~fail #:unless ((current=?) #'a #'b) (typecheck-fail-msg/1 #'b #'a #'e)))] [pattern (~seq [a τ= b] ooo:elipsis) #:with pat #'(~post - (~fail #:unless (types=? #'[a ooo] #'[b ooo]) + (~fail #:unless (=s? #'[a ooo] #'[b ooo]) (typecheck-fail-msg/multi/no-exprs #'[b ooo] #'[a ooo])))] [pattern (~seq [a τ= b #:for e] ooo:elipsis) #:with pat #'(~post - (~fail #:unless (types=? #'[a ooo] #'[b ooo]) + (~fail #:unless (=s? #'[a ooo] #'[b ooo]) (typecheck-fail-msg/multi #'[b ooo] #'[a ooo] #'[e ooo])))] [pattern (~seq #:when condition:expr) #:with pat @@ -296,7 +306,7 @@ #'(~post (~fail #:unless condition message))] ) (define-syntax-class last-clause - #:datum-literals (⊢ ≫ ≻ ⇒ ⇐ :) + #:datum-literals (⊢ ≫ ≻ ⇒ ⇐) #:attributes ([pat 0] [stuff 1] [body 0]) ;; ⇒ conclusion [pattern (~or [⊢ pat ≫ e-stx props:⇒-props/conclusion] @@ -304,21 +314,25 @@ #:with [stuff ...] #'[] #:with body:expr (for/fold ([body #'(quasisyntax/loc this-syntax e-stx)]) - ([k (in-list (syntax->list #'[props.tag ...]))] - [v (in-list (syntax->list #'[props.tag-expr ...]))]) + ([k (in-stx-list #'[props.tag ...])] + [v (in-stx-list #'[props.tag-expr ...])]) (with-syntax ([body body] [k k] [v v]) - #'(assign-type body #:tag 'k v)))] + #`(attach body `k ((current-ev) v))))] ;; ⇒ conclusion, implicit pat [pattern (~or [⊢ e-stx props:⇒-props/conclusion] [⊢ [e-stx props:⇒-props/conclusion]]) #:with :last-clause #'[⊢ [_ ≫ e-stx . props]]] ;; ⇐ conclusion - [pattern [⊢ (~and e-stx (~not [_ ≫ . rst]))] - #:with :last-clause #'[⊢ [_ ≫ e-stx ⇐ : _]]] - [pattern (~or [⊢ pat* ≫ e-stx ⇐ τ-pat] - [⊢ pat* ≫ e-stx ⇐ : τ-pat] - [⊢ [pat* ≫ e-stx ⇐ τ-pat]] - [⊢ [pat* ≫ e-stx ⇐ : τ-pat]]) + [pattern [⊢ (~and e-stx (~not [_ ≫ . rst]))] ;; TODO: this current tag isnt right? + #:with :last-clause #`[⊢ [_ ≫ e-stx ⇐ #,(datum->stx #'h (current-tag)) _]]] + [pattern (~or [⊢ pat* (~seq ≫ e-stx + ⇐ τ-pat ; implicit tag + (~parse tag #',(current-tag)))] + [⊢ pat* ≫ e-stx ⇐ tag:id τ-pat] ; explicit tag + [⊢ [pat* (~seq ≫ e-stx + ⇐ τ-pat ; implicit tag + (~parse tag #',(current-tag)))]] + [⊢ [pat* ≫ e-stx ⇐ tag:id τ-pat]]) ; explicit tag #:with stx (generate-temporary 'stx) #:with τ (generate-temporary #'τ-pat) #:with pat @@ -330,7 +344,7 @@ (~parse τ-pat #'τ)) #:with [stuff ...] #'[] #:with body:expr - #'(assign-type (quasisyntax/loc this-syntax e-stx) #`τ)] + #'(attach (quasisyntax/loc this-syntax e-stx) `tag #`τ)] ;; macro invocations [pattern [≻ e-stx] #:with :last-clause #'[_ ≻ e-stx]] @@ -346,11 +360,12 @@ #:with body:expr ;; should never get here #'(error msg)]) - (define-splicing-syntax-class pat #:datum-literals (⇐ :) + (define-splicing-syntax-class pat #:datum-literals (⇐) [pattern (~seq pat) #:attr transform-body identity] - [pattern (~or (~seq pat* left:⇐ τ-pat) - (~seq pat* left:⇐ : τ-pat)) + [pattern (~or (~seq pat* left:⇐ τ-pat ; implicit tag + (~parse tag #',(current-tag))) + (~seq pat* left:⇐ tag:id τ-pat)) ; explicit tag #:with stx (generate-temporary 'stx) #:with τ (generate-temporary #'τ-pat) #:with b (generate-temporary 'body) @@ -363,11 +378,10 @@ (~parse τ-pat #'τ)) #:attr transform-body (lambda (body) - #`(let ([b #,body]) - (when (and (typeof b) - (not (typecheck? (typeof b) #'τ))) - (raise-⇐-expected-type-error #'left b #'τ (typeof b))) - (assign-type b #'τ)))]) + #`(let* ([b #,body][ty-b (detach b `tag)]) + (when (and ty-b (not (check? ty-b #'τ))) + (raise-⇐-expected-type-error #'left b #'τ ty-b)) + (attach b `tag #'τ)))]) (define-syntax-class rule #:datum-literals (≫) [pattern [pat:pat ≫ clause:clause ... @@ -388,62 +402,59 @@ (require (for-meta 1 'syntax-classes) (for-meta 2 'syntax-classes)) -(define-syntax define-typed-syntax - (lambda (stx) - (syntax-parse stx - ;; single-clause def - [(def (name:id . pats) . rst) - ;; cannot always bind name as pat var, eg #%app, so replace with _ - #:with r:rule #'[(_ . pats) . rst] - #'(-define-typed-syntax name r.norm)] - ;; multi-clause def - [(def name:id - (~and (~seq kw-stuff ...) :stxparse-kws) - rule:rule - ...+) - #'(-define-typed-syntax - name - kw-stuff ... - rule.norm - ...)]))) - (begin-for-syntax - (define-syntax syntax-parse/typed-syntax - (lambda (stx) - (syntax-parse stx - [(stxparse - stx-expr + (define-syntax syntax-parse/typecheck + (syntax-parser + [(_ stx-expr (~and (~seq kw-stuff ...) :stxparse-kws) - rule:rule - ...) - #'(syntax-parse - stx-expr - kw-stuff ... - rule.norm - ...)])))) + rule:rule ...) + #'(syntax-parse stx-expr kw-stuff ... rule.norm ...)]))) + +;; macrotypes/typecheck.rkt already defines (-define-syntax-category type); +;; - just add the "def-named-syntax" part of the def-stx-cat macro below +;; TODO: eliminate code dup with def-named-stx in define-stx-cat below? +(define-syntax define-typed-syntax + (syntax-parser + ;; single-clause def + [(_ (rulename:id . pats) . rst) + ;; using #'rulename as patvar may cause problems, eg #%app, so use _ + #'(define-typed-syntax rulename [(_ . pats) . rst])] + ;; multi-clause def + ;; - let stx-parse/tychk match :rule (dont double-match) + [(_ rulename:id + (~and (~seq kw-stuff ...) :stxparse-kws) + rule ...+) + #'(define-syntax (rulename stx) + (parameterize ([current-check-relation (current-typecheck-relation)] + [current-ev (current-type-eval)] + [current-tag (type-key1)]) + (syntax-parse/typecheck stx kw-stuff ... rule ...)))])) (define-syntax define-syntax-category - (lambda (stx) - (syntax-parse stx - [(_ name:id) + (syntax-parser + [(_ name:id) ; default key1 = ': for types + #'(define-syntax-category : name)] + [(_ key:id name:id) ; default key2 = ':: for kinds + #`(define-syntax-category key name #,(mkx2 #'key))] + [(_ key1:id name:id key2:id) #:with def-named-syntax (format-id #'name "define-~aed-syntax" #'name) - #:with check-relation (format-id #'name "current-~acheck-relation" #'name) + #:with new-check-rel (format-id #'name "current-~acheck-relation" #'name) + #:with new-eval (format-id #'name "current-~a-eval" #'name) #'(begin - (-define-syntax-category name) - (define-syntax (def-named-syntax stx) - (syntax-parse stx + (-define-syntax-category key1 name key2) + (define-syntax def-named-syntax + (syntax-parser ;; 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 + [(_ (rulename:id . pats) . rst) + ;; #'rulename as a pat var may cause problems, eg #%app, so use _ + #'(def-named-syntax rulename [(_ . pats) . rst])] + ;; 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 (... ...))))])))]))) + rule (... ...+)) ; let stx-parse/tychk match :rule stxcls + #'(define-syntax (rulename stx) + (parameterize ([current-check-relation (new-check-rel)] + [current-ev (new-eval)] + [current-tag 'key1]) + (syntax-parse/typecheck stx kw-stuff (... ...) + rule (... ...))))])))]))