start some eq tests; fix dep-ind (in dep-ind-fixed) so indices can depend on params

This commit is contained in:
Stephen Chang 2017-09-19 16:03:07 -04:00
parent 8eb637af0e
commit 84108c7300
5 changed files with 1226 additions and 1 deletions

View File

@ -0,0 +1,717 @@
#lang turnstile/lang
; a basic dependently-typed calculus
; - with inductive datatypes
; created this new file to avoid breaking anything using dep-ind.rkt
; this file is mostly same as dep-ind.rkt but define-datatype has some fixes:
; 1) params and indices must be applied separately
; - for constructor (but not type constructor)
; 2) allows indices to depend on param
; 3) indices were not being inst with params
; 4) arg refs were using x instead of Cx from new expansion
; TODO: re-compute recur-x, ie recur-Cx
; Π λ ≻ ⊢ ≫ → ∧ (bidir ⇒ ⇐) τ⊑
(provide Type (rename-out [Type *])
Π
= eq-refl eq-elim
λ (rename-out [app #%app]) ann
define-datatype 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?
;; set (Type n) : (Type n+1)
;; Type = (Type 0)
(define-internal-type-constructor Type)
(define-typed-syntax Type
[:id --- [ (Type 0)]]
[(_ n:exact-nonnegative-integer)
#:with n+1 (+ (syntax-e #'n) 1)
-------------
[ #,(syntax-property
(syntax-property
#'(Type- 'n) ':
(syntax-property
#'(Type- 'n+1)
'orig
(list #'(Type n+1))))
'orig
(list #'(Type n)))]])
(begin-for-syntax
(define debug? #f)
(define type-eq-debug? #f)
(define debug-match? #f)
;; TODO: fix `type` stx class
;; current-type and type stx class not working
;; for case where var has type that is previous var
;; that is not yet in tyenv
;; eg in (Π ([A : *][a : A]) ...)
;; expansion of 2nd type A will fail with unbound id err
;;
;; attempt 2
;; (define old-type? (current-type?))
;; (current-type?
;; (lambda (t)
;; (printf "t = ~a\n" (stx->datum t))
;; (printf "ty = ~a\n" (stx->datum (typeof t)))
;; (or (Type? (typeof t))
;; (syntax-parse (typeof t)
;; [((~literal Type-) n:exact-nonnegative-integer) #t]
;; [_ #f]))))
;; attempt 1
;; (define old-type? (current-type?))
;; (current-type?
;; (lambda (t) (or (#%type? t) (old-type? t))))
(define old-relation (current-typecheck-relation))
(current-typecheck-relation
(lambda (t1 t2)
(define res
;; expand (Type n) if unexpanded
(or (syntax-parse t1
[((~literal Type-) n)
(typecheck? ((current-type-eval) t1) t2)]
[_ #f])
(old-relation t1 t2)))
(when type-eq-debug?
(pretty-print (stx->datum t1))
(pretty-print (stx->datum t2))
(printf "res: ~a\n" res))
res))
;; used to attach type after app/eval
;; but not all apps will have types, eg
;; - internal type representation
;; - intermediate elim terms
(define (maybe-assign-type e t)
(if (syntax-e t) (assign-type e t) e)))
(define-internal-type-constructor ) ; equiv to Π with no uses on rhs
(define-internal-binding-type ) ; equiv to Π with Type for all params
;; Π expands into combination of internal →- and ∀-
;; uses "let*" syntax where X_i is in scope for τ_i+1 ...
;; TODO: add tests to check this
(define-typed-syntax (Π ([X:id : τ_in] ...) τ_out)
[[X X- : τ_in] ... [τ_out τ_out- tyoutty]
[τ_in τ_in- tyinty] ...]
;; check that types have type (Type _)
;; must re-expand since (Type n) will have type unexpanded (Type n+1)
#:with ((~Type _) ...) (stx-map (current-type-eval) #'(tyoutty tyinty ...))
-------
[ (∀- (X- ...) (→- τ_in- ... τ_out-)) Type]
#;[ #,#`(∀- (X- ...)
#,(assign-type
#'(→- τ_in- ... τ_out-)
#'#%type)) #%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] ...) τ))
;; pattern expanders
(begin-for-syntax
(define-syntax
(pattern-expander
(syntax-parser
[(_ ([x:id : τ_in] ... (~and (~literal ...) ooo)) τ_out)
#'(~∀ (x ... ooo) (~→ τ_in ... ooo τ_out))]
[(_ ([x:id : τ_in] ...) τ_out)
#'(~∀ (x ...) (~→ τ_in ... τ_out))]))))
;; equality -------------------------------------------------------------------
(define-internal-type-constructor =)
(define-typed-syntax (= t1 t2)
[ t1 t1- ty]
[ t2 t2- ty]
;; #:do [(printf "t1: ~a\n" (stx->datum #'t1-))
;; (printf "t2: ~a\n" (stx->datum #'t2-))]
; [t1- τ= t2-]
---------------------
[ (=- t1- t2-) Type])
;; Q: what is the operational meaning of eq-refl?
(define-typed-syntax (eq-refl e)
[ e e- _]
----------
[ (#%app- void-) (= e- e-)])
;; eq-elim: t : T
;; P : (T -> Type)
;; pt : (P t)
;; w : T
;; peq : (= t w)
;; -> (P w)
(define-typed-syntax (eq-elim t P pt w peq)
[ t t- ty]
[ P P- ( ty Type)]
[ pt pt- (app P- t-)]
[ w w- ty]
[ peq peq- (= t- w-)]
--------------
[ pt- (app P- w-)])
;; lambda and #%app -----------------------------------------------------------
;; TODO: fix `type` stx class
(define-typed-syntax λ
;; expected ty only
[(_ (y:id ...) e) ( ([x:id : τ_in] ... ) τ_out)
[[x x- : τ_in] ... #,(substs #'(x ...) #'(y ...) #'e) e- τ_out]
---------
[ (λ- (x- ...) e-)]]
;; both expected ty and annotations
[(_ ([y:id : τ_in*] ...) e) ( ([x:id : τ_in] ...) τ_out)
; [(_ ([y:id : τy_in:type] ...) e) ⇐ (~Π ([x:id : τ_in] ...) τ_out) ≫
#:fail-unless (stx-length=? #'(y ...) #'(x ...))
"function's arity does not match expected type"
[ τ_in* τ_in** Type] ...
; #:when (typechecks? (stx-map (current-type-eval) #'(τ_in* ...))
#:when (typechecks? #'(τ_in** ...) #'(τ_in ...))
; #:when (typechecks? #'(τy_in.norm ...) #'(τ_in ...))
; [τy_in τ= τ_in] ...
[[x x- : τ_in] ... #,(substs #'(x ...) #'(y ...) #'e) e- τ_out]
-------
[ (λ- (x- ...) e-)]]
;; annotations only
[(_ ([x:id : τ_in] ...) e)
[[x x- : τ_in] ... [e e- τ_out] [τ_in τ_in- _] ...]
-------
[ (λ- (x- ...) e-) (Π ([x- : τ_in-] ...) τ_out)]])
;; helps debug which terms (app/evals) do not have types, eg
;; - → in internal type representation
;; - intermediate elim terms
(define-for-syntax false-tys 0)
;; 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 (_ matcher) (_ _ . args))
#:do[(when debug-match?
(printf "potential delayed match ~a ~a\n"
(stx->datum #'matcher)
(stx->datum #'args)))]
#:with ty (typeof this-syntax)
;; TODO: use pat expander instead
#:with (_ m/d . _) (local-expand #'(#%app match/delayed 'dont 'care) 'expression null)
#:when (free-identifier=? #'m/d #'f)
#:do[(when debug-match? (printf "matching\n"))]
;; TODO: need to attach type?
#;[
(unless (syntax-e #'ty)
(displayln 3)
(displayln #'ty)
(set! false-tys (add1 false-tys))
(displayln false-tys))]
(maybe-assign-type #'(matcher . args) (typeof this-syntax))]
;; 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 ...))))]
; #: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 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)
#:do[(when debug?
(define ttt (typeof this-syntax))
(define ttt2 (and ttt
(substs #'(app/eval e_arg ...) #'(r-app x ...) ttt free-identifier=?)))
(define ttt3 (and ttt2
(local-expand ttt2 'expression null)))
(printf "expected type\n")
(pretty-print (stx->datum ttt))
(pretty-print (stx->datum ttt2))
(pretty-print (stx->datum ttt3)))]
#: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
(local-expand
; (substs #'(app/eval e_arg ...) #'(r-app x ...) #'ty free-identifier=?)
;; TODO: this is needed, which means there are some uneval'ed matches
;; but is this the right place?
;; eg, it wasnt waiting on any arg
;; so that mean it could have been evaled but wasnt at some point
(substs #'(app/eval) #'(r-app) #'ty free-identifier=?)
'expression null))
#'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 (typeof #'e+)))
;; (displayln "res expanded:------------------------")
;; (pretty-print
;; (stx->datum (local-expand (substs #'(e_arg ...) #'(x ...) #'e) 'expression null)))
(displayln "res app/eval re-expanding-----------------------"))]
#:with ((~literal let-values) () ((~literal let-values) () e++))
(local-expand
#'(let-syntax (#;[app (make-rename-transformer #'app/eval)]
#;[x (make-variable-like-transformer #'e_arg)]) e+)
'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+)
'expression null))]
#;[(when (not (syntax-e #'ty))
(displayln 1)
(displayln (stx->datum this-syntax))
(displayln #'ty)
(set! false-tys (add1 false-tys))
(displayln false-tys))]
#'e++ #;(substs #'(e_arg ...) #'(x ...) #'e)]
[(_ f . args)
#:do[(when debug?
(printf "not apping\n")
(pretty-print (stx->datum #'f))
(displayln "args")
(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)
#;[(unless (syntax-e #'ty)
(displayln 2)
(displayln (stx->datum this-syntax))
(displayln #'ty)
(displayln (syntax-property this-syntax '::))
(set! false-tys (add1 false-tys))
(displayln false-tys))]
(syntax-parse #'f+
[((~literal #%plain-lambda) . _)
(maybe-assign-type #'(app/eval f+ . args+) #'ty)]
[_
;(maybe-assign-type
#'(#%app- f+ . args+)
;#'ty)
])]))
;; TODO: fix orig after subst
(define-typed-syntax app
[(_ e_fn e_arg ...)
#:do[(when debug?
(displayln "TYPECHECKING")
(pretty-print (stx->datum this-syntax)))]
; #:do[(printf "applying (1) ~a\n" (stx->datum #'e_fn))]
; [⊢ e_fn ≫ (~and e_fn- (_ (x:id ...) e ~!)) ⇒ (~Π ([X : τ_inX] ...) τ_outX)]
[ e_fn e_fn- ( ([X : τ_in] ...) τ_out)]
#:fail-unless (stx-length=? #'[τ_in ...] #'[e_arg ...])
(num-args-fail-msg #'e_fn #'[τ_in ...] #'[e_arg ...])
;; #:do[(displayln "expecting")
;; (pretty-print (stx->datum #'(τ_in ...)))]
;; [⊢ e_arg ≫ _ ⇒ ty2] ... ; typechecking args
;; #:do[(displayln "got")
;; (pretty-print (stx->datum (stx-map typeof #'(ty2 ...))))]
[ e_arg e_arg- τ_in] ... ; typechecking args
-----------------------------
[ (app/eval e_fn- e_arg- ...) #,(substs #'(e_arg- ...) #'(X ...) #'τ_out)]])
(define-typed-syntax (ann e (~datum :) τ)
[ e e- τ]
--------
[ e- τ])
;; top-level ------------------------------------------------------------------
;; TODO: shouldnt need define-type-alias, should be same as define
(define-syntax define-type-alias
(syntax-parser
[(_ alias:id τ);τ:any-type)
#'(define-syntax- alias
(make-variable-like-transformer #'τ))]
#;[(_ (f:id x:id ...) ty)
#'(define-syntax- (f stx)
(syntax-parse stx
[(_ x ...)
#:with τ:any-type #'ty
#'τ.norm]))]))
;; TODO: delete this?
(define-typed-syntax define
[(_ x:id (~datum :) τ e:expr)
[ e e- τ]
#:with y (generate-temporary #'x)
#:with y+props (transfer-props #'e- #'y #:except '(origin))
--------
[ (begin-
(define-syntax x (make-rename-transformer #'y+props))
(define- y e-))]]
[(_ x:id e)
;This won't work with mutually recursive definitions
[ e e- _]
#:with y (generate-temporary #'x)
#:with y+props (transfer-props #'e- #'y #:except '(origin))
--------
[ (begin-
(define-syntax x (make-rename-transformer #'y+props))
(define- y e-))]]
#;[(_ (f [x (~datum :) ty] ... (~or (~datum ) (~datum ->)) ty_out) e ...+)
#:with f- (add-orig (generate-temporary #'f) #'f)
--------
[ (begin-
(define-syntax- f
(make-rename-transformer ( f- : ( ty ... ty_out))))
(define- f-
(stlc+lit:λ ([x : ty] ...)
(stlc+lit:ann (begin e ...) : ty_out))))]])
(define-typed-syntax (unsafe-assign-type e (~datum :) τ) --- [ e τ])
(struct TmpTy- ())
(define-syntax TmpTy
(syntax-parser
[:id (assign-type #'TmpTy- #'Type)]
[(_ . args) (assign-type #'(#%app TmpTy- . args) #'Type)]))
(struct match/delayed (name args) #:transparent)
(define-typed-syntax define-datatype
;; datatype type `TY` is an id ----------------------------------------------
;; - ie, no params or indices
[(_ Name (~datum :) TY:id
[C:id (~datum :) CTY] ...)
; need to expand `CTY` to find recur args,
; but `Name` is still unbound so swap in a tmp id `TmpTy`
#:with (CTY/tmp ...) (subst #'TmpTy #'Name #'(CTY ...))
[ CTY/tmp CTY/tmp- Type] ...
#:with TmpTy+ (local-expand #'TmpTy 'expression null)
;; un-subst TmpTy for Name in expanded CTY
;; TODO: replace TmpTy in origs of CTY_in ... CTY_out
;; TODO: check CTY_out == `Name`?
#:with (( ([x : CTY_in] ...) CTY_out) ...)
(subst #'Name #'TmpTy+ #'(CTY/tmp- ...) free-id=?)
#:with (C/internal ...) (generate-temporaries #'(C ...))
#:with (Ccase ...) (generate-temporaries #'(C ...))
#:with (Ccase- ...) (generate-temporaries #'(C ...))
#:with ((recur-x ...) ...) (stx-map
(lambda (xs ts)
(filter
(lambda (x) x) ; filter out #f
(stx-map
(lambda (x t) ; returns x or #f
(and (free-id=? t #'Name) x))
xs ts)))
#'((x ...) ...) #'((CTY_in ...) ...))
#:with elim-Name (format-id #'Name "elim-~a" #'Name)
#:with match-Name (format-id #'Name "match-~a" #'Name)
#:with Name/internal (generate-temporary #'Name)
--------
[ (begin-
;; define `Name`, eg "Nat", as a valid type
; (define-base-type Name) ; dont use bc uses '::, and runtime errs
(struct Name/internal ())
(define-typed-syntax Name [_:id --- [ (Name/internal) TY]])
;; define structs for `C` constructors
(struct C/internal (x ...) #:transparent) ...
(define C (unsafe-assign-type C/internal : CTY)) ...
;; elimination form
(define-typed-syntax (elim-Name v P Ccase ...)
[ v v- Name]
[ P P- ( Name Type)] ; prop / motive
;; each `Ccase` require 2 sets of args (even if set is empty):
;; 1) args of the constructor `x` ...
;; 2) IHs for each `x` that has type `Name`
[ Ccase Ccase- (Π ([x : CTY_in] ...)
( (app P- recur-x) ...
(app P- (app C x ...))))] ...
-----------
[ (match-Name v- P- Ccase- ...) (app P- v-)])
;; eval the elim redexes
(define-syntax match-Name
(syntax-parser
#;[(_ . args)
#:do[(printf "trying to match:\n~a\n" (stx->datum #'args))]
#:when #f #'(void)]
[(_ v P Ccase ...)
#:with ty (typeof this-syntax)
; local expand since v might be unexpanded due to reflection
(syntax-parse (local-expand #'v 'expression null)
; do eval if v is an actual `C` instance
[((~literal #%plain-app) C-:id x ...)
#:with (_ C+ . _) (local-expand #'(C 'x ...) 'expression null)
#:when (free-identifier=? #'C- #'C+)
(maybe-assign-type
#`(app/eval (app Ccase x ...)
; (match-Name x P Ccase ...) ...)
#,@(stx-map (lambda (y)
(maybe-assign-type
#`(match-Name #,y P Ccase ...)
#'ty))
#'(x ...)))
#'ty)]
...
; else generate a delayed term
;; must be #%app-, not #%plain-app, ow match will not dispatch properly
[_ ;(maybe-assign-type
#'(#%app- match/delayed 'match-Name (void v P Ccase ...))
;#'ty)
])]))
)]]
;; --------------------------------------------------------------------------
;; datatype type `TY` is a fn:
;; - params A ... and indices i ... must be in separate fn types
;; - but actual type formation constructor flattens to A ... i ...
;; - and constructors also flatten to A ... i ...
;; - all cases in elim-Name must consume i ... (but A ... is inferred)
;; --------------------------------------------------------------------------
[(_ Name (~datum :) TY
[C:id (~datum :) CTY] ...)
;; params and indices specified with separate fn types, to distinguish them,
;; but are combined in other places,
;; eg (Name A ... i ...) or (CTY A ... i ...)
[ TY ( ([A : TYA] ...) ; params
( ([i : TYi] ...) ; indices
TY_out)) Type]
; need to expand `CTY` but `Name` is still unbound so use tmp id
; - extract arity of each `C` ...
; - find recur args
#:with (CTY/tmp ...) (subst #'TmpTy #'Name #'(CTY ...))
[ CTY/tmp CTY/tmp- Type] ...
#:with (_ TmpTy+) (local-expand #'(TmpTy) 'expression null)
;; ;; TODO: replace TmpTy in origs of τ_in ... τ_out
;; TODO: how to un-subst TmpTy (which is now a constructor)?
;; - for now, dont use these τ_in/τ_out; just use for arity
;; - instead, re-expand in generated `elim` macro below
;;
;; - 1st Π is tycon params, dont care for now
;; - 2nd Π is tycon indices, dont care for now
;; - 3rd Π is constructor args
#:with (( ([_ : _] ...)
( ([_ : _] ...)
( ([x : CTY_in/tmp] ...) CTY_out/tmp))) ...)
#'(CTY/tmp- ...)
;; each (recur-x ...) is subset of (x ...) that are recur args,
;; ie, they are not fresh ids
#:with ((recur-x ...) ...) (stx-map
(lambda (xs ts)
(filter
(lambda (y) y) ; filter out #f
(stx-map
(lambda (x t)
(and
(syntax-parse t
[((~literal #%plain-app) tmp . _)
(free-id=? #'tmp #'TmpTy+)]
[_ #f])
x)) ; returns x or #f
xs ts)))
#'((x ...) ...)
#'((CTY_in/tmp ...) ...))
#:with ((recur-Cx ...) ...) (stx-map generate-temporaries #'((recur-x ...) ...))
;; pre-generate other patvars; makes nested macros below easier to read
#:with (A- ...) (generate-temporaries #'(A ...))
#:with (i- ...) (generate-temporaries #'(i ...))
;; need to multiply A and i patvars, to match types of `C` ... constructors
;; must be fresh vars to avoid dup patvar errors
#:with ((CA ...) ...) (stx-map (lambda _ (generate-temporaries #'(A ...))) #'(C ...))
#:with ((CTYA ...) ...) (stx-map (lambda _ (generate-temporaries #'(A ...))) #'(C ...))
#:with ((Ci ...) ...) (stx-map (lambda _ (generate-temporaries #'(i ...))) #'(C ...))
#:with ((CTYi/CA ...) ...) (stx-map (lambda _ (generate-temporaries #'(TYi ...))) #'(C ...))
#:with ((CTYi ...) ...) (stx-map (lambda _ (generate-temporaries #'(TYi ...))) #'(C ...))
#:with ((Cx ...) ...) (stx-map (lambda (xs) (generate-temporaries xs)) #'((x ...) ...))
; Ci*recur dups Ci for each recur, to get the ellipses to work out below
#:with (((Ci*recur ...) ...) ...) (stx-map
(lambda (cis recurs)
(stx-map (lambda (r) cis) recurs))
#'((Ci ...) ...)
#'((recur-x ...) ...))
;; not inst'ed CTY_in
#:with ((CTY_in/CA ...) ...) (stx-map generate-temporaries #'((CTY_in/tmp ...) ...))
;; inst'ed CTY_in (with A ...)
#:with ((CTY_in ...) ...) (stx-map generate-temporaries #'((CTY_in/tmp ...) ...))
#:with (CTY_out/CA ...) (generate-temporaries #'(C ...))
#:with (CTY_out ...) (generate-temporaries #'(C ...))
; CTY_out_A matches the A and CTY_out_i matches the i in CTY_out,
; - ie CTY_out = (Name CTY_out_A ... CTY_out_i ...)
; - also, CTY_out_A refs (ie bound-id=) CA and CTY_out_i refs Ci
#:with ((CTY_out_A ...) ...) (stx-map (lambda _ (generate-temporaries #'(A ...))) #'(C ...))
#:with ((CTY_out_i ...) ...) (stx-map (lambda _ (generate-temporaries #'(i ...))) #'(C ...))
;; differently named `i`, to match type of P
#:with (j ...) (generate-temporaries #'(i ...))
; dup (A ...) C times, again for ellipses matching
#:with ((A*C ...) ...) (stx-map (lambda _ #'(A ...)) #'(C ...))
#:with (C/internal ...) (generate-temporaries #'(C ...))
#:with (Ccase ...) (generate-temporaries #'(C ...))
#:with (Ccase- ...) (generate-temporaries #'(C ...))
#:with Name- (mk-- #'Name)
#:with Name-patexpand (mk-~ #'Name)
#:with elim-Name (format-id #'Name "elim-~a" #'Name)
#:with match-Name (format-id #'Name "match-~a" #'Name)
#:with (ccasety ...) (generate-temporaries #'(Ccase ...))
;; these are all the generated definitions that implement the define-datatype
#:with OUTPUT-DEFS
#'(begin-
;; define the type
(define-internal-type-constructor Name)
;; TODO? This works when TYi depends on (e.g., is) A
;; but is this always the case?
(define-typed-syntax (Name A ... i ...)
[ A A- TYA] ...
[ i i- TYi] ...
----------
[ (Name- A- ... i- ...) TY_out])
;; define structs for constructors
(struct C/internal (x ...) #:transparent) ...
;; TODO: this define should be a macro instead?
(define C (unsafe-assign-type
(lambda (A ...) (lambda (i ...) C/internal))
: CTY)) ...
;; define eliminator-form
(define-typed-syntax (elim-Name v P Ccase ...)
;; re-extract CTY_in and CTY_out, since we didnt un-subst above
;; TODO: must re-compute recur-x, ie recur-Cx
#:with (( ([CA : CTYA] ...) ; ignore params, instead infer `A` ... from `v`
( ([Ci : CTYi/CA] ...)
( ([Cx : CTY_in/CA] ...)
CTY_out/CA)))
...)
(stx-map (current-type-eval) #'(CTY ...))
;; compute recur-Cx by finding analogous x/recur-x pairs
;; each (recur-Cx ...) is subset of (Cx ...) that are recur args,
;; ie, they are not fresh ids
#:with ((recur-Cx ...) ...)
(stx-map
(lambda (xs rxs cxs)
(filter
(lambda (z) z) ; filter out #f
(stx-map
(lambda (y cy)
(if (stx-member y rxs) cy #f))
xs cxs)))
#'((x ...) ...)
#'((recur-x ...) ...)
#'((Cx ...) ...))
#;(stx-map
(lambda (xs ts)
(filter
(lambda (y) y) ; filter out #f
(stx-map
(lambda (x t)
(and
(syntax-parse t
[(Name-patexpand . _) #t]
[_ #f])
x)) ; returns x or #f
xs ts)))
#'((Cx ...) ...)
#'((CTY_in/CA ...) ...))
;; target, infers A ...
[ v v- (Name-patexpand A ... i ...)]
;; inst CTY_in/CA and CTY_out/CA with inferred A ...
#:with (((CTYi ...)
(CTY_in ... CTY_out))
...)
(stx-map (lambda (ts tyis cas) (substs #'(A ...) cas #`(#,tyis #,ts)))
#'((CTY_in/CA ... CTY_out/CA) ...)
#'((CTYi/CA ...) ...)
#'((CA ...) ...))
;; get the params and indices in CTY_out
;; - dont actually need CTY_out_A
#:with ((Name-patexpand CTY_out_A ... CTY_out_i ...) ...) #'(CTY_out ...)
;; prop / motive
[ P P- (Π ([j : TYi] ...) ( (Name A ... j ...) Type))]
;; each Ccase consumes 3 nested sets of (possibly empty) args:
;; 1) Ci - indices of the tycon
;; 2) Cx - args of each constructor `C`
;; 3) IHs - for each recur-Cx ... (which are a subset of Cx ...)
;;
;; somewhat of a hack:
;; by reusing Ci and CTY_out_i both to match CTY/CTY_out above, and here,
;; we automatically unify Ci with the indices in CTY_out
[ Ccase _ ccasety] ...
[ Ccase Ccase- (Π ([Ci : CTYi] ...) ; indices
(Π ([Cx : CTY_in] ...) ; constructor args
( (app (app P- Ci*recur ...) recur-Cx) ... ; IHs
(app (app P- CTY_out_i ...)
(app (app (app C A*C ...) Ci ...) Cx ...)))))] ...
-----------
[ (match-Name v- P- Ccase- ...) (app (app P- i ...) v-)])
;; implements reduction of elimator redexes
(define-syntax match-Name
(syntax-parser
#;[(_ . args)
#:do[(displayln "trying to match:")
(pretty-print (stx->datum #'args))]
#:when #f #'(void)]
[(_ v P Ccase ...)
#:with ty (typeof this-syntax)
;; must local expand because `v` may be unexpanded due to reflection
(syntax-parse (local-expand #'v 'expression null)
[((~literal #%plain-app)
((~literal #%plain-app)
((~literal #%plain-app) C-:id CA ...)
Ci ...)
x ...)
#:with (_ C+ . _) (local-expand #'(C 'CA ...) 'expression null)
#:when (free-identifier=? #'C+ #'C-)
;; can use app instead of app/eval to properly propagate types
;; but doesnt quite for in all cases?
(maybe-assign-type
#`(app/eval ;#,(assign-type
(app/eval (app Ccase Ci ...) x ...)
;; TODO: is this right?
; #'(app P Ci ...))
; (match-Name recur-x P Ccase ...) ...)
#,@(stx-map (lambda (r)
(maybe-assign-type
#`(match-Name #,r P Ccase ...)
#'ty))
#'(recur-x ...)))
#'ty)] ...
[_ ;(maybe-assign-type
;; must be #%app-, not #%plain-app, ow match will not dispatch properly
#'(#%app- match/delayed 'match-Name (void v P Ccase ...))
;#'ty)
])])))
;; DEBUG: of generated defs
; #:do[(pretty-print (stx->datum #'OUTPUT-DEFS))]
--------
[ OUTPUT-DEFS]])

View File

@ -393,7 +393,7 @@
(struct match/delayed (name args) #:transparent)
(define-typed-syntax define-datatype
;; datatype type `TY` is an id ---------------------------------------------------
;; datatype type `TY` is an id ----------------------------------------------
;; - ie, no params or indices
[(_ Name (~datum :) TY:id
[C:id (~datum :) CTY] ...)

View File

@ -0,0 +1,84 @@
#lang s-exp "../dep-ind-fixed.rkt"
(require "rackunit-typechecking.rkt")
; Π → λ ∀ ≻ ⊢ ≫ ⇒
;; testing user-defined equality
(define-datatype my= : (Π ([A : (Type 0)]) (Π ([a : A] [b : A]) (Type 0)))
(my-refl : (Π ([A : (Type 0)])
(Π ([x : A][y : A])
(Π ([a : A]) (my= A a a))))))
(define-datatype Nat : *
[Z : ( Nat)]
[S : ( Nat Nat)])
(define-type-alias plus
(λ ([n : Nat][m : Nat])
(elim-Nat n
(λ ([k : Nat]) Nat)
(λ () (λ () m))
(λ ([k : Nat]) (λ ([IH : Nat]) (S IH))))))
;; index args (the Z's) to my-refl are unused
;; TODO: drop them?
(check-type (((my-refl Nat) (Z) (Z)) (Z)) : (my= Nat (Z) (Z))) ; 0=0
(check-not-type (((my-refl Nat) (Z) (Z)) (S (Z))) : (my= Nat (Z) (Z))) ; 1 \neq 0
(check-type (((my-refl Nat) (Z) (Z)) (S (Z)))
: (my= Nat (S (Z)) (S (Z)))) ; 1=1
(check-type (((my-refl Nat) (Z) (Z)) (S (Z)))
: (my= Nat (S (Z)) (plus (S (Z)) (Z)))) ; 1=1+0
(check-type (((my-refl Nat) (Z) (Z)) (S (Z)))
: (my= Nat (plus (S (Z)) (Z)) (plus (S (Z)) (Z)))) ; 1+0=1+0
(check-type (((my-refl Nat) (Z) (Z)) (S (Z)))
: (my= Nat (plus (Z) (S (Z))) (plus (S (Z)) (Z)))) ; 1+0=0+1
(check-type
(((my-refl Nat) (Z) (Z)) (S (S (Z))))
: (my= Nat (plus (S (Z)) (S (Z))) (plus (S (Z)) (plus (S (Z)) (Z))))) ; 1+1=1+1+0
;; = symmetric
(check-type
(λ ([B : (Type 0)])
(λ ([x : B] [y : B])
(λ ([e : (my= B x y)])
(elim-my=
e
(λ ([a : B] [b : B])
(λ ([e : (my= B a b)])
(my= B b a)))
(λ ([a : B] [b : B])
(λ ([c : B])
(λ ()
(((my-refl B) c c) c))))))))
: (Π ([A : (Type 0)])
(Π ([x : A] [y : A])
( (my= A x y) (my= A y x)))))
;; = transitive
; TODO
#;(check-type
(λ ([A : (Type 0)])
(λ ([x : A] [y : A] [z : A])
(λ ([e1 : (my= A x y)] [e2 : (my= A y z)])
(elim-my=
e1
(λ ([a : A] [b : A])
(λ ([e : (my= A a b)])
(my= A a z)))
(λ ([a : A] [b : A])
(λ ([c : A])
(λ ()
(elim-my=
e2
(λ ([a : A] [b : A])
(λ ([e : (my= A a b)])
(my= A c c)))
(λ ([a : A] [b : A])
(λ ([c : A])
(λ ()
(((my-refl A) c c) c))))))))))))
: (Π ([A : (Type 0)])
(Π ([x : A] [y : A] [z : A])
( (my= A x y) (my= A y z) (my= A x z)))))

View File

@ -0,0 +1,271 @@
#lang s-exp "../dep-ind-fixed.rkt"
(require "rackunit-typechecking.rkt")
; Π → λ ∀ ≻ ⊢ ≫ ⇒
;; same as dep-ind-list-tests except uses dep-ind-fixed
;; - constructor params and indices must be applied separately
(define-datatype Nat : *
[Z : ( Nat)]
[S : ( Nat Nat)])
;; some basic tests
(check-type Nat : *)
;; this test wont work if define-datatype uses define-base-type
(check-type (λ ([x : Nat]) Nat) : ( Nat *))
;; constructors require 3 sets of args:
;; 1) params
;; 2) indices
;; 3) constructor args, split into
;; - non-recursive
;; - recursive
(define-datatype List : ( * ( *))
[null : ( (A) ( ( (List A))))]
[cons : ( (A) ( ( A (List A) (List A))))])
(check-type null : ( (A) ( ( (List A)))))
(check-type cons : ( (A) ( ( A (List A) (List A)))))
(check-type (null Nat) : ( ( (List Nat))))
(check-type (cons Nat) : ( ( Nat (List Nat) (List Nat))))
(check-type ((null Nat)) : ( (List Nat)))
(check-type (((null Nat))) : (List Nat))
(check-type (((cons Nat)) (Z) (((null Nat)))) : (List Nat))
;; length 0
(check-type
(elim-List (((null Nat)))
(λ () (λ ([l : (List Nat)]) Nat))
(λ () (λ () (λ () (Z))))
(λ ()
(λ ([x : Nat][xs : (List Nat)])
(λ ([IH : Nat])
(S IH)))))
: Nat
-> (Z))
;; length 1
(check-type
(elim-List (((cons Nat)) (Z) (((null Nat))))
(λ () (λ ([l : (List Nat)]) Nat))
(λ () (λ () (λ () (Z))))
(λ ()
(λ ([x : Nat][xs : (List Nat)])
(λ ([IH : Nat])
(S IH)))))
: Nat
-> (S (Z)))
;; length 2
(check-type
(elim-List (((cons Nat)) (S (Z)) (((cons Nat)) (Z) (((null Nat)))))
(λ () (λ ([l : (List Nat)]) Nat))
(λ () (λ () (λ () (Z))))
(λ ()
(λ ([x : Nat][xs : (List Nat)])
(λ ([IH : Nat])
(S IH)))))
: Nat
-> (S (S (Z))))
(define-type-alias len/Nat
(λ ([lst : (List Nat)])
(elim-List lst
(λ () (λ ([l : (List Nat)]) Nat))
(λ () (λ () (λ () (Z))))
(λ ()
(λ ([x : Nat][xs : (List Nat)])
(λ ([IH : Nat])
(S IH)))))))
(check-type (len/Nat (((null Nat)))) : Nat -> (Z))
(check-type (len/Nat (((cons Nat)) (Z) (((null Nat))))) : Nat -> (S (Z)))
(define-type-alias len
(λ ([A : *])
(λ ([lst : (List A)])
(elim-List lst
(λ () (λ ([l : (List A)]) Nat))
(λ () (λ () (λ () (Z))))
(λ ()
(λ ([x : A][xs : (List A)])
(λ ([IH : Nat])
(S IH))))))))
(check-type (len Nat) : ( (List Nat) Nat))
(check-type ((len Nat) (((null Nat)))) : Nat -> (Z))
(check-type ((len Nat) (((cons Nat)) (Z) (((null Nat))))) : Nat -> (S (Z)))
;; test that elim matches on constructor name, and not arity
(define-datatype Test : *
[A : ( Test)]
[B : ( Test)])
(check-type
(elim-Test (A)
(λ ([x : Test]) Nat)
(λ () (λ () (Z)))
(λ () (λ () (S (Z)))))
: Nat -> (Z))
;; should match second branch, but just arity-checking would match 1st
(check-type
(elim-Test (B)
(λ ([x : Test]) Nat)
(λ () (λ () (Z)))
(λ () (λ () (S (Z)))))
: Nat -> (S (Z)))
;; Vect: indexed "lists" parameterized over length --------------------
(define-datatype Vect : ( * ( Nat *))
[nil : (Π ([A : *]) (Π ([k : Nat]) ( (Vect A (Z)))))]
[cns : (Π ([A : *]) (Π ([k : Nat]) ( A (Vect A k) (Vect A (S k)))))])
(check-type nil : (Π ([A : *]) (Π ([k : Nat]) ( (Vect A (Z))))))
(check-type cns : (Π ([A : *]) (Π ([k : Nat]) ( A (Vect A k) (Vect A (S k))))))
(check-type ((nil Nat) (Z)) : ( (Vect Nat (Z))))
(check-type ((cns Nat) (Z)) : ( Nat (Vect Nat (Z)) (Vect Nat (S (Z)))))
(check-type (((nil Nat) (Z))) : (Vect Nat (Z)))
(check-type (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))) : (Vect Nat (S (Z))))
(check-type (((cns Nat) (S (Z))) (Z)
(((cns Nat) (Z)) (Z)
(((nil Nat) (Z)))))
: (Vect Nat (S (S (Z)))))
(define-type-alias mtNatVec (((nil Nat) (Z))))
(check-type mtNatVec : (Vect Nat (Z)))
;; nil must be applied again (bc it's a constructor of 0 args)
(check-not-type ((nil Nat) (S (Z))) : (Vect Nat (S (Z))))
;; length
(check-type
(elim-Vect (((nil Nat) (Z)))
(λ ([k : Nat]) (λ ([v : (Vect Nat k)]) Nat))
(λ ([k : Nat]) (λ () (λ () (Z))))
(λ ([k : Nat])
(λ ([x : Nat][xs : (Vect Nat k)])
(λ ([IH : Nat])
(S IH)))))
: Nat -> (Z))
(check-type
(elim-Vect (((cns Nat) (Z)) (Z) (((nil Nat) (Z))))
(λ ([k : Nat]) (λ ([v : (Vect Nat k)]) Nat))
(λ ([k : Nat]) (λ () (λ () (Z))))
(λ ([k : Nat])
(λ ([x : Nat][xs : (Vect Nat k)])
(λ ([IH : Nat])
(S IH)))))
: Nat -> (S (Z)))
(check-type
(elim-Vect (((cns Nat) (S (Z))) (Z) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))
(λ ([k : Nat]) (λ ([v : (Vect Nat k)]) Nat))
(λ ([k : Nat]) (λ () (λ () (Z))))
(λ ([k : Nat])
(λ ([x : Nat][xs : (Vect Nat k)])
(λ ([IH : Nat])
(S IH)))))
: Nat -> (S (S (Z))))
(define-type-alias plus
(λ ([n : Nat][m : Nat])
(elim-Nat n
(λ ([k : Nat]) Nat)
(λ () (λ () m))
(λ ([k : Nat]) (λ ([IH : Nat]) (S IH))))))
(check-type plus : ( Nat Nat Nat))
(check-type (plus (Z) (S (S (Z)))) : Nat -> (S (S (Z))))
(check-type (plus (S (S (Z))) (Z)) : Nat -> (S (S (Z))))
(check-type (plus (S (S (Z))) (S (S (S (Z)))))
: Nat -> (S (S (S (S (S (Z)))))))
;; vappend
(check-type
(elim-Vect
(((nil Nat) (Z)))
(λ ([k : Nat])
(λ ([v : (Vect Nat k)])
(Vect Nat k)))
(λ ([k : Nat]) (λ () (λ () (((nil Nat) (Z))))))
(λ ([k : Nat])
(λ ([x : Nat][v : (Vect Nat k)])
(λ ([IH : (Vect Nat k)])
(((cns Nat) k) x IH)))))
: (Vect Nat (Z))
-> (((nil Nat) (Z))))
(define-type-alias vappend
(λ ([A : *])
(λ ([n : Nat][m : Nat])
(λ ([xs : (Vect A n)][ys : (Vect A m)])
(elim-Vect
xs
(λ ([k : Nat])
(λ ([v : (Vect A k)])
(Vect A (plus k m))))
(λ ([k : Nat]) (λ () (λ () ys)))
(λ ([k : Nat])
(λ ([x : A][v : (Vect A k)])
(λ ([IH : (Vect A (plus k m))])
(((cns A) (plus k m)) x IH)))))))))
(check-type
vappend
: ( (B)
(Π ([n : Nat][m : Nat])
( (Vect B n) (Vect B m) (Vect B (plus n m))))))
(check-type
(vappend Nat)
: (Π ([n : Nat][m : Nat])
( (Vect Nat n) (Vect Nat m) (Vect Nat (plus n m)))))
(check-type
((vappend Nat) (Z) (Z))
: ( (Vect Nat (Z)) (Vect Nat (Z)) (Vect Nat (Z))))
;; append nil + nil
(check-type
(((vappend Nat) (Z) (Z)) (((nil Nat) (Z))) (((nil Nat) (Z))))
: (Vect Nat (Z))
-> (((nil Nat) (Z))))
;; append 1 + 0
(check-type
(((vappend Nat) (S (Z)) (Z)) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))) (((nil Nat) (Z))))
: (Vect Nat (S (Z)))
-> (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))
;; m and n flipped
(typecheck-fail
(((vappend Nat) (S (Z)) (Z)) (((nil Nat) (Z))) (((cns Nat) (Z)) (Z) (((nil Nat) (Z))))))
(typecheck-fail
(((vappend Nat) (Z) (S (Z))) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))) (((nil Nat) (Z)))))
;; append 1 + 1
(check-type
(((vappend Nat) (S (Z)) (S (Z))) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))
: (Vect Nat (S (S (Z))))
-> (((cns Nat) (S (Z))) (Z) (((cns Nat) (Z)) (Z) (((nil Nat) (Z))))))
;; append 1 + 2
(check-type
(((vappend Nat) (S (Z)) (S (S (Z))))
(((cns Nat) (Z)) (Z) (((nil Nat) (Z))))
(((cns Nat) (S (Z))) (Z) (((cns Nat) (Z)) (Z) (((nil Nat) (Z))))))
: (Vect Nat (S (S (S (Z)))))
-> (((cns Nat) (S (S (Z)))) (Z) (((cns Nat) (S (Z))) (Z) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))))
;; append 2 + 1
(check-type
(((vappend Nat) (S (S (Z))) (S (Z)))
(((cns Nat) (S (Z))) (Z) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))
(((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))
: (Vect Nat (S (S (S (Z)))))
-> (((cns Nat) (S (S (Z)))) (Z) (((cns Nat) (S (Z))) (Z) (((cns Nat) (Z)) (Z) (((nil Nat) (Z)))))))

View File

@ -0,0 +1,153 @@
#lang s-exp "../dep-ind-fixed.rkt"
(require "rackunit-typechecking.rkt")
; Π → λ ∀ ≻ ⊢ ≫ ⇒
; should be identical to dep-ind-tests.rkt
; since dep-ind-fixed does not change
; first clause of define-datatype
;; examples from Prabhakar's Proust paper
;; this file is like dep-peano-tests.rkt except it uses
;; define-datatype from #lang dep-ind.rkt to define Nat,
;; instead of using the builtin Nat from #lang dep.rkt
;; the examples in this file are mostly identical to dep-peano-tests.rkt,
;; except Z is replaced with (Z)
;; check (Type n) : (Type n+1)
(check-type Type : (Type 1))
(check-type (Type 0) : (Type 1))
(check-not-type (Type 0) : (Type 0))
(check-type (Type 1) : (Type 2))
(check-type (Type 3) : (Type 4))
(typecheck-fail ((λ ([x : Type]) x) Type)
#:with-msg "expected Type, given \\(Type 1\\)")
(check-type ((λ ([x : (Type 1)]) x) Type) : (Type 1))
(check-type ((λ ([x : (Type 2)]) x) (Type 1)) : (Type 2))
;; Peano nums -----------------------------------------------------------------
(define-datatype Nat : *
[Z : ( Nat)]
[S : ( Nat Nat)])
; TODO: special case 0-arity constructor using id macro
(check-type Z : ( Nat))
(check-type S : ( Nat Nat))
;(check-type Z : Nat -> Z)
(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])
(elim-Nat n
(λ ([x : Nat]) C)
(λ () zc)
(λ ([x : Nat]) sc))))))
(check-type nat-rec : ( (C) ( ( C) ( C C) ( Nat C))))
;; nat-rec with no annotations
(check-type
(λ (C)
(λ (zc sc)
(λ (n)
(elim-Nat n
(λ (x) C)
(λ () zc)
(λ (x) sc)))))
: ( (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])
(elim-Nat n
(λ ([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 : 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)
(elim-Nat n
(λ (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 : 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)))))