www

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

commit b439327d78085069960714b0ec9116edfc3fd264
parent 51f9008b9910872afb3be92c3e4914771bf54661
Author: Stephen Chang <stchang@ccs.neu.edu>
Date:   Wed, 20 Apr 2016 13:51:05 -0400

define-type automatically defines accessors and predicates for record variants

Diffstat:
Mtapl/mlish.rkt | 24++++++++++++++++++++++--
Mtapl/tests/mlish-tests.rkt | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtapl/tests/rackunit-typechecking.rkt | 8+++++++-
3 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/tapl/mlish.rkt b/tapl/mlish.rkt @@ -1,5 +1,4 @@ #lang s-exp "typecheck.rkt" -(require (for-syntax syntax/id-set)) (require racket/fixnum racket/flonum) (extends "ext-stlc.rkt" #:except #%app λ → + - void = zero? sub1 add1 not let let* and #%datum begin @@ -10,7 +9,7 @@ (require (only-in "ext-stlc.rkt" →?)) (require (only-in "sysf.rkt" ~∀ ∀ ∀? Λ)) (reuse × tup proj define-type-alias #:from "stlc+rec-iso.rkt") -(require (only-in "stlc+rec-iso.rkt" ~× ×?)) +(require (only-in "stlc+rec-iso.rkt" ~× ×?)) ; using current-type=? from here (provide (rename-out [ext-stlc:and and] [ext-stlc:#%datum #%datum])) (reuse member length reverse list-ref cons nil isnil head tail list #:from "stlc+cons.rkt") (require (prefix-in stlc+cons: (only-in "stlc+cons.rkt" list cons nil))) @@ -240,9 +239,14 @@ #:with ((e_arg ...) ...) (stx-map generate-temporaries #'((τ ...) ...)) #:with ((e_arg- ...) ...) (stx-map generate-temporaries #'((τ ...) ...)) #:with ((τ_arg ...) ...) (stx-map generate-temporaries #'((τ ...) ...)) + #:with ((exposed-acc ...) ...) + (stx-map + (λ (C fs) (stx-map (λ (f) (format-id C "~a-~a" C f)) fs)) + #'(Cons ...) #'((fld ...) ...)) #:with ((acc ...) ...) (stx-map (λ (S fs) (stx-map (λ (f) (format-id S "~a-~a" S f)) fs)) #'(StructName ...) #'((fld ...) ...)) #:with (Cons? ...) (stx-map mk-? #'(StructName ...)) + #:with (exposed-Cons? ...) (stx-map mk-? #'(Cons ...)) #`(begin (define-syntax (NameExtraInfo stx) (syntax-parse stx @@ -252,6 +256,22 @@ #:extra-info 'NameExtraInfo #:no-provide) (struct StructName (fld ...) #:reflection-name 'Cons #:transparent) ... + (define-syntax (exposed-acc stx) ; accessor for records + (syntax-parse stx + [_:id (⊢ acc (∀ (X ...) (ext-stlc:→ (Name X ...) τ)))] + [(o . rst) ; handle if used in fn position + #:with app (datum->syntax #'o '#%app) + #`(app + #,(assign-type #'acc #'(∀ (X ...) (ext-stlc:→ (Name X ...) τ))) + . rst)])) ... ... + (define-syntax (exposed-Cons? stx) ; predicates for each variant + (syntax-parse stx + [_:id (⊢ Cons? (∀ (X ...) (ext-stlc:→ (Name X ...) Bool)))] + [(o . rst) ; handle if used in fn position + #:with app (datum->syntax #'o '#%app) + #`(app + #,(assign-type #'Cons? #'(∀ (X ...) (ext-stlc:→ (Name X ...) Bool))) + . rst)])) ... (define-syntax (Cons stx) (syntax-parse stx ; no args and not polymorphic diff --git a/tapl/tests/mlish-tests.rkt b/tapl/tests/mlish-tests.rkt @@ -310,6 +310,59 @@ (check-type (λ ([x : X]) (λ ([y : Y]) y)) : (→/test {X} X (→/test {Y} Y Y))) (check-not-type (λ ([x : X]) (λ ([y : Y]) x)) : (→/test X (→ X X))) +;; records and automatically-defined accessors and predicates +(define-type (RecoTest X Y) + (RT1 [x : X] [y : Y] [z : String]) + (RT2 [a : Y] [b : X] [c : (List X)]) + (RT3 X Y)) ; mixing records and non-records allowed + +(check-type RT1-x : (→/test (RecoTest X Y) X)) +(check-type RT1-y : (→/test (RecoTest X Y) Y)) +(check-type RT1-z : (→/test (RecoTest X Y) String)) +(check-type RT2-a : (→/test (RecoTest X Y) Y)) +(check-type RT2-b : (→/test (RecoTest X Y) X)) + +(check-type RT1? : (→/test (RecoTest X Y) Bool)) +(check-type RT2? : (→/test (RecoTest X Y) Bool)) +(check-type RT3? : (→/test (RecoTest X Y) Bool)) + +(check-type (RT1-x (RT1 1 #t "2")) : Int -> 1) +(check-type (RT1-y (RT1 1 #t "2")) : Bool -> #t) +(check-type (RT1-z (RT1 1 #t "2")) : String -> "2") + +(check-type (RT2-a (RT2 1 #f Nil)) : Int -> 1) +(check-type (RT2-b (RT2 1 #f Nil)) : Bool -> #f) +(check-type (RT2-c (RT2 1 #f Nil)) : (List Bool) -> Nil) + +(check-type (RT1? (RT1 1 2 "3")) : Bool -> #t) +(check-type (RT1? (RT2 1 2 Nil)) : Bool -> #f) +(check-type (RT1? (RT3 1 "2")) : Bool -> #f) +(check-type (RT3? (RT3 1 2)) : Bool -> #t) +(check-type (RT3? (RT1 1 2 "3")) : Bool -> #f) + +(typecheck-fail RT3-x #:with-msg "unbound identifier") + +;; accessors produce runtime exception if given wrong variant +(check-runtime-exn (RT1-x (RT2 1 #f (Cons #t Nil)))) +(check-runtime-exn (RT1-y (RT2 1 #f (Cons #t Nil)))) +(check-runtime-exn (RT1-z (RT2 1 #f (Cons #t Nil)))) +(check-runtime-exn (RT1-x (RT3 1 2))) +(check-runtime-exn (RT2-a (RT1 1 #f "2"))) +(check-runtime-exn (RT2-c (RT1 1 #f "2"))) +(check-runtime-exn (RT2-c (RT1 1 #f "2"))) +(check-runtime-exn (RT2-a (RT3 #f #t))) + +;; non-match version +(define (rt-fn [rt : (RecoTest X Y)] -> X) + (if (RT1? rt) + (RT1-x rt) + (if (RT2? rt) + (RT2-b rt) + (match rt with [RT3 x y -> x][RT1 x y z -> x][RT2 a b c -> b])))) +(check-type (rt-fn (RT1 1 #f "3")) : Int -> 1) +(check-type (rt-fn (RT2 #f 2 Nil)) : Int -> 2) +(check-type (rt-fn (RT3 10 20)) : Int -> 10) + ; ext-stlc tests -------------------------------------------------- diff --git a/tapl/tests/rackunit-typechecking.rkt b/tapl/tests/rackunit-typechecking.rkt @@ -1,6 +1,6 @@ #lang racket/base (require (for-syntax rackunit syntax/srcloc) rackunit "../typecheck.rkt") -(provide check-type typecheck-fail check-not-type check-props) +(provide check-type typecheck-fail check-not-type check-props check-runtime-exn) (begin-for-syntax (define (add-esc s) (string-append "\\" s)) @@ -91,3 +91,9 @@ "Expected type check failure but expression ~a has valid type, OR wrong err msg received." (syntax->datum #'e))))) #'(void)])) + +(define-syntax (check-runtime-exn stx) + (syntax-parse stx + [(_ e) + #:with e- (expand/df #'e) + (syntax/loc stx (check-exn exn:fail? (lambda () e-)))]))