add nat-ind, nat-rec, right/left plus id examples

This commit is contained in:
Stephen Chang 2017-08-04 12:15:32 -04:00
parent 3fd22a9058
commit bbb6b0a694
2 changed files with 295 additions and 52 deletions

View File

@ -7,13 +7,21 @@
(provide (rename-out [#%type *])
Π
= eq-refl eq-elim
;; Nat Z S nat-ind
λ
(rename-out [app #%app]) ann
= eq-refl eq-elim
Nat (rename-out [Zero Z][Succ S]) nat-ind #;nat-rec
λ (rename-out [app #%app]) ann
define define-type-alias
)
;; TODO:
;; - map #%datum to S and Z
;; - rename define-type-alias to define
;; - add "assistant" part
;; - provide match and match/lambda so nat-ind can be fn
;; - eg see https://gist.github.com/AndrasKovacs/6769513
;; - add dependent existential
;; - remove debugging code?
;; #;(begin-for-syntax
;; (define old-ty= (current-type=?))
;; (current-type=?
@ -24,12 +32,15 @@
;; (current-typecheck-relation (current-type=?)))
;(define-syntax-category : kind) ; defines #%kind for #%type
;; (define-base-type Nat)
;; set Type : Type
;; alternatively, could define new base type Type,
;; and make #%type typecheck with Type
(begin-for-syntax
(define debug? #f)
(define type-eq-debug? #f)
(define debug-match? #f)
;; TODO: fix `type` stx class
;; (define old-type? (current-type?))
;; (current-type?
@ -37,11 +48,12 @@
(define old-relation (current-typecheck-relation))
(current-typecheck-relation
(lambda (t1 t2)
;; (pretty-print (stx->datum t1))
;; (pretty-print (stx->datum t2))
(when type-eq-debug?
(pretty-print (stx->datum t1))
(pretty-print (stx->datum t2)))
;; assumed #f can only come from (typeof #%type)
;; (so this wont work when interacting with untyped code)
(or (and (false? (syntax-e t1)) (#%type? t2)) ; set Type : Type
(or (and (false? (syntax-e t1)) (#%type? t2)) ; assign Type : Type
(old-relation t1 t2)))))
(define-for-syntax Type ((current-type-eval) #'#%type))
@ -58,9 +70,11 @@
[ (∀- (X- ...) (→- τ_in- ... τ_out-)) #%type])
;; abbrevs for Π
;; (→ τ_in τ_out) == (Π (unused : τ_in) τ_out)
(define-simple-macro ( τ_in ... τ_out)
#:with (X ...) (generate-temporaries #'(τ_in ...))
(Π ([X : τ_in] ...) τ_out))
;; (∀ (X) τ) == (∀ ([X : #%type]) τ)
(define-simple-macro ( (X ...) τ)
(Π ([X : #%type] ...) τ))
@ -151,30 +165,50 @@
;; [(~literal +-) (stx+ args)]
;; [(~literal zero?-) (apply zero? (stx->nats args))])))
(define-for-syntax debug? #f)
;; TODO: fix orig after subst
;; TODO: fix orig after subst, for err msgs
;; app/eval should not try to ty check anymore
(define-syntax app/eval
(syntax-parser
#;[(_ f . args) #:do[(printf "app/evaling ")
(printf "f: ~a\n" (stx->datum #'f))
(printf "args: ~a\n" (stx->datum #'args))]
#:when #f #'void]
[(_ f:id n P zc sc)
#:with (_ m/d . _) (local-expand #'(#%app match/delayed 'do 'nt 'ca 're) 'expression null)
#:when (free-identifier=? #'m/d #'f)
;; TODO: need to attach type?
#'(match/nat n P zc sc)]
;; TODO: apply to only lambda args or all args?
[(_ (~and f ((~literal #%plain-lambda) (x ...) e)) e_arg ...)
#:do[(when debug?
(printf "apping: ~a\n" (stx->datum #'f))
(printf "args\n")
(pretty-print (stx->datum #'(e_arg ...))))]
(pretty-print (stx->datum #'(e_arg ...)))
(printf "expected type\n")
(pretty-print (stx->datum (typeof this-syntax))))]
; #:with (~Π ([X : _] ...) τ_out) (typeof #'f) ; must re-subst in type
;; TODO: need to replace all #%app- in this result with app/eval again
;; and then re-expand
; #:with ((~literal #%plain-app) newf . newargs) #'e
; #:do[(displayln #'newf)(displayln #'newargs)(displayln (stx-car #'e+))]
#:with app (datum->syntax (if (identifier? #'e) #'e (stx-car #'e)) '#%app)
#:with e+ (substs #'(app/eval e_arg ...) #'(app x ...) #'e free-identifier=?)
#:with r-app (datum->syntax (if (identifier? #'e) #'e (stx-car #'e)) '#%app)
;; TODO: is this assign-type needed only for tests?
;; eg, see id tests in dep2-peano.rkt
#:with ty (typeof this-syntax)
#:with e-inst (substs #'(app/eval e_arg ...) #'(r-app x ...) #'e free-identifier=?)
;; some apps may not have type (eg in internal reps)
#:with e+ (if (syntax-e #'ty) (assign-type #'e-inst #'ty) #'e-inst)
#:do[(when debug?
(displayln "res:--------------------")
(pretty-print (stx->datum #'e+))
;; (displayln "actual type:")
;; (pretty-print (stx->datum (typeof #'e+)))
;; (displayln "new type:")
;; (pretty-print (stx->datum (substs #'(e_arg ...) #'(X ...) (typeof #'e+))))
;; (displayln "res expanded:------------------------")
;; (pretty-print
;; (stx->datum (local-expand (substs #'(e_arg ...) #'(x ...) #'e) 'expression null)))
(displayln "res app/eval recur expanded-----------------------"))]
(displayln "res app/eval re-expanding-----------------------"))]
#:with ((~literal let-values) () ((~literal let-values) () e++))
(local-expand
#'(let-syntax (#;[app (make-rename-transformer #'app/eval)]
@ -182,6 +216,7 @@
'expression null)
#:do[(when debug?
(pretty-print (stx->datum #'e++))
; (pretty-print (stx->datum (typeof #'e++)))
#;(local-expand
#'(let-syntax ([app (make-rename-transformer #'app/eval)]
#;[x (make-variable-like-transformer #'e_arg)]) e+)
@ -195,6 +230,8 @@
(pretty-print (stx->datum #'args)))]
#:with f+ (expand/df #'f)
#:with args+ (stx-map expand/df #'args)
;; TODO: need to attach type?
; #:with ty (typeof this-syntax)
(syntax-parse #'f+
[((~literal #%plain-lambda) . _)
#'(app/eval f+ . args+)]
@ -203,6 +240,12 @@
;; TODO: fix orig after subst
(define-typed-syntax app
;; matching, ; TODO: where to put this?
#;[(_ f:id . args)
#:with (_ m/d . _) (local-expand #'(match/delayed 1 2 3 4) 'expression null)
#:when (free-identifier=? #'m/d #'f)
------------
[ (match/nat . args)]]
[(_ e_fn e_arg ...)
#:do[(when debug?
(displayln "TYPECHECKING")
@ -403,50 +446,128 @@
(stlc+lit:ann (begin e ...) : ty_out))))]])
;; ;; peano nums -----------------------------------------------------------------
;; (define-typed-syntax Z
;; [_:id ≫ --- [⊢ 0 ⇒ Nat]])
;; peano nums -----------------------------------------------------------------
(define-base-type Nat)
;; (define-typed-syntax (S n) ≫
;; [⊢ n ≫ n- ⇐ Nat]
;; -----------
;; [⊢ (#%app- +- n- 1) ⇒ Nat])
;; #;(define-typed-syntax (sub1 n) ≫
;; [⊢ n ≫ n- ⇐ Nat]
;; #:do[(displayln #'n-)]
;; -----------
;; [⊢ (#%app- -- n- 1) ⇒ Nat])
(struct Z () #:transparent)
(struct S (n) #:transparent)
;; ;; generalized recursor over natural nums
;; ;; (cases dispatched in #%app)
(define-typed-syntax Zero
[_:id --- [ (Z) Nat]])
(define-typed-syntax (Succ n)
[ n n- Nat]
-----------
[ (S n-) Nat])
#;(define-typed-syntax (sub1 n)
[ n n- Nat]
#:do[(displayln #'n-)]
-----------
[ (#%app- -- n- 1) Nat])
;; generalized recursor over natural nums
;; (cases dispatched in #%app)
;; (define- (nat-ind- P z s n) (#%app- void))
;; (define-syntax nat-ind
;; (make-variable-like-transformer
;; (assign-type
;; #'nat-ind-
;; #'(Π ([P : (→ Nat #%type)]
;; [z : (P Z)]
;; [s : (Π ([k : Nat]) (→ (P k) (P (S k))))]
;; [z : (app P Zero)]
;; [s : (Π ([k : Nat]) (→ (app P k) (app P (Succ k))))]
;; [n : Nat])
;; (P n)))))
;; (app P n)))))
;; #;(define-type-alias nat-ind
;; (λ ([P : (→ Nat #%type)]
;; [z : (P Z)]
;; [s : (Π ([k : Nat]) (→ (P k) (P (S k))))]
;; [n : Nat])
;; #'(#%app- nat-ind- P z s n)))
;; #;(define-typed-syntax (nat-ind P z s n) ≫
;; [⊢ P ≫ P- ⇐ (→ Nat #%type)]
;; ; [⊢ b ≫ b- ⇒ _] ; zero
;; ; [⊢ c ≫ c- ⇒ _] ; succ
;; ; [⊢ d ≫ d- ⇒ _]
;; [⊢ z ≫ z- ⇐ (P- Z)] ; zero
;; [⊢ s ≫ s- ⇐ (Π ([k : Nat]) (→ (P- k) (P- (S k))))] ; succ
;; [⊢ n ≫ n- ⇐ Nat]
;; #:with res (if (typecheck? #'n- (expand/df #'Z))
;; #'z-
;; #'(s- (nat-ind P- z- s- (sub1 n-))))
;; ----------------
;; [⊢ res ⇒ (P- n-)])
;; ; [≻ (P- d-)])
#;(define-type-alias nat-ind
(λ ([P : ( Nat #%type)]
[z : (P Z)]
[s : (Π ([k : Nat]) ( (P k) (P (S k))))]
[n : Nat])
#'(#%app- nat-ind- P z s n)))
(struct match/delayed (n P zc sc))
#;(define-syntax match/eval
(syntax-parser
[(_ n zc sc) #:do[(printf "matching: ~a\n" (stx->datum #'n))] #:when #f #'(void)]
[(_ ((~literal #%plain-app) z0:id) zc sc)
#:with (_ z1) (local-expand #'(Z) 'expression null)
#:when (free-identifier=? #'z0 #'z1)
#'zc]
[(_ ((~literal #%plain-app) s0:id m) zc sc)
#:with (_ s1 . _) (local-expand #'(S 'dont-care) 'expression null)
#:when (free-identifier=? #'s0 #'s1)
#:when (displayln 2)
#`(app sc (nat-rec #,(typeof #'zc) zc sc m))]
[(_ n zc sc) #'(match/delayed n zc sc)]))
;; this is an "eval" form; should not do any more type checking
;; otherwise, will get type errs some some subexprs may still have uninst tys
;; eg, zc and sc were typechecked with paramaterized P instead of inst'ed P
(define-syntax match/nat
(syntax-parser
[(_ n P zc sc)
#:do[(when debug-match?
(printf "match/nating: ~a\n" (stx->datum #'(n P zc sc)))
#;(printf "zc ty: ~a\n" (stx->datum (typeof #'zc)))
#;(printf "sc ty: ~a\n" (stx->datum (typeof #'sc))))]
#:when #f #'(void)]
[(_ (~and n ((~literal #%plain-app) z0:id)) P zc sc)
#:with (_ z1) (local-expand #'(#%app Z) 'expression null)
#:when (free-identifier=? #'z0 #'z1)
#:do [(when debug-match? (displayln 'zc))]
;; #:when (printf "match eval res zero ety: ~a\n" (stx->datum (typeof this-syntax)))
;; #:when (printf "match eval res zero ty: ~a\n" (stx->datum (typeof #'zc)))
(assign-type #'zc #'(app/eval P n))]
[(_ (~and n ((~literal #%plain-app) s0:id m)) P zc sc)
#:with (_ s1 . _) (local-expand #'(#%app S 'dont-care) 'expression null)
#:when (free-identifier=? #'s0 #'s1)
#:with ( ([_ : _] ...) τ_out) (typeof #'sc)
#:do[(when debug-match? (displayln 'sc))]
;; #:when (printf "match eval res succ ety: ~a\n" (stx->datum (typeof this-syntax)))
;; #:when (printf "match eval res succ ty: ~a\n" (stx->datum (typeof #'sc)))
;; #:when (printf "match eval res succ ty: ~a\n" (stx->datum (typeof #'(app/eval (app/eval sc m) (match/nat m P zc sc)))))
; #`(app sc m (nat-rec #,(typeof #'zc) zc sc m))]
; #:with ty (typeof this-syntax)
(assign-type
#`(app/eval #,(assign-type #'(app/eval sc m) #'τ_out) (match/nat m P zc sc))
#'(app/eval P n))
; #'res
; (if (syntax-e #'ty) (assign-type #'res #'ty) #'res)
#;(assign-type #`(app/eval #,(assign-type #'(app/eval sc m) #'τ_out) (match/nat m P zc sc)) (typeof this-syntax))]
[(_ n P zc sc)
#:do[(when debug-match? (displayln "delay match"))]
(assign-type #'(#%app match/delayed n P zc sc) #'(app/eval P n))]))
#;(define-typed-syntax (nat-rec ty zc sc n)
[ ty ty- #%type]
[ zc zc- ty-] ; zero case
[ sc sc- ( ty- ty-)] ; succ case
[ n n- Nat]
;; #:with res
;; (syntax-parse #'n-
;; [aaa #:do[(printf "matching: ~a\n" (stx->datum #'aaa))] #:when #f #'(void)]
;; [((~literal #%plain-app) (~literal Z)) #'zc-]
;; [((~literal #%plain-app) (~literal S) m) #'(app sc- (nat-rec zc- sc- m))])
--------------------
; [⊢ (match/eval n- zc- sc-) ⇒ ty-])
[ (match/nat n-
zc-
(λ ([n-1 : Nat][rec : ty-])
(sc- rec)))
ty-])
(define-typed-syntax (nat-ind P z s n)
[ P P- ( Nat #%type)]
[ z z- (app P- Zero)] ; zero
[ s s- (Π ([k : Nat]) ( (app P- k) (app P- (Succ k))))] ; succ
[ n n- Nat]
;; #:with res (if (typecheck? #'n- (expand/df #'Z))
;; #'z-
;; #'(s- (nat-ind P- z- s- (sub1 n-))))
----------------
[ (match/nat n-
P-
z-
s-
#;(λ ([n-1 : Nat][rec : (app P- n-)])
(app s- n-1 rec #;(nat-ind P- z- s- n-1))))
(app P- n-)])
; [≻ (P- d-)])

View File

@ -0,0 +1,122 @@
#lang s-exp "../dep2.rkt"
(require "rackunit-typechecking.rkt")
; Π → λ ∀ ≻ ⊢ ≫ ⇒
;; examples from Prabhakar's Proust paper
;; Peano nums -----------------------------------------------------------------
(check-type Z : Nat -> Z)
(check-type (S Z) : Nat -> (S Z))
(check-type (S (S Z)) : Nat -> (S (S Z)))
(define-type-alias nat-rec
(λ ([C : *])
(λ ([zc : C][sc : ( C C)])
(λ ([n : Nat])
(nat-ind (λ ([x : Nat]) C)
zc
(λ ([x : Nat]) sc)
n)))))
(check-type nat-rec : ( (C) ( C ( C C) ( Nat C))))
;; nat-rec with no annotations
(check-type
(λ (C)
(λ (zc sc)
(λ (n)
(nat-ind (λ (x) C)
zc
(λ (x) sc)
n))))
: ( (C) ( C ( C C) ( Nat C))))
(check-type (nat-rec Nat) : ( Nat ( Nat Nat) ( Nat Nat)))
(check-type ((nat-rec Nat) Z (λ ([n : Nat]) (S n))) : ( Nat Nat))
;; basic identity example, to test eval
(define-type-alias id ((nat-rec Nat) Z (λ ([n : Nat]) (S n))))
(check-type (id Z) : Nat -> Z)
;; this example will err if eval tries to tycheck again
(check-type (id (S Z)) : Nat)
(check-type (id (S Z)) : Nat -> (S Z))
(define-type-alias plus
(λ ([n : Nat])
(((nat-rec ( Nat Nat))
(λ ([m : Nat]) m)
(λ ([pm : ( Nat Nat)])
(λ ([x : Nat])
(S (pm x)))))
n)))
(check-type plus : ( Nat ( Nat Nat)))
;; infer, and dont curry
(check-type
(λ (n1 n2)
((((nat-rec ( Nat Nat))
(λ (m) m)
(λ (pm) (λ (x) (S (pm x)))))
n1)
n2))
: ( Nat Nat Nat))
;(check-type ((plus Z) Z) : Nat -> 0)
;(check-type ((plus (S Z)) (S Z)) : Nat -> 2)
;(check-type ((plus (S Z)) Z) : Nat -> 1)
;(check-type ((plus (S Z)) Z) : Nat -> 1)
(check-type (plus Z) : ( Nat Nat))
(check-type ((plus Z) Z) : Nat -> Z)
(check-type ((plus Z) (S Z)) : Nat -> (S Z))
(check-type ((plus (S Z)) Z) : Nat -> (S Z))
(check-type ((plus (S Z)) (S Z)) : Nat -> (S (S Z)))
(check-type ((plus (S (S Z))) (S Z)) : Nat -> (S (S (S Z))))
(check-type ((plus (S Z)) (S (S Z))) : Nat -> (S (S (S Z))))
;; plus zero (left)
(check-type (λ ([n : Nat]) (eq-refl n))
: (Π ([n : Nat]) (= ((plus Z) n) n)))
;; plus zero (right)
(check-type
(λ ([n : Nat])
(nat-ind (λ ([m : Nat]) (= ((plus m) Z) m))
(eq-refl Z)
(λ ([k : Nat])
(λ ([p : (= ((plus k) Z) k)])
(eq-elim ((plus k) Z)
(λ ([W : Nat]) (= (S ((plus k) Z)) (S W)))
(eq-refl (S ((plus k) Z)))
k
p)))
n))
: (Π ([n : Nat]) (= ((plus n) Z) n)))
;; plus zero identity, no annotations
;; left:
(check-type (λ (n) (eq-refl n))
: (Π ([n : Nat]) (= ((plus Z) n) n)))
;; right:
(check-type
(λ (n)
(nat-ind (λ (m) (= ((plus m) Z) m))
(eq-refl Z)
(λ (k) (λ (p)
(eq-elim ((plus k) Z)
(λ (W) (= (S ((plus k) Z)) (S W)))
(eq-refl (S ((plus k) Z)))
k
p)))
n))
: (Π ([n : Nat]) (= ((plus n) Z) n)))
;; nat-ind as a function ; TODO: need matching form or matching lambda
#;(define-typed-alias nat-ind2
(λ ([P : ( Nat *)]
[Zcase : (P Z)]
[Scase : (Π ([k : Nat]) ( (P k) (P (S k))))]
[n : Nat])
(match/nat n ZCase (SCase n (nat-ind2 P ZCase SCase n-1)))))